Summary
Fork-backed create_pull_request fails every git operation against head-repo with HTTP 400 when head-github-app supplies a separate fork token.
This appears to be the create_pull_request fork equivalent of #40280: the safe-output checkout retains the upstream repository's persisted http.https://github.com/.extraheader, while the fork path supplies head-github-token separately. Git sends duplicate/conflicting Authorization headers to the fork remote.
Environment
- gh-aw compiler/setup: v0.83.1
create-pull-request.target-repo: microsoft/vscode
create-pull-request.head-repo: bryanchen-d/vscode
- upstream credential:
safe-outputs.github-app
- fork credential:
create-pull-request.head-github-app
- runner:
ubuntu-slim
safe-outputs:
github-app:
client-id: ${{ steps.kv-app.outputs.client-id }}
private-key: ${{ steps.kv-app.outputs.pem }}
owner: microsoft
repositories: [vscode]
create-pull-request:
target-repo: microsoft/vscode
head-repo: bryanchen-d/vscode
allowed-repos: [microsoft/vscode, bryanchen-d/vscode]
head-github-app:
client-id: ${{ steps.kv-app.outputs.client-id }}
private-key: ${{ steps.kv-app.outputs.pem }}
Observed
The head token mints successfully for exactly bryanchen-d/vscode. The handler generates a valid patch/bundle, then direct git push and its fallbacks fail with:
fatal: unable to access 'https://github.com/bryanchen-d/vscode.git/':
The requested URL returned error: 400
No fork branch is created. With fallback-as-issue: true, the handler creates a fallback issue and the workflow reports success.
Controlled probe
A temporary job minted a token from the same App key with the same scope:
owner: bryanchen-d
repositories: vscode
permission-contents: write
Using that token in the same runner/job:
- GitHub REST API ref create/delete succeeded.
- Plain git fetch succeeded.
- Plain git push creating a disposable fork branch succeeded.
- Plain git push deleting that branch succeeded.
The probe branch was deleted. This rules out App installation, repository selection, and effective contents-write permission.
Suspected code path
Current create_pull_request.cjs temporarily calls overridePersistedExtraheader(...) for ls-remote, then restores the upstream checkout header before pushSignedCommits(...) receives pushRemoteUrl plus the separate pushToken.
For a fork-backed PR, the persisted upstream header and fork credential must not both reach the fork request. The fork token should temporarily replace the persisted header for the entire fork remote operation, or the push should otherwise use exactly one Authorization source.
Expected
Fork branch is pushed with only the head-github-app token, then the upstream token creates the PR in target-repo.
Related
Summary
Fork-backed
create_pull_requestfails every git operation againsthead-repowith HTTP 400 whenhead-github-appsupplies a separate fork token.This appears to be the
create_pull_requestfork equivalent of #40280: the safe-output checkout retains the upstream repository's persistedhttp.https://github.com/.extraheader, while the fork path supplieshead-github-tokenseparately. Git sends duplicate/conflicting Authorization headers to the fork remote.Environment
create-pull-request.target-repo:microsoft/vscodecreate-pull-request.head-repo:bryanchen-d/vscodesafe-outputs.github-appcreate-pull-request.head-github-appubuntu-slimObserved
The head token mints successfully for exactly
bryanchen-d/vscode. The handler generates a valid patch/bundle, then direct git push and its fallbacks fail with:No fork branch is created. With
fallback-as-issue: true, the handler creates a fallback issue and the workflow reports success.Controlled probe
A temporary job minted a token from the same App key with the same scope:
Using that token in the same runner/job:
The probe branch was deleted. This rules out App installation, repository selection, and effective contents-write permission.
Suspected code path
Current
create_pull_request.cjstemporarily callsoverridePersistedExtraheader(...)forls-remote, then restores the upstream checkout header beforepushSignedCommits(...)receivespushRemoteUrlplus the separatepushToken.For a fork-backed PR, the persisted upstream header and fork credential must not both reach the fork request. The fork token should temporarily replace the persisted header for the entire fork remote operation, or the push should otherwise use exactly one Authorization source.
Expected
Fork branch is pushed with only the
head-github-apptoken, then the upstream token creates the PR intarget-repo.Related
push_to_pull_request_branch.