diff --git a/.github/workflows/stale-lock-files.yml b/.github/workflows/stale-lock-files.yml new file mode 100644 index 00000000000..8d89b0e9e0e --- /dev/null +++ b/.github/workflows/stale-lock-files.yml @@ -0,0 +1,50 @@ +name: Stale Lock Files + +on: + pull_request: + types: [opened, synchronize, reopened, ready_for_review] + paths: + - '.github/workflows/*.md' + - 'scripts/check-stale-lock-files.sh' + - '.github/workflows/stale-lock-files.yml' + +permissions: + contents: read + +jobs: + check-stale-lock-files: + name: Check for stale .lock.yml files + runs-on: ubuntu-latest + timeout-minutes: 5 + concurrency: + group: ci-${{ github.ref }}-stale-lock-files + cancel-in-progress: true + steps: + - name: Checkout code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + + - name: Fetch base branch + if: github.event_name == 'pull_request' + run: git fetch origin ${{ github.base_ref }} --no-tags + + - name: Check stale lock files + run: | + bash scripts/check-stale-lock-files.sh --base-ref "origin/${{ github.base_ref }}" + + - name: Add step summary on failure + if: failure() + run: | + echo "## ❌ Stale Lock Files Detected" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "One or more \`.github/workflows/*.md\` files were modified but their" >> $GITHUB_STEP_SUMMARY + echo "compiled \`.lock.yml\` outputs were not regenerated." >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**To fix**, recompile the workflows and commit the updated lock files:" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`bash" >> $GITHUB_STEP_SUMMARY + echo "make recompile" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "Then commit the regenerated \`.lock.yml\` files together with the \`.md\` changes." >> $GITHUB_STEP_SUMMARY diff --git a/Makefile b/Makefile index f416afcca04..6aa19a1988a 100644 --- a/Makefile +++ b/Makefile @@ -473,9 +473,17 @@ bundle-js: @echo "✓ bundle-js tool built" @echo "To bundle a JavaScript file: ./bundle-js [output-file]" -# Test all code (Go, JavaScript, and wasm golden) +# Run Bash script tests (check-stale-lock-files, check-workflow-drift) +.PHONY: test-scripts +test-scripts: build + @echo "Running Bash script tests..." + bash scripts/check-stale-lock-files_test.sh + bash scripts/check-workflow-drift_test.sh ./$(BINARY_NAME) + @echo "✓ All Bash script tests passed" + +# Test all code (Go, JavaScript, wasm golden, and shell scripts) .PHONY: test-all -test-all: test test-js test-wasm-golden +test-all: test test-js test-wasm-golden test-scripts # Run tests with coverage .PHONY: test-coverage @@ -1238,7 +1246,8 @@ help: @echo " test-impacted-js - Run impacted JavaScript unit tests for current branch changes" @echo " test-impacted-go - Run impacted Go unit tests for current branch changes" @echo " test-impacted - Run impacted JavaScript and Go unit tests for current branch changes" - @echo " test-all - Run all tests (Go, JavaScript, and wasm golden)" + @echo " test-scripts - Run Bash script tests (check-stale-lock-files, check-workflow-drift)" + @echo " test-all - Run all tests (Go, JavaScript, wasm golden, and shell scripts)" @echo " test-wasm-golden - Run wasm golden tests (Go string API path)" @echo " test-wasm - Build wasm and run Node.js golden comparison test" @echo " update-wasm-golden - Regenerate wasm golden files from current compiler output" diff --git a/scripts/check-stale-lock-files_test.sh b/scripts/check-stale-lock-files_test.sh index f6afe960dc2..0d7a95dec7c 100755 --- a/scripts/check-stale-lock-files_test.sh +++ b/scripts/check-stale-lock-files_test.sh @@ -224,6 +224,67 @@ else fail "--base-ref should pass when markdown + lock changed" "$(cat "$T10_OUT")" fi +# --------------------------------------------------------------------------- +# Test 11: .md files in excluded shared/ subdirectory are not flagged. +# --------------------------------------------------------------------------- +echo "Test 11: .md files in shared/ subdirectory are excluded..." +T11="$TMP_ROOT/t11" +mkdir -p "$T11" +git -C "$T11" init -q +git -C "$T11" config user.email "test@test.com" +git -C "$T11" config user.name "Test" +mkdir -p "$T11/.github/workflows/shared" +printf '%s\n' "# shared-tool" > "$T11/.github/workflows/shared/tools.md" +git -C "$T11" add . +git -C "$T11" commit -q -m "initial" +printf '%s\n' "# shared-tool (edited)" > "$T11/.github/workflows/shared/tools.md" +T11_OUT="$TMP_ROOT/t11-output.txt" +if (cd "$T11" && bash "$STALE_SCRIPT" >"$T11_OUT" 2>&1); then + pass "shared/ .md files are excluded (exits 0)" +else + fail "shared/ .md files should be excluded (should exit 0)" "$(cat "$T11_OUT")" +fi + +# --------------------------------------------------------------------------- +# Test 12: .md files in excluded skills/ subdirectory are not flagged. +# --------------------------------------------------------------------------- +echo "Test 12: .md files in skills/ subdirectory are excluded..." +T12="$TMP_ROOT/t12" +mkdir -p "$T12" +git -C "$T12" init -q +git -C "$T12" config user.email "test@test.com" +git -C "$T12" config user.name "Test" +mkdir -p "$T12/.github/workflows/skills" +printf '%s\n' "# skill-doc" > "$T12/.github/workflows/skills/example.md" +git -C "$T12" add . +git -C "$T12" commit -q -m "initial" +printf '%s\n' "# skill-doc (edited)" > "$T12/.github/workflows/skills/example.md" +T12_OUT="$TMP_ROOT/t12-output.txt" +if (cd "$T12" && bash "$STALE_SCRIPT" >"$T12_OUT" 2>&1); then + pass "skills/ .md files are excluded (exits 0)" +else + fail "skills/ .md files should be excluded (should exit 0)" "$(cat "$T12_OUT")" +fi + +# --------------------------------------------------------------------------- +# Test 13: unknown --base-ref falls back to working-tree diff and still +# catches a stale .md in the working tree. +# --------------------------------------------------------------------------- +echo "Test 13: unknown --base-ref falls back gracefully and still catches stale .md..." +T13="$TMP_ROOT/t13" +mkdir -p "$T13" +create_fixture_repo "$T13" "fallback-workflow" +# Modify the .md in the working tree (not committed) +printf '%s\n' "# fallback-workflow (edited)" > "$T13/.github/workflows/fallback-workflow.md" +T13_OUT="$TMP_ROOT/t13-output.txt" +if (cd "$T13" && bash "$STALE_SCRIPT" --base-ref "nonexistent-sha-12345" >"$T13_OUT" 2>&1); then + fail "unknown base-ref with stale .md should exit 1" "$(cat "$T13_OUT")" +elif grep -q "fallback-workflow.md" "$T13_OUT"; then + pass "unknown --base-ref falls back gracefully and catches stale .md" +else + fail "unknown --base-ref fallback output did not name the stale file" "$(cat "$T13_OUT")" +fi + echo echo "Tests passed: $TESTS_PASSED" echo "Tests failed: $TESTS_FAILED"