ci: detect a GitHub Action that is not pinned to a commit - #162
Merged
Conversation
A `uses:` reference that names a tag resolves to whatever the upstream owner points it at today. The owner can move the tag. Nothing in our repositories records that it moved, so a release can change behaviour with no commit of ours to review, to blame, or to revert. That is not hypothetical here. pypa/gh-action-pypi-publish v1.14.0 bundles twine 6.1.0 and packaging 25.0, which reject Metadata-Version 2.5 -- the version current hatchling emits. It broke the openadapt-evals 0.91.0 release AFTER the tag, the version commit and the GitHub release had already landed, so PyPI stayed stale while every other artifact said the version shipped. Twelve repositories were fixed by hand in 2026-08. Nothing detects the next one: dependency-review reads dependency manifests rather than workflow `uses:` lines, and Dependabot is happy to leave a floating tag floating. Measured across the 38 non-archived, non-fork repositories we own: 451 references are SHA-pinned and 112 are not, across 19 repositories. Two track a branch (superfly/flyctl-actions@master), which follows another project's HEAD continuously. Alerting on 112 items every week is the muted-alert failure this repo already warns about in sweep_default_branch_ci.py, so the accepted backlog lives in action-pin-baseline.json (93 decisions covering those 112 references) and only a NEW or WORSE reference raises the alarm. The backlog is counted but never alerted on, so the number falls because someone chose to work on it. Validation never regenerates the baseline; --write-baseline does, deliberately, for review -- the same shape as public-artifacts.json in openadapt-flow. Weekly rather than daily: pin drift is slow and operator-driven, and a daily run would repeat the same answer six extra times a week. Verified against the live organisation: quiet with the baseline (alert=false over 38 repositories), and firing with one row removed, naming openadapt-wright deploy.yml:92 and verify-fly-credential.yml:38. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
A
uses:reference that names a tag resolves to whatever the upstream owner points it at today. The owner can move the tag. Nothing in our repositories records that it moved, so a release can change behaviour with no commit of ours to review, blame, or revert.Not hypothetical.
pypa/gh-action-pypi-publishv1.14.0 bundles twine 6.1.0 and packaging 25.0, which rejectMetadata-Version: 2.5— the version current hatchling emits. It broke the openadapt-evals 0.91.0 release after the tag, the version commit and the GitHub release had landed, so PyPI stayed stale while every other artifact said the version shipped. Twelve repositories were corrected by hand in 2026-08.Nothing detects the next one.
dependency-review.ymlreads dependency manifests, not workflowuses:lines.codeql.ymlanalyses code. Dependabot proposes version bumps but is equally happy to leave a floating tag floating.What the sweep found
Measured across the 38 non-archived, non-fork repositories we own:
uses:referencesTwo track a branch —
superfly/flyctl-actions/setup-flyctl@master— which follows another project's HEAD continuously.Why a baseline instead of a weekly count
Alerting on 112 items every week is exactly the failure
sweep_default_branch_ci.pyalready warns about: "a daily issue that cries wolf gets muted, and a muted alert is worse than none."So the accepted backlog lives in
action-pin-baseline.json(93 decisions covering those 112 references) and the sweep alerts only on a reference that is new, or worse than what was accepted. The backlog is counted in the report and never raises the alarm, so the number falls because someone chose to work on it — not because a bot nagged.Validation never regenerates the baseline.
--write-baselinedoes, deliberately, for review — the same shape aspublic-artifacts.jsonin openadapt-flow.Why here, and why weekly
repos.ymlis the docs-site registry and omits internal tooling, so a sweep built on it would have missedopenadapt-consiliumandopenadapt-viewer— both of which needed this exact fix in 2026-08. This uses the live organisation listing, the same choice and the same reason assweep_default_branch_ci.py. Archived repos and forks are excluded.Weekly, not daily: pin drift is slow and operator-driven, and a daily run would repeat the same answer six extra times a week.
08:29 Mondayis clear of every other cron here.Verification
Run against the live organisation, not just fixtures:
38 repositories, 112 unpinned, 0 needing attention,alert=false clear=true.alert=true, namingopenadapt-wright deploy.yml:92andverify-fly-credential.yml:38as branch-tracking.One test caught a real bug during development: the same
(repo, workflow, ref)appears in several jobs of one file, so the first baseline held 112 rows for 93 decisions whileload_baselinesilently collapsed them — the committed file disagreed with the file the validator used. Fixed inworst_tier_per_identity.Cost
One
ubuntu-latestrunner, standard library only, no dependency install for the sweep itself. Roughly 40 repository reads plus one file read per workflow file, against a 1000/hour budget. This repository is public, so the minutes are free.🤖 Generated with Claude Code