Skip to content

fix(release): nightly gate must pin the entry-point workflow, not event_name - #1358

Merged
thinmintdev merged 4 commits into
mainfrom
fix/nightly-workflow-call-gate
Jul 27, 2026
Merged

fix(release): nightly gate must pin the entry-point workflow, not event_name#1358
thinmintdev merged 4 commits into
mainfrom
fix/nightly-workflow-call-gate

Conversation

@thinmintdev

Copy link
Copy Markdown
Contributor

Nightly releases have been failing since 2026-07-24 (run 30077877380, again 30254411300) with:

##[error]nightly releases require workflow_call

Root cause

c41db8e5 ("scope manifest signing identities") added a trigger-shape rule to the Resolve immutable release policy job that no run can satisfy:

if [[ "${GITHUB_EVENT_NAME}" != "workflow_call" ]]; then
  echo "::error::nightly releases require workflow_call"

Under workflow_call the github context belongs to the caller, so GITHUB_EVENT_NAME is schedule (cron) or workflow_dispatch (forced re-cut) — never the literal workflow_call. This repo already established that caller/callee split empirically in 8cba6dd5 (#832).

The caller (nightly.yml) was correct all along and is untouched.

Note the 07-25 and 07-26 runs were not genuinely green: both ~13s with the release job skipped, because main HEAD was already nightly-tagged.

Fix

The gate regressed, so the gate is restored — in a form strictly narrower than the original intent. GITHUB_WORKFLOW_REF must match the canonical nightly.yml@refs/heads/main, which names the repository, entry-point workflow, and branch, where "was a reusable call" named none of them. Fails closed when unset. The event-shape assertion is kept in satisfiable form. Stable/preview path untouched.

Verification

  • tests/release/ tests/updater/ tests/installer/367 passed, 1 skipped (skip: shellcheck absent)
  • New contract test is red against pre-fix release.yml, green after
  • The gate bash was extracted and driven through 10 env scenarios: both legitimate nightly shapes pass; rogue caller workflow, attacker branch, fork prefix, and unset var all reject

Not provable offline

That the runner populates GITHUB_WORKFLOW_REF as <owner>/<repo>/.github/workflows/nightly.yml@refs/heads/main inside a called reusable workflow. Evidence is indirect but strong (#832). Fails closed if wrong. Validate after merge with gh workflow run nightly.yml --ref main -f force=true.

Nothing downstream of this gate has executed since 2026-07-23, so a later-stage failure could still be hiding behind it.

🤖 Generated with Claude Code

thinmintdev and others added 3 commits July 27, 2026 08:59
…nt_name

c41db8e ("scope manifest signing identities") added a trigger-shape rule to
the "Resolve immutable release policy" job that no run can ever satisfy:

    if [[ "${GITHUB_EVENT_NAME}" != "workflow_call" ]]; then
      echo "::error::nightly releases require workflow_call"

Under workflow_call the `github` context — and therefore GITHUB_EVENT_NAME —
belongs to the CALLER, so a nightly reports `schedule` (cron) or
`workflow_dispatch` (forced re-cut), never the literal "workflow_call". This
repo already established that caller/callee context split empirically in
8cba6dd (#832), where github.workflow_ref was found to be the entry-point
workflow (nightly.yml) rather than the signing workflow.

The gate has therefore failed closed on every nightly that actually reached
the release job since the hardening landed: run 30077877380 (2026-07-24) and
run 30254411300 (2026-07-27). The 07-25 and 07-26 runs were green only
because the change gate skipped the release job entirely (main HEAD was
already nightly-tagged), and the 07-23 run predates the hardening.

The caller is correct and is left untouched — nightly.yml does invoke
release.yml via `uses:` from refs/heads/main. The gate is what regressed, so
the gate is restored to a satisfiable form that is strictly narrower than the
original intent: GITHUB_WORKFLOW_REF (the entry-point ref) must equal the
canonical nightly.yml at refs/heads/main. That names the repository, the
entry-point workflow, and the branch, where "was a reusable call" named none
of them. It fails closed when the variable is unset. The event-shape
assertion is kept in satisfiable form (schedule or workflow_dispatch, the two
triggers nightly.yml declares), and the refs/heads/main check plus the
refs/heads/main signer identity are unchanged. The stable/preview branch is
untouched.

Adds a static contract test that fails against the pre-fix workflow.

Not provable offline: only a real nightly run can confirm GITHUB_WORKFLOW_REF
is populated as expected on the runner.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
CI's ruff reformats this file; the lane's local ruff did not (lockfile skew —
the repo venv is unreadable so the lane fell back to system ruff).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@thinmintdev
thinmintdev enabled auto-merge (squash) July 27, 2026 14:54
@thinmintdev
thinmintdev merged commit 185092a into main Jul 27, 2026
4 checks passed
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