ci: pin GitHub Actions to commit SHAs, move Slack notifications to v4 - #59
Merged
Conversation
ysibirski
force-pushed
the
ci/pin-gha-actions-to-sha
branch
3 times, most recently
from
August 19, 2026 15:40
e7e7e5d to
5aaab3d
Compare
Every `uses:` reference moves from a mutable tag to the exact commit that tag
resolves to today, so a tag repointed upstream cannot silently change what runs
in CI. Each SHA was resolved from the ref already present in the workflow and
checked against the GitHub API, including dereferencing annotated tags.
Two references are not pure pins.
actions-rs/toolchain is archived and read-only upstream, so it is replaced by
dtolnay/rust-toolchain. The inputs differ, so the `with:` block changes with it:
dtolnay declares neither `profile` nor `override` (it hardcodes
`--profile minimal` and runs `rustup default`), and spells `target` as
`targets`.
slackapi/slack-github-action moves from v1.25.0 (Jan 2024, and the v1 line ended
that November) to v4.0.0. v2 moved the webhook URL and type from env vars to
`webhook`/`webhook-type` inputs, and made a failed post non-fatal by default, so
`errors: true` is set on all three steps to keep the v1 behaviour of failing the
step. v4 also retries a failed post five times over about five minutes before
giving up, where v1 posted once. Payloads are JSON and use only GitHub `${{ }}`
expressions, so neither `payload-templated` nor the v4 YAML parsing change
applies. The runners report 2.336.0, which satisfies the Node 24 runtime v3
introduced.
The two release notifiers additionally get `continue-on-error: true`. They run
after the release and images have already published, so a notification problem
should stay visible as a red step without taking the release job down with it.
The audit notifier deliberately does not get it: that job exists only to notify,
so a delivery failure there must still fail.
The audit notification also moves to the SLACK_WEBHOOK_ALERTS_ZKVERIFY secret
and gains a repo prefix in its header. Both repos alert into the same channel
and `github.workflow` renders as "Audit" in each, so the two headers were
previously indistinguishable.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ysibirski
force-pushed
the
ci/pin-gha-actions-to-sha
branch
from
August 19, 2026 17:06
5aaab3d to
1154265
Compare
drgora
approved these changes
Aug 20, 2026
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.
Description
Pins every GitHub Actions reference to a commit SHA, replaces an archived action, and moves the Slack notifications off an unmaintained major version.
A tag like
@v4is mutable: whoever controls the upstream repo can repoint it at any commit, and CI picks that up on the next run with no change here. A SHA cannot be repointed.Jira Ticket
N/A
Changes
Pinning
44 references across 16 files. Every SHA below is the commit its existing ref resolves to today, so those actions run byte-identically after this merge.
actions/checkout×18@v411d5960a326750d5838078e36cf38b85af677262# v4.4.0actions/upload-artifact×10@v4ea165f8d65b6e75b540449e92b4886f43607fa02# v4.6.2actions/download-artifact×5@v4d3f86a106a0bac45b974a628896c90dbdf5c8093# v4.3.0softprops/action-gh-release×2@v23bb12739c298aeb8a4eeaf626c5b8d85266b0e65# v2.6.2WarpBuilds/cache×2@v140f3443ae7b70e568d6e2070ea897f3df94d7553# v1.4.13WarpBuilds/cache/restore×1@v140f3443ae7b70e568d6e2070ea897f3df94d7553# v1.4.13WarpBuilds/cache/save×1@v140f3443ae7b70e568d6e2070ea897f3df94d7553# v1.4.13Swatinem/rust-cache×1@v2.5.02656b87321093db1cb55fbd73183d195214fdfd1# v2.5.0actions-rs/toolchain×1@v1dtolnay/rust-toolchain@6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772# v1slackapi/slack-github-action×3@v1.25.0dcb1066f776dd043e64d0e8ba94ca15cc7e1875d# v4.0.0Ten of these live in the composite actions under
.github/actions/, not in.github/workflows/.The archived action
actions-rs/toolchainis archived and read-only upstream, so pinning it would freeze CI onto an action that receives no fixes. Replaced withdtolnay/rust-toolchain, its de facto successor. The inputs are not identical, so thewith:block inglobal.ymlchanges with it:profile: minimalandoverride: trueare gone becausedtolnay/rust-toolchaindeclares neither input: it hardcodes--profile minimalin its rustup invocation, and runsrustup default(process-wide, broader than the old directory-scopedrustup override set).targetbecomestargets. Read off the action's ownaction.ymlat the pinned commit, which declares exactlytoolchain,targets,target(an alias) andcomponents.Slack action v1.25.0 → v4.0.0
v1.25.0 is from January 2024 and the v1 line ended that November, so it no longer receives fixes. The interface changed in v2, so the
env:block becomes inputs:errors: trueis load-bearing. v2 onward defaults it tofalse, which swallows a failed post entirely — the only trace is acore.debugcall, invisible withoutACTIONS_STEP_DEBUG, and the step exits 0. v1.25.0 always calledcore.setFailed. Without this line the bump would be a silent downgrade, turning a visible failure into a green job with no Slack message.One behaviour change to be aware of: v4 retries a failed post five times over about five minutes before giving up, where v1 posted once.
Slack failure semantics, split by purpose
errors: trueis set on all six steps, but the two release notifiers also getcontinue-on-error: true:errorscontinue-on-errorCI-auditfailure notificationtruetruetrueWithout the second column, a Slack outage retroactively reds a successful release. In this repo that also skipped the rustdoc deploy, since
rustdoc-jobdeclaresneeds: [release]— exactly what happened on 2026-08-07.continue-on-errorcovers both failure modes, including payload parse errors, whicherrorsdoes not govern.Audit alert webhook and header
The audit notification moves from
QA_SLACK_WEBHOOK_URLtoSLACK_WEBHOOK_ALERTS_ZKVERIFY, and its header gains a repo prefix:${{ github.workflow }}renders asAuditin both repos, so before this the two headers were byte-identical — unusable once both alert into the same channel.QA_SLACK_WEBHOOK_URLmust not be deleted — it is still referenced byzkVerify-qa,zkverifyjs,attestation-bot, both*_ci_testingmirrors, andHorizenLabs/NH-core-ci_testing.Breaking Changes
None expected. Apart from the Slack steps and the four
with:lines inglobal.yml, every changed line is auses:line.The toolchain step is the one to watch on the first run: it installs the same stable toolchain, the same
wasm32-unknown-unknowntarget and the same components, but through a different action.Slack failure semantics change deliberately for the release notifiers: an undeliverable notification is now a red step rather than a red job. The audit notifier keeps v1 semantics exactly.
Checks
Additional information
Rather than hand-editing,
.github/was reset toHEADand the pinning regenerated from a pin table, so no stray edit could survive. Each pin was then verified against the GitHub API: resolve the ref that was in the file before this PR, dereference the annotated tag to its commit, assert it equals the SHA written. All report identical. Nouses:reference anywhere under.github/is left on a non-SHA ref, noactions-rs/*reference remains, and all 23 YAML files parse.The v1 → v4 Slack jump was verified against the action's own source at the pinned commit rather than its release notes:
webhook,webhook-typeanderrorsare the correct v4 input names, and the validator accepts only the literalincoming-webhook—INCOMING_WEBHOOKthrows. There is no reference toSLACK_WEBHOOK_TYPEanywhere in the v4 bundle, so moving the type towith:was mandatory.errorsgoverns network and Slack-response failures only; a payload parse error throws from the config constructor outside that path and fails the step regardless.${{→{{templating only topayload-file-path, never to an inlinepayload, and flattened only forWORKFLOW_TRIGGER; these steps use an incoming webhook. GitHub Actions expands${{ }}before the action sees the input, sopayload-templatedis irrelevant.actions/runnershipped Node 24 support in 2.327.0.JSON.parseregardless.webhook-trigger-only; theincoming-webhookpath is not affected.All six payloads across this repo and the sibling zkVerify repo were machine-validated — JSON parse after expression substitution, plus Block Kit shape checks. This repo's three are all valid; the one defective payload was in zkVerify's
CI-release.ymland is fixed in the companion PR.One follow-up deliberately out of scope:
continue-on-error: trueon the release-notify steps would let a Slack outage annotate the run without redding the release. That is a policy change and belongs in its own PR; as written, this PR preserves the v1 contract exactly.