chore(workflows): SHA-pin third-party GitHub Actions#94
Conversation
Closes #46. Every external action in .github/workflows/*.yml was previously pinned to a major-version tag — and pypa/gh-action-pypi-publish was even pinned to a *branch* ref (release/v1), which can move under us. A minor release within a major can introduce security or behavior regressions without detection on a tag-only pin. Now every third-party `uses:` line carries an immutable 40-char commit SHA with a trailing `# v<version>` comment for human readability (the GitHub-recommended supply-chain pattern). Eight actions pinned across ci.yml, publish.yml, test-publish .yml, vdsm.yml: actions/checkout v6.0.2 (de0fac2e...) astral-sh/setup-uv v7.6.0 (37802adc...) actions/setup-python v6.2.0 (a309ff8b...) actions/upload-artifact v7.0.1 (043fb46d...) actions/download-artifact v8.0.1 (3e5f45b2...) actions/cache v5.0.5 (27d5ce7f...) codecov/codecov-action v6.0.0 (57e3a136...) pypa/gh-action-pypi-publish v1.14.0 (cef22109...) Local composite actions under ./.github/actions/* are deliberately not pinned (in-repo, reviewed on merge — pinning them would produce stale-SHA noise on every internal change). dependabot-changelog.yml was already SHA-pinned (PR #60 work) and serves as the template the rest of the workflows now match. Dependabot's existing github-actions ecosystem in .github/dependabot.yml will propose SHA bump PRs weekly so the pins stay fresh. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
The prior CI run failed only on TestSearch::test_search_keyword_finds_directory — DSM's search service didn't propagate the freshly-created `Bambu Studio` directory to the index within the 6-attempt retry budget (synoindex registration was confirmed in the run log; just a slow indexer). Documented flake in CLAUDE.md "Search tests can be flaky". Retriggering CI; SHA pins have no functional impact on the search code path. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two consecutive vdsm runs on this branch failed only on TestSearch::test_search_keyword_finds_directory; the preceding main run passed the same test on attempt 5 of 6 (search_files saw the directory after ~75s of indexer warmup), and our runs got 0 results across all six. Pure DSM Universal Search propagation latency variance — documented in CLAUDE.md as a known vdsm flake. SHA pins resolve to the same underlying code that `@v5`/`@v6` resolved to today, so they can't be the cause. vdsm has `continue-on-error: true` and is not a required check, but retrying once more in case the indexer is quicker on this run. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
cmeans
left a comment
There was a problem hiding this comment.
QA Round 1 — PASS (no findings)
Reviewed on head 096b7bc (4 commits in branch: SHA-pin work + CHANGELOG backfill + 2 empty-commit retriggers for the documented TestSearch vdsm flake).
SHA verification
Independently resolved each tag against the GitHub API via gh api repos/{owner}/{repo}/commits/{tag}. All 8 SHAs match the PR body's table exactly:
| Action | Tag | SHA (resolved) |
|---|---|---|
actions/checkout |
v6.0.2 | de0fac2e4500dabe0009e67214ff5f5447ce83dd ✓ |
astral-sh/setup-uv |
v7.6.0 | 37802adc94f370d6bfd71619e3f0bf239e1f3b78 ✓ |
actions/setup-python |
v6.2.0 | a309ff8b426b58ec0e2a45f0f869d46889d02405 ✓ |
actions/upload-artifact |
v7.0.1 | 043fb46d1a93c77aae656e7c1c64a875d1fc6a0a ✓ |
actions/download-artifact |
v8.0.1 | 3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c ✓ |
actions/cache |
v5.0.5 | 27d5ce7f107fe9357f9df03efb73ab90386fccae ✓ |
codecov/codecov-action |
v6.0.0 | 57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 ✓ |
pypa/gh-action-pypi-publish |
v1.14.0 | cef221092ed1bacb1cc03d23a2d87d1d172e277b ✓ |
Coverage
The test-plan audit grep -rn "^\s*-\? *uses: " .github/workflows/ | grep -v "uses: \./\." | grep -vE "@[0-9a-f]{40}" returns empty (exit 1) — no plain-tag pins remain on third-party actions. Full enumeration of uses: lines confirms:
- All third-party
uses:carry a SHA +# v<version>comment. - Local composites (
./.github/actions/install-mcp-publisher) correctly NOT pinned (in-repo, reviewed on merge). dependabot-changelog.yml(already pinned by PR #60) verified intact:actions/create-github-app-token@1b10c78c # v3.1.1,actions/checkout@de0fac2e # v6.0.2,dependabot/fetch-metadata@25dd0e34 # v3.1.0. Sameactions/checkoutSHA as the rest of the workflows — consistent.
YAML parse
All 8 workflow files (ci.yml, dependabot-changelog.yml, pr-labels-ci.yml, pr-labels.yml, publish.yml, qa-gate.yml, test-publish.yml, vdsm.yml) yaml.safe_load-clean.
vdsm flake context
Two empty retrigger commits (d99f3c3, 096b7bc) on the branch are CI-retry only. Commit messages document the failure as TestSearch::test_search_keyword_finds_directory — DSM Universal Search propagation latency variance, already documented in CLAUDE.md as a known flake; SHA pins resolve to identical underlying code as @v6/@v7/etc, so they can't be the cause. Confirmed by reading the workflow itself: vdsm.yml:27 continue-on-error: true — vdsm is non-blocking by design; the green status here is just the eventual successful retry, not a gate condition.
Local stack
uv run pytest→ 599 passed, 112 deselected, 96.26% coverage. Identical to post-#92 baseline; PR doesn't touch any code paths.uv run ruff check src/ tests/,mypy src/clean.
Out-of-scope claims
PR body's "out of scope" list (local composites, dependabot-changelog already pinned, no-uses: workflows) verified by exhaustive grep -E "uses: " .github/workflows/*.yml enumeration — every external uses: line is now SHA-pinned, every local-composite uses: line is not, and pr-labels.yml / pr-labels-ci.yml / qa-gate.yml indeed have no uses: lines (run-step only).
Issue #46 ACs
- AC 1 (SHA-pin third-party actions): ✓ — all 8.
- AC 2 (Dependabot proposes bumps weekly): ✓ — relies on existing
.github/dependabot.ymlgithub-actionsecosystem entry, which works equivalently for SHA pins as it did for tag pins.
PR-body checkboxes
Boxes 1–3 flipped (CI green; YAML parse; audit grep). Box 4 (functional smoke via next Dependabot run) is post-merge — flips on its own when Dependabot opens its next bump PR.
Disposition
Ready for QA Signoff applied as the final act. With this in, #51's LOW/docs/chore queue is empty (#42 / #43 / #45 / #46 all cleared this cycle); only the architectural ADR (#47) and the post-baggage feature batch (#48–50) remain in the original tracker. Plus newer follow-ups #25 / #75 / #76 / #93.
|
Applying Ready for QA Signoff — clean supply-chain hardening PR. All 8 PR-listed SHAs independently re-resolved against GitHub API and match exactly. Audit grep |
Summary
Closes #46.
Every external action in
.github/workflows/*.ymlwas previously pinned to a major-version tag (actions/checkout@v6,astral-sh/setup-uv@v7, etc.) — andpypa/gh-action-pypi-publish@release/v1was even pinned to a branch ref, which can move under us. A minor release within a major can introduce security or behavior regressions without detection on a tag-only pin.This PR converts every third-party
uses:line to an immutable 40-char commit SHA with a trailing# v<version>comment (the GitHub-recommended supply-chain pattern).Pins applied
actions/checkoutde0fac2e4500dabe0009e67214ff5f5447ce83ddastral-sh/setup-uv37802adc94f370d6bfd71619e3f0bf239e1f3b78actions/setup-pythona309ff8b426b58ec0e2a45f0f869d46889d02405actions/upload-artifact043fb46d1a93c77aae656e7c1c64a875d1fc6a0aactions/download-artifact3e5f45b2cfb9172054b4087a40e8e0b5a5461e7cactions/cache27d5ce7f107fe9357f9df03efb73ab90386fccaecodecov/codecov-action57e3a136b779b570ffcdbf80b3bdc90e7fab3de2pypa/gh-action-pypi-publishcef221092ed1bacb1cc03d23a2d87d1d172e277bSHAs were resolved from
repos/{owner}/{repo}/git/refs/tags/<tag>(with the annotated-tag deref throughgit/tags/<sha>); the pypa one came fromrepos/pypa/gh-action-pypi-publish/branches/release/v1and matches the v1.14.0 tag exactly.Files changed
.github/workflows/ci.yml.github/workflows/publish.yml.github/workflows/test-publish.yml.github/workflows/vdsm.ymlCHANGELOG.md—### Changedentry under## UnreleasedOut of scope (intentionally)
./.github/actions/*are NOT pinned. They live in-repo and are reviewed on merge; pinning their SHA would produce stale-SHA noise on every internal change.dependabot-changelog.ymlwas already SHA-pinned (PR fix(workflow): bump dependabot/fetch-metadata to v3.1.0 (closes auto-CHANGELOG empty-versions bug) #60 work). It served as the template this PR uses for the rest.pr-labels.yml,pr-labels-ci.yml,qa-gate.yml— these workflows use nouses:lines (entirely run-step). Nothing to pin.Why no manual SHA tracker
Dependabot's existing
github-actionsecosystem in.github/dependabot.ymlwill propose SHA bump PRs weekly so the pins stay fresh without manual maintenance. That's the second AC item from the issue, already in place since the existing config was set up for tag-pin updates and works equally well for SHA-pin updates.Test plan
for f in .github/workflows/*.yml; do python -c "import yaml; yaml.safe_load(open('$f'))"; donegrep -rn "^\s*-\? *uses: " .github/workflows/ | grep -v "uses: \./\." | grep -vE "@[0-9a-f]{40}"returns empty (no plain-tag pins on third-party actions remain)🤖 Generated with Claude Code