ci: pin every GitHub Action to a commit SHA - #295
Merged
mergify[bot] merged 1 commit intoAug 5, 2026
Merged
Conversation
CI has failed on every pull request since 2026-07-01. The last green run was 2026-06-30 (run 28426521860). Nothing in this repo changed: the only commits touching `.github/workflows/` since then are Dependabot tag bumps, and the `uses:` refs have looked like this for the repo's whole life. What changed is outside the repo. `tools/semgrep` runs `semgrep --config=auto`, which resolves the Community rule registry **at run time**, and that registry now carries `yaml.github-actions.security.github-actions-mutable-action-tag`. It reports 7 blocking findings — every `uses:` names a mutable tag instead of a 40-character commit SHA: | file | lines | |---|---| | `.github/workflows/ci.yaml` | 19, 20, 27, 43 | | `.github/workflows/release.yml` | 16, 17, 25 | That single red job is why ruff 0.15.19 → 0.16.0, actions/checkout, actions/setup-python and even Mergify's own configuration-deprecation PRs are all stuck. The loop is self-sustaining: Dependabot writes *mutable tags* when it bumps an action, so its own GHA bumps trip the rule they just re-introduced. #291 is literally "bump actions/checkout from 7.0.0 to 7.0.1" failing on the tag it wrote. ## The one that is not a lint nit `release.yml:25` was `pypa/gh-action-pypi-publish@release/v1` — a mutable **branch**, not even a tag, on the job that holds this project's PyPI publish rights (`environment: release`, `id-token: write`). A push to that branch upstream — by its owner or by anyone who compromises the repository — runs with permission to publish `sql-compare` to PyPI on the next release, and there is no version number to notice it by. This is the real supply-chain exposure here; it is pinned deliberately, not incidentally. ## What this does and does not cost Every ref is pinned to the SHA its current tag resolves to, so behaviour is unchanged today — including `gh-action-pypi-publish`, where `dc37677` (v1.14.2) is exactly where `release/v1` points right now. The trailing `# vX.Y.Z` comment keeps the file readable and is the field Dependabot rewrites. No new maintenance: Dependabot updates SHA-pinned actions natively and `.github/dependabot.yml` already enables the `github-actions` ecosystem. Verified locally against the exact CI image — `semgrep/semgrep:1.138.0` running `semgrep --config=auto --error --timeout=15 .` over the same 18 targets with the same 329 rules: **7 blocking findings before, 0 after.** ## What this does NOT fix — a call for @jd Pinning the semgrep *image* pins the **engine**, not the **rules**. `--config=auto` still resolves the registry live, which is precisely how this repo went red with zero commits, and it will happen again the next time the Community ruleset gains a rule this repo trips. Green today does not mean green tomorrow. Three options, none of them free: 1. **Pin a ruleset** — `--config=p/<ruleset>` or a versioned registry pin. Keeps CI reproducible; new upstream rules only arrive when someone bumps them, which is also the downside. 2. **Vendor the rules** — copy the rule YAML into the repo. Fully reproducible and diffable; someone has to own refreshing it. 3. **Accept the drift** — status quo. Zero effort, and the fleet occasionally goes red with no local cause. Note the monorepo has explicitly made this choice and recorded it in its root `CLAUDE.md`. Changing the security posture is a maintainer call, so this PR does not make it. It only unblocks CI. Change-Id: Ica52caa915bdbf1cde52e1849ca89f8fad0537db
Contributor
Merge Protections🟢 All 6 merge protections satisfied — ready to merge. Show 6 satisfied protections🟢 🤖 Continuous Integration
🟢 👀 Review Requirements
🟢 Enforce conventional commitMake sure that we follow https://www.conventionalcommits.org/en/v1.0.0/
🟢 🔎 Reviews
🟢 📕 PR description
🟢 🚦 Auto-queueWhen all merge protections are satisfied, this pull request will be queued automatically. |
There was a problem hiding this comment.
Pull request overview
Pins all GitHub Actions uses: references in this repository’s CI and release workflows to immutable commit SHAs to satisfy Semgrep’s yaml.github-actions.security.github-actions-mutable-action-tag rule and unblock CI.
Changes:
- Replace
actions/checkout@v7.0.0withactions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0(with# v7.0.0comment). - Replace
actions/setup-python@v6.3.0withactions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1(with# v6.3.0comment). - Replace
pypa/gh-action-pypi-publish@release/v1withpypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33(with# v1.14.2comment).
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| .github/workflows/ci.yaml | Pins checkout/setup-python action refs to commit SHAs in test + semgrep jobs. |
| .github/workflows/release.yml | Pins checkout/setup-python and PyPI publish action refs to commit SHAs for release publishing. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
jd
marked this pull request as ready for review
August 5, 2026 13:43
kozlek
approved these changes
Aug 5, 2026
Contributor
Merge Queue Status
This pull request spent 34 seconds in the queue, including 9 seconds running CI. Required conditions to merge
|
mergify
Bot
deleted the
devs/jd/jd/sql-compare-action-pinning/pin-github-action-commit-sha--ca52caa9
branch
August 5, 2026 13:50
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.
CI has failed on every pull request since 2026-07-01. The last green run
was 2026-06-30 (run 28426521860). Nothing in this repo changed: the only
commits touching
.github/workflows/since then are Dependabot tag bumps,and the
uses:refs have looked like this for the repo's whole life.What changed is outside the repo.
tools/semgreprunssemgrep --config=auto, which resolves the Community rule registry atrun time, and that registry now carries
yaml.github-actions.security.github-actions-mutable-action-tag. Itreports 7 blocking findings — every
uses:names a mutable tag instead ofa 40-character commit SHA:
.github/workflows/ci.yaml.github/workflows/release.ymlThat single red job is why ruff 0.15.19 → 0.16.0, actions/checkout,
actions/setup-python and even Mergify's own configuration-deprecation PRs
are all stuck. The loop is self-sustaining: Dependabot writes mutable
tags when it bumps an action, so its own GHA bumps trip the rule they
just re-introduced. #291 is literally "bump actions/checkout from 7.0.0 to
7.0.1" failing on the tag it wrote.
The one that is not a lint nit
release.yml:25waspypa/gh-action-pypi-publish@release/v1— a mutablebranch, not even a tag, on the job that holds this project's PyPI
publish rights (
environment: release,id-token: write). A push to thatbranch upstream — by its owner or by anyone who compromises the
repository — runs with permission to publish
sql-compareto PyPI on thenext release, and there is no version number to notice it by. This is the
real supply-chain exposure here; it is pinned deliberately, not
incidentally.
What this does and does not cost
Every ref is pinned to the SHA its current tag resolves to, so behaviour
is unchanged today — including
gh-action-pypi-publish, wheredc37677(v1.14.2) is exactly whererelease/v1points right now. Thetrailing
# vX.Y.Zcomment keeps the file readable and is the fieldDependabot rewrites. No new maintenance: Dependabot updates SHA-pinned
actions natively and
.github/dependabot.ymlalready enables thegithub-actionsecosystem.Verified locally against the exact CI image —
semgrep/semgrep:1.138.0running
semgrep --config=auto --error --timeout=15 .over the same 18targets with the same 329 rules: 7 blocking findings before, 0 after.
What this does NOT fix — a call for @jd
Pinning the semgrep image pins the engine, not the rules.
--config=autostill resolves the registry live, which is precisely howthis repo went red with zero commits, and it will happen again the next
time the Community ruleset gains a rule this repo trips. Green today does
not mean green tomorrow. Three options, none of them free:
--config=p/<ruleset>or a versioned registrypin. Keeps CI reproducible; new upstream rules only arrive when someone
bumps them, which is also the downside.
reproducible and diffable; someone has to own refreshing it.
occasionally goes red with no local cause. Note the monorepo has
explicitly made this choice and recorded it in its root
CLAUDE.md.Changing the security posture is a maintainer call, so this PR does not
make it. It only unblocks CI.