bug(recover): the recovery workflow's push fails with HTTP 400 because the checkout's Authorization header is never unset #1574

Open
opened 2026-10-08 01:32:14 +02:00 by engineer · 0 comments
Member

reusable-recover-pending-cut.yml cannot push its recovery branch on an adopter that checks out with the default persist-credentials. The push fails with HTTP 400, so the dispatch-only recovery workflow (#1135) never opens its PR.

Measured 2026-10-08, frankenbit/tic-tac-toe, pinned @v0.67.0

Run 32, recover pending cut, version=0.2.0, real run (dry run 31 on the same tree succeeded):

recovered: v0.2.0 is back under [Unreleased] in CHANGELOG.md; restored_fragments=2
restored fragment: changelog.d/1-release-toolkit-v0.67.0.changed.md
restored fragment: changelog.d/5-readme-links.fixed.md
Switched to a new branch 'recover-pending-cut/0.2.0'
fatal: unable to access 'https://git.frankenbit.de/frankenbit/tic-tac-toe/': The requested URL returned error: 400

Cause

  • The job's checkout step is actions/checkout@v4 with no persist-credentials: false. It leaves http.https://git.frankenbit.de/.extraheader = AUTHORIZATION: basic … in the repo-local git config.
  • The push (reusable-recover-pending-cut.yml:363 on v0.67.0 and on main) writes a second Authorization: token … extraheader into a GIT_CONFIG_GLOBAL file and does not unset the local one. git sends both headers.
  • Reproduced without credentials against git.frankenbit.de with git ls-remote --heads:
    • one dummy Authorization header: the normal auth challenge (could not read Username);
    • the same plus a URL-scoped AUTHORIZATION: basic header: The requested URL returned error: 400, the exact error above;
    • no header: anonymous read succeeds.
  • rt prep already handles this. withTokenConfig (internal/prep/git.go:393, :558) unsets http.<base>/.extraheader first, "or it would out-rank GIT_CONFIG_GLOBAL". The recovery step is shell, not rt prep, and has no such line.

Not measured

  • Whether toolkit-self's own recovery wrapper hits it. It uses the same reusable, so I expect so.
  • Whether the step's later API calls (opening the PR) have a similar problem. The run never got that far.

Workaround used

The documented local route: run rt recover-pending-cut locally and commit by hand, which produced tic-tac-toe#8. The restore itself is unaffected: the restored fragments are byte-identical to the prepare's parent.

Arm the fix should carry

A test in which the checkout's persisted extraheader is present at push time. A step that works only on a credential-free checkout would pass every arm that never runs actions/checkout.

Refs #1135, #1166

`reusable-recover-pending-cut.yml` cannot push its recovery branch on an adopter that checks out with the default `persist-credentials`. The push fails with HTTP 400, so the dispatch-only recovery workflow (#1135) never opens its PR. ## Measured 2026-10-08, frankenbit/tic-tac-toe, pinned `@v0.67.0` Run 32, `recover pending cut`, `version=0.2.0`, real run (dry run 31 on the same tree succeeded): ``` recovered: v0.2.0 is back under [Unreleased] in CHANGELOG.md; restored_fragments=2 restored fragment: changelog.d/1-release-toolkit-v0.67.0.changed.md restored fragment: changelog.d/5-readme-links.fixed.md Switched to a new branch 'recover-pending-cut/0.2.0' fatal: unable to access 'https://git.frankenbit.de/frankenbit/tic-tac-toe/': The requested URL returned error: 400 ``` ## Cause - The job's `checkout` step is `actions/checkout@v4` with no `persist-credentials: false`. It leaves `http.https://git.frankenbit.de/.extraheader = AUTHORIZATION: basic …` in the repo-local git config. - The push (`reusable-recover-pending-cut.yml:363` on `v0.67.0` and on `main`) writes a second `Authorization: token …` extraheader into a `GIT_CONFIG_GLOBAL` file and does not unset the local one. git sends both headers. - **Reproduced without credentials** against git.frankenbit.de with `git ls-remote --heads`: - one dummy `Authorization` header: the normal auth challenge (`could not read Username`); - the same plus a URL-scoped `AUTHORIZATION: basic` header: `The requested URL returned error: 400`, the exact error above; - no header: anonymous read succeeds. - `rt prep` already handles this. `withTokenConfig` (`internal/prep/git.go:393`, `:558`) unsets `http.<base>/.extraheader` first, "or it would out-rank GIT_CONFIG_GLOBAL". The recovery step is shell, not `rt prep`, and has no such line. ## Not measured - Whether toolkit-self's own recovery wrapper hits it. It uses the same reusable, so I expect so. - Whether the step's later API calls (opening the PR) have a similar problem. The run never got that far. ## Workaround used The documented local route: run `rt recover-pending-cut` locally and commit by hand, which produced tic-tac-toe#8. The restore itself is unaffected: the restored fragments are byte-identical to the prepare's parent. ## Arm the fix should carry A test in which the checkout's persisted extraheader is present at push time. A step that works only on a credential-free checkout would pass every arm that never runs `actions/checkout`. Refs #1135, #1166
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
frankenbit/release-toolkit#1574
No description provided.