bug(release): the post-condition step's rt default-branch call gets neither override variable and no API token #1564
Labels
No labels
bump
major
bump
minor
bump
patch
kind
bug
kind
chore
kind
docs
kind
epic
kind
feature
loop/paused
mirror-drift
priority
critical
priority
high
priority
low
priority
medium
size
L
size
M
size
S
size
XL
status
blocked
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
frankenbit/release-toolkit#1564
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
#1160 found that default-branch layers 1 (
RT_DEFAULT_BRANCH) and 3 (TOOLKIT_DEFAULT_BRANCH_HINT) were inert where recover-pending-cut callsrt default-branch, and wired them in. It compared that step with "the sibling callsite inreusable-release.yml", which is the act-on-decision step. A third callsite was not on #1160's list: the cut's post-condition step. That step sets neither variable, and it calls the API before the token is exported.Measured
All reads are on live
origin/main6a36090, 2026-10-06.The callsite.
.forgejo/workflows/reusable-release.yml:951,verify manifest bookkeeping (post-condition, #637), runs on every executed cut (:952:mode == 'cut' && cut_gate == 'execute').:983-986it runsrt default-branch --config "$CONFIG_PATH_INPUT" --default-branch "${RT_DEFAULT_BRANCH:-}" --default-branch-hint "${TOOLKIT_DEFAULT_BRANCH_HINT:-}".post-condition COULD NOT GRADE - default branch unresolvableand exits 2 (:987-988).rt default-branchcall in the file.What each layer gets there.
RT_DEFAULT_BRANCHenv:(:954-967) does not set it, the file has no workflow- or job-levelenv:, and the only$GITHUB_ENVwrite isCUT_START_MS(:528). The act-on-decision step sets it (:615), but step env does not cross steps, asreusable-recover-pending-cut.yml:252-253says (#1160).default_branch:in the config file--config)TOOLKIT_DEFAULT_BRANCH_HINT:614)rt default-branchalways calls the API (cmd/rt/default_branch.go,runDefaultBranch) withFORGEJO_TOKENfrom the environment (newForge,cmd/rt/prep.go:905-908). This step exportsFORGEJO_TOKENat:1033, 50 lines after the call.git symbolic-ref refs/remotes/origin/HEADactions/checkout@v4,fetch-depth: 0,:183-187) creates that refAn anonymous API read fails on a private repository. Measured on this forge:
GET /api/v1/repos/frankenbit/cid-probe(private) returns 404 without a token and 200 with one.frankenbit/release-toolkit(public) returns 200 both ways. An API error falls through to layer 5 (internal/prep/git.go:309-313).The
.giteatwin has the same shape: the call is at:996-999and the token export at:1046.The comments say otherwise.
:979-982calls this the "Same invocation as the path-α call at :685; the six-layer chain (#370/#379) is identical either way.":685is in the act-on-decision step, which no longer callsrt default-branch. The comments at:514and:604that say it does are stale as well.Why toolkit-self never sees it. Its repository is public, so the anonymous API read answers at layer 4.
Why it matters
This is a hypothesis until layer 5 is measured (first AC). On a private adopter repository with no
default_branch:in its config, layer 2 is empty too. If the checkout leavesorigin/HEADunset, every executed cut would then end with this step red (exit 2, could not grade), after the tag, the release and the manifest have already landed. The branch name does not matter: a repository onmainis exposed the same way as one onmaster.It also breaks a documented override.
docs/integration.mdtells adopters that avars.RT_DEFAULT_BRANCHrepo variable is picked up "automatically". At this step it is not.Acceptance criteria
actions/checkout@v4withfetch-depth: 0leavesrefs/remotes/origin/HEADset. Record the run and the log line here. The ACs below do not wait on it; it decides how exposed past private-repo cuts were.env:wiresRT_DEFAULT_BRANCH: ${{ vars.RT_DEFAULT_BRANCH }}andTOOLKIT_DEFAULT_BRANCH_HINT: ${{ github.event.repository.default_branch }}, as the act-on-decision step and recover-pending-cut do. The.giteatwin matches.rt default-branchat this step reaches the API with the resolved token, not anonymously. The PR says how, e.g. by exporting the token before the call.:514,:604and:979-982name the callsites as they are today.rt default-branchin a reusable workflow lacks either variable in itsenv:. It is mutation-verified: it fails against6a36090, where this step lacks both.go test ./...and the bats suite pass.Out of scope
rt prepfalls back to"main"(cmd/rt/prep.go:731) when no layer resolves, wherert default-branchrefuses.rt default-branch's own comment explains that refusal: a wrong guess is the #370 silent-corruption class. The act-on-decision step wires every layer, so the backstop is reached only through a misconfiguration. Whether it should refuse too is a separate decision.rt manifest-postconditionfetchesorigin mainwhenRT_DEFAULT_BRANCHis empty (cmd/rt/manifest_postcondition.go:280-284). In CI its result is then replaced by--landed-sha/--branch-readable(:157-160), so this costs one wasted fetch, not a wrong verdict.