Skip to content

fix(setup-stack): authenticate the sparse-workspace heal with the job token - #97

Merged
azlekov merged 1 commit into
mainfrom
fix/setup-stack-sparse-heal-auth
Sep 29, 2026
Merged

azlekov merged 1 commit into
mainfrom
fix/setup-stack-sparse-heal-auth

Conversation

@azlekov

@azlekov azlekov commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

Problem

The sparse-workspace heal added in #94 fails on private repos. A self-hosted workspace left sparse by an earlier job is also a blob-less partial clone. So git sparse-checkout disable lazily fetches every blob outside the old sparse set from the promisor remote. Checkouts no longer persist credentials, so that fetch has none:

::notice::healing a sparse workspace left by an earlier job
fatal: could not read Username for 'https://github.com': No such device or address
fatal: could not fetch <sha> from promisor remote
Error: Process completed with exit code 128.

The job fails in setup-stack, before install or any phase runs, on whichever runner has such a workspace.

Fix

  • The job token (github.token, which already has contents: read on the checked-out repo) reaches only the sparse-checkout disable call, as an http.<server>/.extraheader passed through GIT_CONFIG_COUNT/KEY/VALUE env. It is never written to .git/config, so the "no persisted credentials" rule still holds, and it never appears in argv, where another job on the same machine could see it with ps.
  • The encoded value is masked, and it is built with base64 | tr -d '\n' because BSD base64 on a macOS runner has no -w0.
  • A heal that still fails now prints an error saying what to do, instead of a bare exit 128.
  • AGENTS.md's sparse-checkout row records why the heal needs the token.

Verification

Reproduced locally against a private repo over HTTPS: a --filter=blob:none clone with a non-cone sparse checkout and core.sparseCheckout=true in .git/config (the layout git 2.43 leaves), no stored credentials, and GIT_TERMINAL_PROMPT=0. The step's script was taken straight from action.yml with yq.

exit files on disk token in .git/config / log
old step 128, same error as CI 10 (still sparse) –
new step 0 all tracked files none / none
new step, run again 0 (no-op) unchanged none

zizmor (--min-severity informational), shellcheck on the step script, typos, and actions/setup-stack/test.sh (24/24) all pass.

Not changed

Newer git (2.55 in my test) can write core.sparseCheckout to config.worktree, where the --local probe does not look. That only matters once a self-hosted runner upgrades its git, and it deserves its own change.

🤖 Generated with Claude Code

… token

A sparse workspace left on a self-hosted runner is also a blob-less partial clone,
so `git sparse-checkout disable` lazily fetches the missing blobs. Checkouts no
longer persist credentials, so on a private repo that fetch failed with "could not
read Username" and exit 128, failing the job before any phase ran.

The job token now reaches that one git command through GIT_CONFIG_* env, never
.git/config or argv, and a heal that still fails says what to do.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@azlekov
azlekov merged commit dbb6185 into main Sep 29, 2026
1 check passed
@azlekov
azlekov deleted the fix/setup-stack-sparse-heal-auth branch September 29, 2026 13:24
@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 1.16.2 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant