Skip to content

fix: push external-update state with state token, not bot#203

Merged
joshua-temple merged 2 commits into
mainfrom
fix/external-update-token
Jun 17, 2026
Merged

fix: push external-update state with state token, not bot#203
joshua-temple merged 2 commits into
mainfrom
fix/external-update-token

Conversation

@joshua-temple

Copy link
Copy Markdown
Collaborator

Problem

The generated external-update receiver checked out with a hardcoded ${{ secrets.GITHUB_TOKEN }}, so its manifest git push ran as github-actions[bot]. On a primary repo whose trunk has required_pull_request_reviews, that identity is blocked by the PR-review rule even with enforce_admins disabled (which only exempts admin users). External-update was the only state-writer using the bot; orchestrate, promote, and hotfix check out with the configured state token and bypass protection. The push failure was also opaque because the push output was discarded, so the underlying protected-branch rejection never surfaced.

Fix

  • Token (internal/generate/external.go): the receiver now checks out with the state token (g.config.GetStateToken()), the same accessor orchestrate/promote/hotfix use, with fetch-depth: 0 for parity. Falls back to GITHUB_TOKEN when state_token is unset (back-compat).
  • Diagnosability (internal/external/command.go): the captured push output is now included in the failure error.
  • Concurrency (internal/generate/external.go): the receiver concurrency group defaults to the same ref-scoped key orchestrate serializes on (orchestrate-${{ github.ref }}), with cancel-in-progress: false, so external and internal state writes serialize on one queue and an incoming notification never cancels a live pipeline. An explicit concurrency.group is still honored.

This is record-only behavior; deploy-on-update is a separate, deferred capability and is not implemented here.

Verification

  • go build ./..., go test ./... (1406 pass), golangci-lint run ./... (0 issues) all green.
  • New tests: receiver checkout emits the state token + fetch-depth: 0; back-compat default to GITHUB_TOKEN; default concurrency group shares orchestrate's queue and is non-cancelling; push failure surfaces the captured output.

The generated external-update receiver checked out with a hardcoded
${{ secrets.GITHUB_TOKEN }}, so its manifest push ran as
github-actions[bot]. On a primary repo whose trunk has required pull
request reviews, that identity is blocked even with enforce_admins
disabled, so external-update failed with a swallowed protected-branch
rejection. The other state-writers (orchestrate, promote, hotfix) check
out with the configured state token and bypass protection.

- external.go: check out the receiver with the state token (falls back
  to GITHUB_TOKEN when state_token is unset) and fetch-depth: 0, for
  parity with the other state-writers.
- command.go: include the captured push output in the failure error so a
  rejected push is diagnosable instead of opaque.
- external.go: default the receiver concurrency group to the same
  ref-scoped key orchestrate uses, with cancel-in-progress false, so
  external and internal state writes serialize on one queue and an
  incoming notification never cancels a live pipeline.

Signed-off-by: Joshua Temple <joshua.temple@stablekernel.com>
@joshua-temple joshua-temple enabled auto-merge (squash) June 17, 2026 14:00
Signed-off-by: Joshua Temple <joshua.temple@stablekernel.com>
@joshua-temple joshua-temple merged commit 2eec1bb into main Jun 17, 2026
7 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