anthropics / anthropics/claude-code-action
Checkout credential cleanup is still a no-op: checkout persists via includeIf.gitdir, not include.path (follow-up to #1510)
- Lenguaje dominante
- TypeScript
- Estrellas
- 8.9k
- Forks
- 2.1k
- Métricas de merge de PR
- Métricas de PR pendientes
Descripción
## Summary
The fix for #1510 walks `include.path` entries of the local git config, but `actions/checkout`
does not use `include.path` in the normal (non-global) code path — it writes `includeIf.gitdir:`
entries. So `replaceCheckoutCredentials` still removes nothing, and the checkout credential
(the workflow `GITHUB_TOKEN`) stays active for every git operation in the job, overriding the
app token the action puts into the remote URL.
Observed with `anthropics/claude-code-action@v1.0.199` and `actions/checkout@v7.0.1`.
## Evidence
Job log of a real tag-mode run (`@claude` on an issue comment, `use_commit_signing: false`):
```
Configuring git authentication for non-signing mode
Configuring git user...
Removing existing git authentication headers...
No existing authentication headers to remove <-- cleanup found nothing
Updating remote URL with authentication...
✓ Updated remote URL with authentication token
```
The agent then committed and pushed successfully — but under `GITHUB_TOKEN`, not under the app
token, which we could confirm from the outside: the push produced **no** `pull_request:
synchronize` event, so neither CI nor our review workflow ran on the commit the agent added to
its own PR. GitHub suppresses events for actions performed with `GITHUB_TOKEN`, and that is only
consistent with the header winning over the credentials embedded in the remote URL.
For contrast, the same repository on `actions/checkout@v5.1.0` (which still wrote the header into
`.git/config`) logged `✓ Removed existing authentication headers`, and pushes from the agent did
raise `synchronize` — CI and review ran on bot-pushed commits. The regression appeared for us
exactly when Dependabot bumped checkout from v5.1.0 to v7.0.1.
## Why the current cleanup misses it
`replaceCheckoutCredentials` (`src/github/operations/git-config.ts`, v1.0.199) unsets
`http./.extraheader` from the local config and then from every file listed in
`git config --local --get-all include.path`.
`actions/checkout` v7.0.1 (`src/git-auth-helper.ts`, `configureToken`) only writes `include.path`
in the `globalConfig` branch. In the branch that runs for a normal checkout it writes, into the
repository-local config:
```ts
const hostIncludeKey = `includeIf.gitdir:${gitDir}.path`
await this.git.config(hostIncludeKey, credentialsConfigPath)
const hostWorktreeIncludeKey = `includeIf.gitdir:${gitDir}/worktrees/*.path`
await this.git.config(hostWorktreeIncludeKey, credentialsConfigPath)
```
plus equivalent container-path variants. None of these are `include.path`, so the loop iterates
over an empty list and the header survives.
## Impact
Two shapes, depending on what the workflow grants `GITHUB_TOKEN`:
1. **`contents: write` granted** — the push succeeds under `GITHUB_TOKEN` and silently raises no
events. Downstream workflows never run on commits the agent adds to an existing PR, while the
PR still shows the checks from the previous SHA, so it looks verified. This is the failure we
hit; it is silent, which makes it worse than a hard error.
2. **`contents: write` not granted** — the push fails with `403 Write access to repository not
granted`, which appears to be what #907 describes.
## Suggested fix
Collect every include target rather than just `include.path`, e.g.
```
git config --local --get-regexp '^include(If\..+)?\.path$'
```
and unset `http./.extraheader` from each referenced file (the same
`git config --file --unset-all` call already used). Optionally also drop the
`includeIf.gitdir:*` keys themselves once their credential file no longer holds the header.
A regression test would need to cover the non-global branch specifically — the current one only
exercises `include.path`, which checkout uses solely for global config.
## Workaround for others hitting this
Give `actions/checkout` a token that is not `GITHUB_TOKEN` (e.g. from
`actions/create-github-app-token`), so whichever credential wins is still one whose actions raise
events. `persist-credentials: false` also avoids the conflict, but it has separate reported
issues (#1236, #1711).
Guía de contribución
Evaluación
Este issue todavía no se ha evaluado.