Summary
In the safe_outputs job, the checkout steps are generated by the same CheckoutManager generators the agent job uses (GenerateDefaultCheckoutStep / GenerateAdditionalCheckoutSteps). Those generators emit persist-credentials: false, so actions/checkout runs its post-checkout "Removing auth" step and strips the http.<server>/.extraheader credential and the embedded credential helper config from each checkout's .git/config.
That security default is correct for the agent job (the untrusted agent must not be able to read credentials from disk), but it is wrong for the safe_outputs job, which legitimately performs git fetch / git push against the checked-out repositories (e.g. push_to_pull_request_branch, create_pull_request).
Evidence
Failing run (github/github-automation ci-perf):
push_to_pull_request_branch failed with: Failed to apply bundle: Failed to fetch bundle: fatal: couldn't find remote ref refs/heads/dsyme/ci-perf/...
- Step 7 ("Checkout ./github-automation") and the cross-repo checkout of
github/github both run persist-credentials: false, triggering actions/checkout's Removing auth / Removing HTTP extra header post-step.
After auth is stripped, the safe_outputs job relies on a fragile combination to re-establish credentials:
- a "Configure Git credentials" step that does
git -C <path> remote set-url origin https://x-access-token:${GIT_TOKEN}@.../repo.git, and
- per-operation
GIT_CONFIG_* (http.<server>/.extraheader) env injected by the handler (getGitAuthEnv).
This layering is brittle (token-in-URL vs extraheader precedence, per-remote re-auth, shallow/partial-clone interactions) and is the kind of state that leads to fetch/push failures in the safe_outputs job.
Expected behavior
For the safe_outputs job checkout generation (now unified with the agent job), credentials should be retained (persist-credentials: true, no post-checkout auth removal) because subsequent git push / git fetch are allowed and expected in that job. The agent job must keep persist-credentials: false.
Proposed fix
Add a "keep credentials for push" mode to CheckoutManager that the safe_outputs path (buildSharedPRCheckoutSteps) enables. In that mode the default and additional checkout generators emit persist-credentials: true and skip the credential-cleanup step, so the push-capable token installed at checkout time remains available for the handlers. The agent job is unchanged.
Affected code
pkg/workflow/checkout_step_generator.go (GenerateDefaultCheckoutStep, generateCheckoutStepLines)
pkg/workflow/checkout_manager.go (CheckoutManager)
pkg/workflow/compiler_safe_outputs_steps.go (buildSharedPRCheckoutSteps)
Summary
In the safe_outputs job, the checkout steps are generated by the same
CheckoutManagergenerators the agent job uses (GenerateDefaultCheckoutStep/GenerateAdditionalCheckoutSteps). Those generators emitpersist-credentials: false, soactions/checkoutruns its post-checkout "Removing auth" step and strips thehttp.<server>/.extraheadercredential and the embedded credential helper config from each checkout's.git/config.That security default is correct for the agent job (the untrusted agent must not be able to read credentials from disk), but it is wrong for the safe_outputs job, which legitimately performs
git fetch/git pushagainst the checked-out repositories (e.g.push_to_pull_request_branch,create_pull_request).Evidence
Failing run (github/github-automation
ci-perf):push_to_pull_request_branchfailed with:Failed to apply bundle: Failed to fetch bundle: fatal: couldn't find remote ref refs/heads/dsyme/ci-perf/...github/githubboth runpersist-credentials: false, triggering actions/checkout'sRemoving auth/Removing HTTP extra headerpost-step.After auth is stripped, the safe_outputs job relies on a fragile combination to re-establish credentials:
git -C <path> remote set-url origin https://x-access-token:${GIT_TOKEN}@.../repo.git, andGIT_CONFIG_*(http.<server>/.extraheader) env injected by the handler (getGitAuthEnv).This layering is brittle (token-in-URL vs extraheader precedence, per-remote re-auth, shallow/partial-clone interactions) and is the kind of state that leads to fetch/push failures in the safe_outputs job.
Expected behavior
For the safe_outputs job checkout generation (now unified with the agent job), credentials should be retained (
persist-credentials: true, no post-checkout auth removal) because subsequentgit push/git fetchare allowed and expected in that job. The agent job must keeppersist-credentials: false.Proposed fix
Add a "keep credentials for push" mode to
CheckoutManagerthat the safe_outputs path (buildSharedPRCheckoutSteps) enables. In that mode the default and additional checkout generators emitpersist-credentials: trueand skip the credential-cleanup step, so the push-capable token installed at checkout time remains available for the handlers. The agent job is unchanged.Affected code
pkg/workflow/checkout_step_generator.go(GenerateDefaultCheckoutStep,generateCheckoutStepLines)pkg/workflow/checkout_manager.go(CheckoutManager)pkg/workflow/compiler_safe_outputs_steps.go(buildSharedPRCheckoutSteps)