Draft: feat(artifact-registry): add the glab artifact-registry command family
Adds an experimental glab artifact-registry command family that exchanges a
GitLab credential for a short-lived GitLab Artifact Registry access token, and
hands that token to Docker, Maven, Gradle, npm, or sbt. ar is kept as an
alias.
Related to gitlab-org/ops/artifact-registry#222 (closed)
Commands
| Command | Purpose |
|---|---|
glab artifact-registry get-token |
Prints the bare token on stdout so a shell can capture it, for example to feed docker login. Flags: --duration (default 15m), --hostname, --output json. |
glab artifact-registry status |
Prints the issuer, subject, and expiry from the exchanged token's claims. |
glab artifact-registry login |
Writes credentials for one of --docker, --maven, --gradle, --npm, --sbt against --registry. Flags: --duration (default 1h, ignored for --docker), --registry-alias, --hostname. |
Supporting changes
internal/api/artifactregistry— client forPOST /api/v4/token_exchangewith audiencegitlab-artifact-registry. Token lifetime is clamped to 15m–12h. It sits underinternal/apirather than beside the commands because bothglab artifact-registryand theglab auth dockercredential helper consume it, and command packages must not import each other.- New per-host config key
artifact_registry_domainsininternal/config/schema.go, mirroringcontainer_registry_domains. - The
glab auth dockercredential helper now tries the Artifact Registry token exchange first and falls back to the existing container registry behavior when the requested registry is not listed inartifact_registry_domains.findAssociatedHostnamewas split so both paths share one domain lookup.
Notes for reviewers
- Command named in full,
arkept as an alias, per @phikai's review feedback. This followsglab container-registry, which is aliased tocr. The directory isinternal/commands/artifact_registry/so it mirrors the command name, ascontainer_registrydoes.TestNewCmd_NameAndAliasasserts on alias resolution through cobra'sFind, not just theAliasesfield, soglab ar statusstaying runnable is covered by a test. - Every command carries
text.ExperimentalString, so the family is labeled EXPERIMENTAL in--helpand in the generated docs underdocs/source/artifact-registry/. - Token claims are decoded unverified (
ParseUnverified). The trust boundary is the TLS connection to the GitLab host that issued the token, not the token's signature. - The Docker credential-helper shim is written with a temp file and rename, so
an interrupted
glab artifact-registry login --dockercannot leave a partial helper onPATH. loginDockerresolves the Docker config directory through$DOCKER_CONFIG, falling back to~/.docker. Writing to the wrong directory fails silently: login reports success anddocker pullthen cannot authenticate. This reads a third-party tool's environment variable to interoperate with it, not a glab setting, so it is deliberately not aKeySchemaentry.- The
artifactregistrytests drive ahttptestserver rather thangitlabtesting.NewTestClient.token_exchangeis not part of client-go's service surface — the client calls it throughgl.NewRequest/gl.Do— so there is no generated mock to use for it.
Reviewing this
The diff is 3082 insertions against 17 deletions, but 1729 of those lines are
tests and 220 are make gen-docs output, which leaves about 1100 lines of
production code. Almost none of it replaces anything: the only place existing
behavior changes is the Docker credential helper.
A reading order that front-loads the parts worth the most attention:
internal/commands/auth/docker/helper.goandartifact_registry.go—Helper.Getnow tries the Artifact Registry path first and falls through to the pre-existing container registry path. This is the whole regression surface; everything else is a new, unreferenced subtree.internal/api/artifactregistry/artifactregistry.go— the token exchange and the duration bounds.internal/commands/artifact_registry/login/login.go— the flag surface and which writer each flag dispatches to.login/docker.go,maven.go,gradle.go,npm.go,sbt.go— one writer each, independent of one another.maven.gois the least ordinary of them.get_token/andstatus/— thin wrappers over the client in step 2.
Happy to split this along the commit boundaries if you'd rather review it in pieces — roughly the token-exchange client plus the two read-only commands, the credential helper integration, and the writers. Say the word and I'll do it.