#215 testing-blocks: lead description with Use-this-when trigger #418
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Tessl Skill Lint | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| jobs: | |
| tessl-lint: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 0 | |
| - uses: tesslio/setup-tessl@v2 | |
| - name: Detect changed tiles | |
| id: detect | |
| run: | | |
| if [ "${{ github.event_name }}" = "pull_request" ]; then | |
| CHANGED_FILES=$(git diff --name-only origin/${{ github.base_ref }}...HEAD) | |
| # Build list of all tile directories (parents of tile.json) | |
| ALL_TILE_DIRS=$(find plugins -name tile.json -exec dirname {} \; 2>/dev/null | sort) | |
| if [ -z "$ALL_TILE_DIRS" ]; then | |
| echo "skip=true" >> "$GITHUB_OUTPUT" | |
| echo "No tile.json files found in repo." | |
| exit 0 | |
| fi | |
| # For each changed file, find which tile directory it belongs to | |
| DIRS="" | |
| for changed_file in $CHANGED_FILES; do | |
| for tile_dir in $ALL_TILE_DIRS; do | |
| case "$changed_file" in | |
| "$tile_dir"/*) | |
| case " $DIRS " in | |
| *" $tile_dir "*) ;; | |
| *) DIRS="$DIRS $tile_dir" ;; | |
| esac | |
| break | |
| ;; | |
| esac | |
| done | |
| done | |
| DIRS=$(echo "$DIRS" | xargs) # trim whitespace | |
| # Check for unmatched changes under plugins/ | |
| UNMATCHED=false | |
| for changed_file in $CHANGED_FILES; do | |
| case "$changed_file" in | |
| plugins/*) | |
| MATCHED=false | |
| for tile_dir in $ALL_TILE_DIRS; do | |
| case "$changed_file" in | |
| "$tile_dir"/*) MATCHED=true; break ;; | |
| esac | |
| done | |
| if [ "$MATCHED" = "false" ]; then | |
| UNMATCHED=true | |
| break | |
| fi | |
| ;; | |
| esac | |
| done | |
| if [ "$UNMATCHED" = "true" ]; then | |
| echo "Unmatched changes under plugins/ detected — linting all tiles" | |
| DIRS=$(echo "$ALL_TILE_DIRS" | tr "\n" " " | xargs) | |
| fi | |
| if [ -z "$DIRS" ]; then | |
| echo "skip=true" >> "$GITHUB_OUTPUT" | |
| echo "Changed files don't belong to any tile — nothing to lint." | |
| else | |
| echo "skip=false" >> "$GITHUB_OUTPUT" | |
| echo "dirs=$DIRS" >> "$GITHUB_OUTPUT" | |
| echo "Tiles to lint: $DIRS" | |
| fi | |
| else | |
| # Push to main: lint all tiles | |
| ALL_TILE_DIRS=$(find plugins -name tile.json -exec dirname {} \; 2>/dev/null | tr "\n" " " | xargs) | |
| if [ -z "$ALL_TILE_DIRS" ]; then | |
| echo "skip=true" >> "$GITHUB_OUTPUT" | |
| echo "No tile.json files found in repo." | |
| else | |
| echo "skip=false" >> "$GITHUB_OUTPUT" | |
| echo "dirs=$ALL_TILE_DIRS" >> "$GITHUB_OUTPUT" | |
| echo "Push to main — linting all tiles: $ALL_TILE_DIRS" | |
| fi | |
| fi | |
| - name: Post skip summary | |
| if: steps.detect.outputs.skip == 'true' | |
| run: | | |
| { | |
| echo "## Skill Lint" | |
| echo "" | |
| echo "No tiles to lint." | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| - name: Lint tiles | |
| if: steps.detect.outputs.skip != 'true' | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| PASS=0 | |
| FAIL=0 | |
| TOTAL_WARNINGS=0 | |
| SUMMARY_FILE=$(mktemp) | |
| ERRORS_FILE=$(mktemp) | |
| COMMENT_FILE=$(mktemp) | |
| PAYLOAD_FILE=$(mktemp) | |
| trap 'rm -f "$SUMMARY_FILE" "$ERRORS_FILE" "$COMMENT_FILE" "$PAYLOAD_FILE"' EXIT | |
| # Marker used to find/update the sticky PR comment on subsequent runs | |
| MARKER="<!-- tessl-lint-summary -->" | |
| { | |
| echo "$MARKER" | |
| echo "## Tessl Skill Lint" | |
| echo "" | |
| } > "$COMMENT_FILE" | |
| for tile_dir in ${{ steps.detect.outputs.dirs }}; do | |
| TILE_NAME=$(basename "$tile_dir") | |
| echo "::group::Linting $TILE_NAME ($tile_dir)" | |
| EXIT_CODE=0 | |
| OUTPUT=$(tessl skill lint "$tile_dir" 2>&1) || EXIT_CODE=$? | |
| echo "$OUTPUT" | |
| echo "::endgroup::" | |
| WARN_COUNT=$(echo "$OUTPUT" | grep -c '^⚠' || true) | |
| TOTAL_WARNINGS=$((TOTAL_WARNINGS + WARN_COUNT)) | |
| # ::warning:: annotation per tile with warnings (visible in run details) | |
| if [ "$WARN_COUNT" -gt 0 ]; then | |
| echo "::warning::$TILE_NAME has $WARN_COUNT lint warning(s) — see job log or PR comment" | |
| fi | |
| if [ "$EXIT_CODE" -ne 0 ]; then | |
| FAIL=$((FAIL + 1)) | |
| echo "| $TILE_NAME | $WARN_COUNT warnings | ❌ error |" >> "$SUMMARY_FILE" | |
| echo " ❌ $TILE_NAME: lint failed (exit code $EXIT_CODE)" >> "$ERRORS_FILE" | |
| { | |
| echo "### ❌ \`$TILE_NAME\` — lint failed" | |
| echo "" | |
| echo '```' | |
| echo "$OUTPUT" | |
| echo '```' | |
| echo "" | |
| } >> "$COMMENT_FILE" | |
| else | |
| PASS=$((PASS + 1)) | |
| if [ "$WARN_COUNT" -gt 0 ]; then | |
| echo "| $TILE_NAME | $WARN_COUNT warnings | ✅ |" >> "$SUMMARY_FILE" | |
| { | |
| echo "<details>" | |
| echo "<summary>⚠️ <strong><code>$TILE_NAME</code></strong> — $WARN_COUNT warning(s)</summary>" | |
| echo "" | |
| echo '```' | |
| echo "$OUTPUT" | |
| echo '```' | |
| echo "</details>" | |
| echo "" | |
| } >> "$COMMENT_FILE" | |
| else | |
| echo "| $TILE_NAME | clean | ✅ |" >> "$SUMMARY_FILE" | |
| echo "✅ \`$TILE_NAME\` — clean" >> "$COMMENT_FILE" | |
| echo "" >> "$COMMENT_FILE" | |
| fi | |
| fi | |
| done | |
| TOTAL=$((PASS + FAIL)) | |
| # GitHub Actions step summary | |
| { | |
| echo "## Skill Lint" | |
| echo "" | |
| echo "| Tile | Status | Result |" | |
| echo "|------|--------|--------|" | |
| cat "$SUMMARY_FILE" | |
| echo "| **Total** | **$PASS/$TOTAL passed** | $([ "$FAIL" -eq 0 ] && echo '✅' || echo '❌') |" | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| # Footer in sticky comment | |
| { | |
| echo "" | |
| echo "---" | |
| if [ "$FAIL" -gt 0 ]; then | |
| echo "**$FAIL tile(s) failed lint**, $TOTAL_WARNINGS warning(s) across $TOTAL tile(s) checked." | |
| elif [ "$TOTAL_WARNINGS" -gt 0 ]; then | |
| echo "✅ All $TOTAL tile(s) lint passed with $TOTAL_WARNINGS warning(s) total." | |
| else | |
| echo "✅ All $TOTAL tile(s) clean." | |
| fi | |
| echo "" | |
| echo "_Updated by [\`tessl-lint\`](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}) for commit ${GITHUB_SHA:0:7}._" | |
| } >> "$COMMENT_FILE" | |
| # stdout summary | |
| echo "" | |
| echo "=============================" | |
| echo " Skill Lint Summary" | |
| echo "=============================" | |
| echo " Total: $TOTAL" | |
| echo " Passed: $PASS" | |
| echo " Failed: $FAIL" | |
| echo " Warnings: $TOTAL_WARNINGS" | |
| if [ -s "$ERRORS_FILE" ]; then | |
| echo "" | |
| echo " Failed tiles:" | |
| cat "$ERRORS_FILE" | |
| fi | |
| echo "=============================" | |
| # Sticky PR comment (pull_request events only) | |
| if [ "${{ github.event_name }}" = "pull_request" ]; then | |
| PR_NUMBER=${{ github.event.pull_request.number }} | |
| REPO="${{ github.repository }}" | |
| # JSON-encode the comment body via jq so quotes/newlines survive | |
| jq -n --rawfile body "$COMMENT_FILE" '{body: $body}' > "$PAYLOAD_FILE" | |
| EXISTING_ID=$(gh api "repos/$REPO/issues/$PR_NUMBER/comments" --paginate \ | |
| --jq ".[] | select(.body | contains(\"$MARKER\")) | .id" | head -1 || true) | |
| if [ -n "$EXISTING_ID" ]; then | |
| echo "Updating existing sticky comment $EXISTING_ID" | |
| gh api -X PATCH "repos/$REPO/issues/comments/$EXISTING_ID" --input "$PAYLOAD_FILE" >/dev/null || echo "::warning::Could not update PR comment (token may be read-only for fork PRs)" | |
| else | |
| echo "Creating new sticky comment" | |
| gh api -X POST "repos/$REPO/issues/$PR_NUMBER/comments" --input "$PAYLOAD_FILE" >/dev/null || echo "::warning::Could not post PR comment (token may be read-only for fork PRs)" | |
| fi | |
| fi | |
| if [ "$FAIL" -gt 0 ]; then | |
| echo "::error::$FAIL tile(s) failed lint" | |
| exit 1 | |
| fi |