feat(ci): Disclose Hotfix workflow - #11361
Open
basvandijk wants to merge 3 commits into
Open
Conversation
Publishes a security hotfix built in dfinity/ic-private by pushing its exact elected commit to dfinity/ic as the same hotfix-* branch, authenticated as the "Push to RC Branch" GitHub App. The push triggers Release Testing, which re-builds the commit, re-uploads byte-identically (rclone --checksum --immutable) and mints the build-provenance attestation that consumers verify CDN downloads against. The push is create-only (--force-with-lease with an empty expected value) and validates that the ic-private branch tip is exactly the requested commit; a branch already disclosed at that commit is a no-op, any other state fails loudly. The DISCLOSE_HOTFIX environment gates the app credentials behind required reviewers and a master-only deployment branch policy. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Adds a manually dispatched workflow for safely disclosing private security hotfix branches to the public repository.
Changes:
- Validates hotfix branch and commit inputs.
- Uses a GitHub App token to verify and create the public branch safely.
- Reports disclosure status and the downstream Release Testing run.
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
basvandijk
commented
Aug 27, 2026
basvandijk
commented
Aug 27, 2026
The DISCLOSE_HOTFIX required reviewers approve the run before any step executes, and GitHub surfaces workflow_dispatch inputs neither in the run list nor in the approval dialog, so without a run-name a reviewer approves app-credential access without seeing which branch and commit they are disclosing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- The attest-uploads precondition now matches the top-level job key instead of a bare substring, so comments, needs: references or commented-out jobs no longer satisfy it (Copilot). - The Release Testing run-URL poll filters by branch and event=push in addition to head_sha, so it cannot select a run for another branch or a prior workflow_dispatch at the same commit (Copilot). - Incident-signal wording now names the infra team, and the header names the reviewer teams (basvandijk). Co-Authored-By: Claude Fable 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.
Adds a
workflow_dispatchworkflow that discloses a security hotfix built and deployed from the private dfinity/ic-private repository: it pushes the exact elected commit to this repository as the samehotfix-*branch, authenticated as the "Push to RC Branch" GitHub App. The push triggers Release Testing, which re-builds the commit, re-uploads its artifacts (rclone--checksum --immutable: identical bytes ⇒ no-op, differing bytes ⇒ hard failure) and mints the build-provenance attestation that release workflows and the mainnet-revisions updater verify CDN downloads against (attestation stack: #11323 → #11324 → #11325 → #11326).Runs of this workflow have to be approved by either DRE or Infra.
Flow
confirmcheckbox must be ticked (booleanrequired: truedoes not enforce this, same pattern as ci-kickoff-manual.yml), branch must matchhotfix-<suffix>of[A-Za-z0-9._-]+(/excluded: branch protections and triggers matchhotfix-*with fnmatch, where*does not cross/), commit must be a full 40-hex sha.dfinity/ic+dfinity/ic-private(same action pin as schedule-rc.yml).ls-remoteonly: the ic-private branch must exist with its tip exactly at the requested commit; on dfinity/ic the branch must be absent (proceed), already at the commit (succeed as a no-op) or anything else (fail loudly — never move an existing branch).attest-uploadsjob (the run then can't mint an attestation).--force-with-lease="refs/heads/<branch>:"(empty expected value = the ref must not exist, so the push can create but never move/fast-forward); a concurrent creation at the same commit is treated as a benign race; post-verify withls-remote.attest-uploadsis green).Why the tip must be exact
The attestation binds to the branch tip that Release Testing builds. A descendant or different commit would attest the wrong sha — hence exact-tip validation, the create-only lease, and the per-branch
concurrencygroup (queued re-runs re-validate and converge to no-op/conflict instead of racing).Environment / prerequisites
DISCLOSE_HOTFIXenvironment exists (2026-08-27) with required reviewers (dfinity/dre + dfinity/infra), a master-only deployment branch policy (a Write user dispatching a modified copy of this workflow from a side branch cannot reach the credentials) and environment-scoped copies of the app credentials. It is separate fromPUSH_TO_RC_BRANCHbecause required reviewers there would leave schedule-rc's nightly cron hanging.dfinity/ic-private(Contents: read).if: github.repository == 'dfinity/ic').Validation
runblocks (extracted from the YAML) were replayed against scratch bare repos: happy path creates the ref at exactly the commit; re-run is a no-op; existing-at-different-commit (including an ancestor — the silent-fast-forward case) is rejected both by the precheck and independently by the lease; private-tip-moved-after-validation aborts at theFETCH_HEADre-check; benign/hostile create races behave as designed;public-hotfix-x,hotfix-a/b,hotfix-a..b, short shas and an uncheckedconfirmall fail input validation. 24/24 passed.actionlint(1.7.12) and shellcheck clean.hotfix-attest-drill-*branch from ic-private at a post-stack commit; confirm push → Release Testing → rclone no-ops →attest-uploadsmints; re-dispatch with identical inputs (no-op) and with a different commit (conflict failure); delete the branch.Operator usage
Actions UI → Disclose Hotfix → branch + commit + confirm, or:
🤖 Generated with Claude Code