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 for POST /api/v4/token_exchange with audience gitlab-artifact-registry. Token lifetime is clamped to 15m–12h. It sits under internal/api rather than beside the commands because both glab artifact-registry and the glab auth docker credential helper consume it, and command packages must not import each other.
  • New per-host config key artifact_registry_domains in internal/config/schema.go, mirroring container_registry_domains.
  • The glab auth docker credential 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 in artifact_registry_domains. findAssociatedHostname was split so both paths share one domain lookup.

Notes for reviewers

  • Command named in full, ar kept as an alias, per @phikai's review feedback. This follows glab container-registry, which is aliased to cr. The directory is internal/commands/artifact_registry/ so it mirrors the command name, as container_registry does. TestNewCmd_NameAndAlias asserts on alias resolution through cobra's Find, not just the Aliases field, so glab ar status staying runnable is covered by a test.
  • Every command carries text.ExperimentalString, so the family is labeled EXPERIMENTAL in --help and in the generated docs under docs/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 --docker cannot leave a partial helper on PATH.
  • loginDocker resolves the Docker config directory through $DOCKER_CONFIG, falling back to ~/.docker. Writing to the wrong directory fails silently: login reports success and docker pull then cannot authenticate. This reads a third-party tool's environment variable to interoperate with it, not a glab setting, so it is deliberately not a KeySchema entry.
  • The artifactregistry tests drive a httptest server rather than gitlabtesting.NewTestClient. token_exchange is not part of client-go's service surface — the client calls it through gl.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:

  1. internal/commands/auth/docker/helper.go and artifact_registry.goHelper.Get now 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.
  2. internal/api/artifactregistry/artifactregistry.go — the token exchange and the duration bounds.
  3. internal/commands/artifact_registry/login/login.go — the flag surface and which writer each flag dispatches to.
  4. login/docker.go, maven.go, gradle.go, npm.go, sbt.go — one writer each, independent of one another. maven.go is the least ordinary of them.
  5. get_token/ and status/ — 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.

Edited by Sylvia Shen

Merge request reports

Loading
Loading