Hive
fix(cli): authenticate the HTTPS fetch fallback and report every candidate
GitHub issue · Closed
What changed
Two follow-ups to #11490, which added an HTTPS fetch fallback for SSH-declared dependencies:
- The HTTPS fallback now carries a credential. When a fetch candidate is an HTTPS GitHub/GitLab URL and swifterpm can discover a token (
GITHUB_TOKEN/GH_TOKEN/gh, orGITLAB_TOKEN/CI_JOB_TOKEN/glab), it injects-c http.<base>.extraheader=Authorization: …into thegit fetch/ls-remoteinvocation — the same mechanismactions/checkoutuses. Wired into all three git paths:shallowFetchCheckoutand bothls-remoteresolution paths. - The fetch error now lists every attempted candidate and its individual failure, instead of only the last one.
Why
The fallback from #11490 added a route but not a credential. shallowFetchCheckout/ls-remote shelled out to plain git, so the HTTPS candidate only authenticated when an ambient credential happened to be configured (a url.<base>.insteadOf token rewrite, a credential helper, or ~/.netrc). A private dependency declared over SSH therefore kept failing in CI even with a usable GITHUB_TOKEN/GH_TOKEN in the environment, because the authenticated API-tarball path and the git fetch path drew their credentials from different sources. The fallback was weaker than it looked.
This was hard to see because the thrown error only surfaced lastError. For an SSH-declared dependency the candidate order is [ssh, https, ssh.git], so the reported error was always the trailing SSH candidate (Permission denied (publickey)) — masking whether the HTTPS candidate was even attempted and how it failed.
How
-
New
GitTransportAuth.configArguments(for:)returns the-c http.<base>.extraheader=Authorization: …arguments for an HTTPS candidate when a provider token is available. Token kind maps to the credential form git transport accepts:- GitHub →
Basic base64(x-access-token:<token>) - GitLab personal access token →
Basic base64(oauth2:<token>); CI job token →Basic base64(gitlab-ci-token:<token>); OAuth →Bearer <token>
- GitHub →
-
The token is passed with
-c, so it is never written to the on-disk git config. SSH candidates get no arguments (ssh-agent stays in charge), and when no token is available nothing is added, so configured ambient credentials keep working unchanged.GIT_TERMINAL_PROMPT=0still makes a wrong/expired credential fail fast instead of prompting. -
New
GitFetchFailureaggregates the per-candidate errors into a single message, e.g.:could not fetch any candidate location for git@github.com:owner/private-lib:- git@github.com:owner/private-lib: …Permission denied (publickey).- https://github.com/owner/private-lib.git: …HTTP 403 / Authentication failed- git@github.com:owner/private-lib.git: …Permission denied (publickey).
Relation to swift-package-manager
SwiftPM’s GitRepositoryProvider shells out to the system git binary (via GitShellHelper/AsyncProcess), just like swifterpm — it does not use libgit2. It clones the exact resolved URL without deriving SSH/HTTPS alternates, injects only -c http.lowSpeedLimit/http.lowSpeedTime network-stall guards, and delegates all authentication to git’s own credential resolution (credential helpers, url.<base>.insteadOf rewrites, ~/.netrc, ssh-agent) inherited from the environment and global git config.
swifterpm already honours every one of those ambient mechanisms — it inherits the environment and global git config — so it matches SwiftPM whenever git itself can resolve the credential for the remote. The gap this PR closes is the one case SwiftPM punts entirely to user configuration: a provider token that is present in the environment (GH_TOKEN/GITLAB_TOKEN/etc.) but has no corresponding git credential configured. swifterpm discovers that token for its API-tarball path already; this wires the same token into the git HTTPS fetch so the two paths stop drawing credentials from different sources.
Scope / limitation
This widens what authenticates; it cannot grant access. The provided token must actually be able to read the private dependency. In particular, the default GitHub Actions GITHUB_TOKEN cannot reach a different private repository — that requires a PAT, a GitHub App installation token, or an SSH deploy key. With these changes, exposing such a token as GH_TOKEN/GITHUB_TOKEN is sufficient; without one, no code change can help.
Validation
swift build --replace-scm-with-registryonmain: clean.- Full swifterpm suite (112 tests) passes; added unit tests for the GitHub/GitLab argument builders and the SSH no-op case.
- Confirmed real
gitparses the emitted-c http.https://github.com/.extraheader=…form correctly (subsection extracted, header value preserved).
🤖 Generated with Claude Code
Investigate, reproduce, or fix this item in an isolated repository. Each Flight preserves its outcome and agent session.
Start a Flight and preserve its objective, outcome, and session here.
No GitHub comments yet.