Skip to content

feat(base): stateless gitlab.com credential helper, split helpers into 12b (SCRUM-1952) - #99

Open
maxsv0 wants to merge 1 commit into
mainfrom
feat/base_gitlab-credential-helper
Open

feat(base): stateless gitlab.com credential helper, split helpers into 12b (SCRUM-1952)#99
maxsv0 wants to merge 1 commit into
mainfrom
feat/base_gitlab-credential-helper

Conversation

@maxsv0

@maxsv0 maxsv0 commented Aug 20, 2026

Copy link
Copy Markdown
Member

Problem

gitlab.com had no git credential helper at all. glab has been installed unconditionally since 03b (SCRUM-1958), but nothing ever registered a credential source for the host, so every private GitLab clone died with:

fatal: could not read Username for 'https://gitlab.com': terminal prompts disabled

Confirmed live on a customer agent: GITLAB_TOKEN correctly present in ~/.agent-env, POST /api/projects/apply still failing with exactly that error and an empty workspace.

Two independent defects — fixing either alone would not have helped.

1. No gitlab.com helper

Added, shaped like the Bitbucket helper rather than the gh one: it answers git's credential protocol directly instead of shelling out to glab auth git-credential.

Routing through glab would require a stored glab auth login, which:

  • cannot be registered before a token exists — re-opening the provision-order race this is meant to close;
  • goes stale whenever the token rotates;
  • needs --insecure-storage, because glab prefers the OS keyring, which is unreadable from systemd units and cron.

Stateless means a token dropped in at any later time works on the very next git command: no login, no re-auth, no reboot. username=oauth2 is verified against gitlab.com's smart-HTTP endpoint (200 with a valid token, 401 with a bad one).

glab stays exactly what it is good for — glab mr create, which reads GITLAB_TOKEN from the env job preambles already source.

2. The fix could never have reached the fleet

The helpers lived in 12-workspace.sh, which is policy-excluded from refresh-manifest.txt as one-time user/workspace setup. So no credential-helper fix could ever reach an already-provisioned agent.

Split the block into 12b-git-credential-helpers.sh — pure idempotent git config writes, nothing installed — and listed that in the manifest, same carve-out shape as 15b. 12 itself stays excluded.

Also

Hands ~/.gitconfig back to the agent user. git config -f rewrites via temp+rename, so the newly-reachable root-run refresh path would otherwise leave it root-owned and break git config --global for the agent. Provisioning never hit this; the refresh path does.

No credential is ever written to .gitconfig — every helper sources ~/.agent-env at call time, which is also what makes rotation a no-op.

Testing

test-12b-git-credential-helpers.sh (17 assertions) covers wiring, the manifest invariant (12b in / 12 out / no double-write left in 12), no-token-at-rest, and executes the step for real against a sandbox HOME, driven through git credential fill:

  • resolves gitlab.comusername=oauth2 + current token
  • a rotated token is served immediately, with no re-auth
  • an absent token makes the helper decline (never blank credentials)
  • repeated runs stay idempotent (helper count stays at 1)
  • the step survives the refresh contract's set -euo pipefail

Bitbucket + portal-registry helpers re-verified after extraction.

Suite: 22 passed before → 23 after. The same 6 failures are present on untouched origin/main (macOS BSD realpath lacks -m) and are unrelated.

Companion

the-assistant docs/gitlab-agent-credential-lane converges the two manual scripts on the same helper and corrects the docs.

🤖 Generated with Claude Code

…o 12b (SCRUM-1952)

gitlab.com had NO git credential helper. `glab` has been installed
unconditionally since 03b (SCRUM-1958), but nothing ever registered a
credential source for the host, so every private GitLab clone died with:

  fatal: could not read Username for 'https://gitlab.com': terminal
  prompts disabled

Verified live on a customer agent: GITLAB_TOKEN present in ~/.agent-env,
`git clone` still failing with exactly that error.

Two things were broken, and fixing only one would not have helped:

1. No gitlab.com helper. Added, shaped like the Bitbucket one rather than
   the gh one: it answers git's credential protocol directly instead of
   shelling out to `glab auth git-credential`. That avoids a stored `glab
   auth login` — state that goes stale against ~/.agent-env, needs
   --insecure-storage to stay readable from systemd/cron, and cannot be
   registered before a token exists, which re-opens the provision-order
   race this is meant to close. Stateless means a token dropped in at any
   later time works on the very next git command: no login, no re-auth,
   no reboot. `username=oauth2` is verified against gitlab.com's
   smart-HTTP endpoint (200 with a valid token, 401 with a bad one).

2. The helpers lived in 12-workspace.sh, which is policy-excluded from
   refresh-manifest.txt as one-time user/workspace setup. So no helper
   fix could EVER reach an already-provisioned agent. Split the block
   into 12b-git-credential-helpers.sh — pure idempotent `git config`
   writes, nothing installed — and list that in the manifest, same
   carve-out shape as 15b. 12 itself stays excluded.

Also hands ~/.gitconfig back to the agent user: `git config -f` rewrites
via temp+rename, so the newly-reachable root-run refresh path would
otherwise leave it root-owned and break `git config --global`.

No credential is ever written to .gitconfig — every helper sources
~/.agent-env at call time, which is also what makes rotation a no-op.

test-12b-git-credential-helpers.sh covers wiring, the manifest invariant
(12b in / 12 out), no-token-at-rest, and executes the step for real
against a sandbox HOME: resolution, rotation without re-auth, clean
decline when no token is set, and idempotency across repeated runs.

Suite: 22 passed before, 23 after; the same 6 pre-existing macOS
`realpath -m` failures are unchanged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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