Pin freshness audit #5
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: Pin freshness audit | |
| # Validates that every action pin (workflow + composite) still resolves | |
| # upstream — closes the silently-deprecated-tag class. Complements the | |
| # shape-only `Action pinning audit` job in ci.yml (which checks pin | |
| # *shape* on every PR; this checks pin *freshness* on a schedule). | |
| # | |
| # Schedule: weekly + workflow_dispatch. Not on every PR — rate-limit | |
| # aware (5000 req/hr per token, this audit costs ~70 req/run) and not | |
| # blocking. Findings are surfaced as `::warning::` annotations and (when | |
| # any are found) auto-file an issue tagged `harness,security`. | |
| # | |
| # Script + 15 unit tests live in | |
| # `.github/scripts/check_pin_freshness.py` + `tests/test_check_pin_freshness.py`. | |
| on: | |
| schedule: | |
| # Monday 06:00 UTC — alongside artifact-cleanup. Avoids weekend | |
| # noise; weekly cadence is enough for a non-blocking gate. | |
| - cron: "0 6 * * 1" | |
| workflow_dispatch: | |
| inputs: | |
| strict: | |
| description: "Run in strict mode (findings → errors, exit 1)" | |
| type: boolean | |
| default: false | |
| permissions: | |
| contents: read | |
| issues: write | |
| jobs: | |
| audit: | |
| name: Pin freshness audit | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 | |
| with: | |
| python-version: "3.14" | |
| - name: Run pin-freshness audit | |
| id: audit | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| PIN_FRESHNESS_STRICT: ${{ inputs.strict && '1' || '' }} | |
| run: python .github/scripts/check_pin_freshness.py | |
| - name: File issue on findings | |
| # Only fire when default-mode (warn) found something — strict mode | |
| # already failed the workflow and operator attention is automatic. | |
| if: > | |
| always() && | |
| steps.audit.outputs.findings_count != '0' && | |
| steps.audit.outputs.findings_count != '' | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| FINDINGS: ${{ steps.audit.outputs.findings_count }} | |
| RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} | |
| run: | | |
| set -euo pipefail | |
| gh issue create \ | |
| --title "chore: pin-freshness audit found ${FINDINGS} stale pin(s)" \ | |
| --label "harness,security" \ | |
| --body "$(cat <<EOF | |
| The weekly pin-freshness audit flagged ${FINDINGS} stale pin(s). | |
| See the run log for the per-pin annotations: ${RUN_URL} | |
| Each finding is one of: | |
| - **Tag pin no longer resolves** — upstream tag was deleted or renamed; bump to a current tag or SHA pin per \`docs/DEVELOPMENT.md#action-pinning-policy\`. | |
| - **SHA pin: documented tag re-tagged** — the trailing \`# vN.M.P\` comment names a tag that now points at a different SHA upstream. Either bump the pin to the new SHA (preferred) or update the comment to a tag that still matches. | |
| - **API failure** — couldn't reach the upstream registry; transient, will likely clear on the next weekly run. | |
| Closes when the offending pin is updated and the next audit run is clean. | |
| EOF | |
| )" |