Skip to content

ci: own the setup, changelog, and merge-queue steps instead of calling out for them - #1044

Merged
sirtimid merged 1 commit into
sirtimid/pin-workflow-actionsfrom
sirtimid/vendor-ci-actions
Sep 8, 2026
Merged

ci: own the setup, changelog, and merge-queue steps instead of calling out for them#1044
sirtimid merged 1 commit into
sirtimid/pin-workflow-actionsfrom
sirtimid/vendor-ci-actions

Conversation

@sirtimid

@sirtimid sirtimid commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Stacked on #1043. That PR pins our own action references; this one closes the gap it exposed.

Why #1043 wasn't enough

The policy resolves actions transitively. Pinning the composite action we call does nothing if that action reaches a mutable tag one layer down — which all three of the ones on the PR path do:

Composite we call Its own unpinned uses:
action-checkout-and-setup cache/restore@v6, checkout@v7, setup-node@v6, action-retry-command@v1, cache/save@v6
github-tools/check-changelog checkout@v6 ×2, setup-node@v6
github-tools/check-skip-merge-queue github-script@v9 ×2

All are on their latest releases, so no upgrade reaches them, and they live in an org we no longer belong to. Holding the steps ourselves is the only way to make the pin true rather than decorative.

What changed

action-checkout-and-setup → explicit actions/checkout + local setup-environment. 11 call sites. Checkout can't live inside the local action — uses: ./... resolves against the workspace, so the repo must be on disk before the runner can find the file.

The local action is deliberately narrower: it keeps Yarn's download cache but drops the node_modules cache and the lookup-only fast path. Those turn on a cache key that has to account for the native rebuilds postinstall runs (better-sqlite3, node-datachannel), and a subtly wrong key yields a job that passes against stale binaries. yarn install now runs every job — that costs time and buys the guarantee that what's on disk matches the lockfile.

is-high-risk-environment: true on the three publish jobs is preserved as cache: false.

check-changelog → a local job. The upstream action cloned github-tools and ran a script over the diff. The local one asks the same question directly: every package with a CHANGELOG.md that this PR touches must have that changelog in the diff too. Compares against the merge base, not the base tip, so commits landing on main afterwards aren't read as this branch's changes. no-changelog still opts out. Packages without a changelog are private and skipped — the same set yarn changelog:validate covers.

Tested against real history before committing:

Case Expected Got
#1021 — packages changed, changelogs updated pass pass
#1043.github only pass pass
be4f406 — code changed, changelog came later fail fail
9b4717f — changelog-only commit pass pass
19689c6 — private package only pass pass
empty diff pass pass

check-skip-merge-queue → ported as-is, both scripts unchanged, onto a pinned actions/github-script.

What this does not fix

The release path still calls MetaMask/action-npm-publish and MetaMask/action-is-release, which have the same defect. Both are gated on github.event_name == 'push' and neither is part of the all-jobs-complete gate, so they block releases, not pull requests. Publishing is where a mistake is expensive and the OIDC permissions were only just fixed in #1041, so those are better pinned upstream than rewritten here.

So: PR and merge-queue CI should go green with this; cutting a release still needs either upstream pins or a narrow policy exception for those two.

Verification

  • actionlint 1.6.25 — the version and download pin the Check workflows job uses — passes clean.
  • No unpinned external uses: remains anywhere in .github/.
  • All workflow YAML parses; prettier --check clean.
  • detect-changes treats .github/actions/* as code, so this PR runs the full lint/build/test matrix on both Node versions — the new setup-environment action gets exercised by all seven job types here rather than after merge.

🤖 Generated with Claude Code


Note

Medium Risk
CI and release-adjacent workflow behavior changes (stricter changelog diff, slower installs, merge-queue skip logic); no application runtime code, but misconfiguration could block merges or weaken publish-job cache isolation if cache: false is omitted.

Overview
Replaces three MetaMask composite actions whose transitive uses: still pointed at mutable tags, so PR-path CI can pin the full dependency chain locally.

Environment setup: Adds setup-environment (pinned setup-node, cache, Corepack, yarn install --immutable) and swaps every action-checkout-and-setup call for explicit actions/checkout plus that action. Publish jobs pass cache: false instead of is-high-risk-environment. The local action drops node_modules caching and always runs a full install so native postinstall binaries stay aligned with the lockfile; only Yarn’s download cache remains.

Changelog PR check: Inlines the former github-tools/check-changelog as a bash step: diff from merge base to HEAD, require each touched packages/* with a CHANGELOG.md to include that file in the diff, honor no-changelog, skip packages without a changelog.

Merge queue: Vendors check-skip-merge-queue under .github/actions with pinned actions/github-script; main.yml calls the local copy. Behavior unchanged—outputs whether the PR is first in queue and up to date with base for skip gating.

Reviewed by Cursor Bugbot for commit 5c2bd57. Bugbot is set up for automated code reviews on this repo. Configure here.

…g out for them

Pinning our own references was necessary but not sufficient. The policy
resolves actions transitively, and the MetaMask actions we call reach mutable
tags one layer down:

    action-checkout-and-setup  -> cache/restore@v6, checkout@v7, setup-node@v6,
                                  action-retry-command@v1, cache/save@v6
    github-tools/check-changelog        -> checkout@v6 x2, setup-node@v6
    github-tools/check-skip-merge-queue -> github-script@v9 x2

All three are on their latest releases, so no upgrade reaches them, and they
live in an organization we no longer belong to. A pin one layer deep is no pin
at all, so the only way to make the chain true is to hold it.

`action-checkout-and-setup` becomes an explicit `actions/checkout` plus a local
`setup-environment` action -- Node, Corepack, Yarn's download cache, install.
Checkout cannot live inside it: `uses: ./...` resolves against the workspace,
so the repository has to be on disk before the runner can find the file.

The local action is narrower than the one it replaces. It drops the
node_modules cache and the lookup-only fast path, keeping only Yarn's download
cache, because those turn on a cache key that has to account for the native
rebuilds `postinstall` runs. `yarn install` runs every job now, which costs
time and buys the guarantee that what is on disk matches the lockfile.

`check-changelog` cloned github-tools to run a script over the diff. The local
job asks the same question directly: every package with a CHANGELOG that this
pull request touches must have that changelog in the diff too. It compares
against the merge base rather than the base tip, so commits landing on main
afterwards are not read as this branch's. `no-changelog` still opts out.

`check-skip-merge-queue` is ported as-is, its two scripts unchanged, onto a
pinned `actions/github-script`.

The release path still calls `action-npm-publish` and `action-is-release`,
which have the same defect. Both are gated on `push`, so they block releases
rather than pull requests, and neither is part of the `all-jobs-complete` gate.
Publishing is where a mistake is expensive and OIDC permissions were only just
fixed in #1041, so they are better handled upstream than rewritten here.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@sirtimid
sirtimid merged commit 5c2bd57 into sirtimid/pin-workflow-actions Sep 8, 2026
11 checks passed
@sirtimid
sirtimid deleted the sirtimid/vendor-ci-actions branch September 8, 2026 14:15

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 5c2bd57. Configure here.

# A package with no changelog is private and releases nothing, so
# there is no entry for it to be missing. This is the same set
# `yarn changelog:validate` covers, which runs `--no-private`.
[[ -f "$changelog" ]] || continue

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Private packages fail changelog presence

Medium Severity

The presence check treats any packages/*/CHANGELOG.md as a published package. Several private workspaces ship a changelog from the template, including agentmask, create-package, kernel-agents, template-package, and sample-services. Edits there now fail this job even though yarn changelog:validate skips them with --no-private.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 5c2bd57. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant