Skip to content

feat: support GitHub App token sources for state and release tokens#240

Merged
joshua-temple merged 1 commit into
mainfrom
feat/app-token-sources
Jun 21, 2026
Merged

feat: support GitHub App token sources for state and release tokens#240
joshua-temple merged 1 commit into
mainfrom
feat/app-token-sources

Conversation

@joshua-temple

Copy link
Copy Markdown
Collaborator

Problem

release_token/state_token accept only a static secret expression. A static secret works for a PAT, but a GitHub App identity is the better fit for a protected trunk: it is owned by an org (survives a person rotating a PAT or leaving), mints a per-run token that expires in about an hour (no long-lived push-capable secret on the trunk, only the App private key is stored), and can be added directly to a ruleset bypass list. Closes #97 and #6.

Fix

Add optional, additive sibling fields release_token_app and state_token_app on the trunk config, each an AppTokenSource{ app_id, private_key } carrying secret REFERENCES (never raw key material). When a seam has an App source, the generator emits an actions/create-github-app-token minting step before the consuming steps and rewires consumers to the minted output; otherwise behavior is unchanged.

  • A configured App source takes precedence over the static token for that seam, applied uniformly to every job that consumes the token (release/promote/rollback/hotfix/setup-cli release seam, and the state-write/finalize state seam).
  • Validation: when an App source is present, both app_id and private_key are required and must be secret-reference shaped; raw key material (a newline or the PRIVATE KEY marker) is rejected. Lenient on absence.
  • schema_version is not bumped (stays 1). All three manifest.schema.json copies regenerated and byte-identical.
  • Both the static-secret/PAT path and the GitHub App path are documented (operator setup + manifest config).

Pin added: actions/create-github-app-token@v3 (sha bcd2ba49218906704ab6c1aa796996da409d3eb1, v3.2.0), via the existing pin registry so pin policy applies uniformly.

act/gitea fallback

The minting action has no GitHub App under act/gitea, so the mint step is guarded by if: ${{ github.server_url == 'https://github.com' }} (matching the existing real-vs-gitea split that keys on GITHUB_SERVER_URL). Consumers reference ${{ steps.<mint-id>.outputs.token || <static-fallback> }}: on gitea the skipped step's empty output falls through to the static token, so the e2e harness stays green without configuring an App source.

Verification

  • OFF-state is byte-identical: with no App source configured, generated output is unchanged. TestPlan_MatchesGeneratedBytes, determinism, and all goldens stay green.
  • App-enabled output asserted (mint step id + create-github-app-token uses + if-guard + fallback ref) including an actionlint pass.
  • Validation tests cover half-config and raw-key-material rejection, plus a manifest using the new fields validating at the current schema version.
  • e2e scenario 30-app-token-source proves the act path is unaffected (no-App manifest regenerates with no drift) and an App-source manifest parses/validates with the act fallback keeping the workflow runnable.
  • go build, go test ./..., golangci-lint run ./..., and the e2e module build/vet all pass.

Signed-off-by: Joshua Temple <joshua.temple@stablekernel.com>
@joshua-temple joshua-temple enabled auto-merge (squash) June 21, 2026 10:54
@joshua-temple joshua-temple merged commit 213af76 into main Jun 21, 2026
15 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.

Support GitHub App identities for state_token and release_token

1 participant