Scope the remaining release app tokens and bind a trailer - #6272
Merged
Conversation
actions/create-github-app-token with no permission-* inputs mints a token carrying every permission the app installation holds. This one pushes the tag and creates the GitHub Release, both of which are contents, so that is all it now asks for. The release-creation step also interpolated the triggering actor into the shell. That value is read out of a git commit trailer, so unlike the version — which is checked against a semver pattern before use — nothing constrains it. It is bound through the step's existing env: block. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #6272 +/- ##
==========================================
+ Coverage 72.70% 72.77% +0.06%
==========================================
Files 742 742
Lines 77299 77299
==========================================
+ Hits 56201 56255 +54
+ Misses 17136 17064 -72
- Partials 3962 3980 +18 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
The token was already limited to a single repository by owner and repositories, but not to a single permission. GoReleaser commits the formula straight to the tap's default branch — the brews block configures no pull request — so contents is all it uses. Confirmed against the tap's history: every release lands as a direct "Brew formula update" commit, and the app token is the push credential, so the installation demonstrably holds this permission already. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
jhrozek
approved these changes
Aug 10, 2026
This was referenced Aug 10, 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.
Continues the release-workflow work in #6253, after #6263, #6266 and #6270. Covers both remaining
github-appfindings — the last two unscoped app tokens in the repository.Summary
actions/create-github-app-tokenwith nopermission-*inputs mints an installation-wide token. Both now name what they use.TRIGGERED_BY="${{ steps.actor.outputs.triggered_by }}", where the value comes fromgit log --format='%(trailers:key=Release-Triggered-By,valueonly)'. Unlike the version in the same step — checked against^[0-9]+\.[0-9]+\.[0-9]+$before use — nothing constrains a trailer. Bound through the step's existingenv:block.create-release-tag.yml— release appgit pushthe tag,gh release createcontents: writereleaser.yml— Homebrew tap appstacklok/homebrew-tapcontents: writeRepo-wide High goes 4 → 2. Both survivors are in
helm-publish.ymland are fixed by #6270, so these two PRs together take High to zero.Confidence
The Homebrew tap token is the better-evidenced of the two. It was already limited to a single repository by
owner:+repositories:, so this narrows it from "everything on one auxiliary repo" to "one permission on one auxiliary repo". Three things confirmcontents: writeis both sufficient and available:brews:block configures nopull_request:, so the formula is committed straight to the tap's default branch.Brew formula update for toolhive version vX.Y.Zcommit, never a pull request.HOMEBREW_TAP_GITHUB_TOKENis wired from this token's output, so the token is the push credential. Those pushes have succeeded every release for months, which means the installation already holds the permission. No org-settings check is needed.Its failure mode is also the mildest in the release path: the formula step runs at the end of
goreleaser release --clean, after the binaries are uploaded, and this workflow already has a "Remove existing release assets (allows re-runs)" step because it is designed to be re-run.The release tag token will mint — the same
RELEASE_APPwas shown to holdcontents: writeby the #6266 dispatch, andpermission-*can only narrow. Two smaller things remain inferred rather than observed:Workflows: writewhen the resolved target commit modifies workflow files. Release commits touchVERSION, chart manifests and generated chart READMEs, so this should not apply — but it is a conditional a blanket token satisfies silently, and requestingworkflows: writeas insurance is not an option: if the installation lacks it, the token step fails outright.--generate-noteshas no documented fine-grained permission requirement.Its failure mode: it fires when a release pull request merges, and a token failure stops the run after the merge is on
mainbut before the tag exists — soreleaser.ymlnever triggers and nothing is half-published. Recovery is re-running the workflow or tagging by hand. It stalls a release rather than corrupting one.Neither workflow can be exercised by a pull request.
create-release-tag.ymlruns onpushtomainwithpaths: [VERSION];releaser.ymlonrelease: published. Neither has aworkflow_dispatch, so unlike #6266 there is no dry run.Part of #6253
Type of change
Test plan
Unit tests (
task test)E2E tests (
task test-e2e)Linting (
task lint-fix)Manual testing (describe below)
Traced every use of both tokens. The release app token is the
GH_TOKENfor the tag push, the release-existence check and the release creation, and nothing else. The tap token is passed only asHOMEBREW_TAP_GITHUB_TOKENto GoReleaser.Established the tap's update mechanism from its own commit history and from the GoReleaser config, rather than assuming it.
Confirmed the
TRIGGERED_BYbinding preserves behaviour: the value is in the step'senv:, the script still reads$TRIGGERED_BY, and no interpolation ofsteps.actor.outputsremains in the block.Confirmed the version in that same step is validated against a semver pattern earlier in the job, which is why it is left interpolated rather than converted for symmetry.
Both workflows parse as YAML;
actionlintreports nothing new —releaser.yml's one finding is the pre-existingextract-release-actorreference tracked in Release failure Slack notice never shows the release triggerer #6265.zizmor:github-app2 → 0.Does this introduce a user-facing change?
No.
Special notes for reviewers
create-release-tag.ymlchange, it can be dropped and replaced with a justified# zizmor: ignore[github-app], the way Suppress four zizmor findings that cannot be fixed #6262 handled findings that cannot be fixed — a--min-severity=highgate stays reachable either way. The Homebrew change is worth keeping regardless, since its permission is confirmed by months of successful pushes.template-injectionfindings in these files aregithub.repositoryand the semver-validated version, neither of which can carry a shell metacharacter. Left alone deliberately; recorded in zizmor findings backlog: pins, expressions and checkout credentials #6253.Generated with Claude Code