Artifact registry: Add the setup snippet content and tool selector

What does this MR do and why?

monolith/S05 Step 10, per the repository detail plan. This change is behind the feature flag :artifact_registry_ui, and is dark: nothing on the page mounts these components yet.

Adds the setup-instruction building blocks that the setup drawer (Step 11) and the repository empty state (Step 12) both render. Splitting the content out from its two surfaces means each of those lands as a rendering concern rather than as a content-authoring MR. Because nothing mounts these yet, the Storybook stories are how this step is reviewed — see the validation steps below.

Changes

  • setup_instructions/snippets.jssetupSnippetSections({ format, tool, section, name, repositoryUrl }) returns a list of sections, each with an optional heading and an ordered list of blocks. A block pairs prose with the snippet that follows it, and may carry prose alone, which is what Step 12's numbered steps need — so the empty state renders this same model as an <ol> rather than authoring a second set of snippets.
  • setup_instructions/tool_selector.vueGlCollapsibleListbox over the format's tool set, labelled by an sr-only span, matching the pattern repositories_toolbar.vue already uses.
  • setup_instructions/setup_snippets.vue — renders the model. Injects slug/clientBaseUrl and calls the existing buildRepositoryClientUrl itself, so URL composition lives in one place rather than in both future consumers.
  • setup_instructions/snippet_code_block.vue — the copyable monospace block (see the first note below).
  • setup_instructions/setup_snippets.stories.js — one story per format and tab, each pairing the selector with the snippets.
  • constants.js — the per-format tool sets and the section identifiers.
  • Jest/VTU specs for all four modules: 132 new tests.

Snippets are composed from the repository's own ADR-009 client URL, never from a placeholder origin, and every snippet that authenticates names a token placeholder rather than carrying a credential. An unknown format or an uncomposable URL yields no sections at all, so a consumer can leave the surface out instead of offering guidance with a hole in it — the same posture repository_actions.vue already takes for its copy-URL item.

Screenshots or screen recordings

Storybook only -- nothing in the app mounts these yet.

Maven, Install tab. Compare against the prototype's setup drawer (kebab -> View setup instructions).

maven-install

The tool selector, and what switching it does. The prototype's selector is inert; this one swaps the snippets. npm offers three tools, and picking yarn changes both the command and the config file it belongs in (.npmrc -> .yarnrc.yml).

Selector open yarn selected
npm-selector-open npm-yarn-selected

Docker at drawer width (~420px), the case that motivated snippet_code_block.vue. Long commands wrap inside the block instead of overflowing the column, and the copy button stays clear of the text.

docker-drawer-width

How to set up and validate locally

  1. yarn storybook:start
  2. Open ee / artifact_registry / repositories / detail / setup_instructions / setup_snippets.
  3. On Maven Install, compare against the prototype's setup drawer (kebab → View setup instructions): prose with inline <code> for pom.xml, grey code blocks with a copy button top right, then the Repository setup heading.
  4. Switch the build tool on any story and confirm the snippets change — e.g. Docker CLI → Podman rewrites both the push and the sign-in command, npm → yarn swaps .npmrc for .yarnrc.yml.
  5. Narrow the browser to roughly drawer width (~420px) on Docker Publish and confirm long commands wrap inside the block instead of overflowing.
  6. Copy any block and confirm the toast appears.

Agent Notes

Verified locally: 529 AR frontend tests pass (132 new), eslint and prettier clean, locale/gitlab.pot regenerated. Storybook checked by hand for Maven install layout against the prototype, tool switching for Docker→Podman and npm→yarn, and wrapping at 420px.

Three deviations from the plan, all deliberate:

  1. snippet_code_block.vue is a new component rather than a reuse of code_instruction.vue, which the plan called for. That component's <pre> cannot be made to wrap from outside: the global pre rule in typography.scss sets word-break but no white-space, so the UA's white-space: pre wins, and there is no overflow-x outside .md. The Maven configuration at a real repository URL then bleeds out of a drawer-width column — reproduced, and fixed by the new block. It also renders its <label> in both branches while the labelled input exists only in the single-line branch, so a labelled multiline block would ship a dangling for. The new component composes the same SimpleCopyButton, so the aria-live announcement and the success toast still come for free.
  2. The snippet builders live in snippets.js, not constants.js. They are functions; only the tool sets and section identifiers are constants, and those did go to constants.js.
  3. No per-format documentation link. The plan assigns it to Step 11, which is fortunate — no Artifact Registry docs page exists to point at yet.

Two places the prototype is deliberately not followed:

  1. The prototype's tool selectors are partly inert. In the empty state renderCliSteps() ignores the selected tool entirely; in the drawer, Maven still shows mvn install when Gradle is selected. This step's acceptance criterion requires switching tools to swap the snippets, so the Gradle Groovy and Gradle Kotlin variants the prototype lacks are authored here, following the wording maven_installation.vue established for the Package Registry.
  2. Tool sets are the merged spec's three per format, not the prototype's abbreviated two.

For design — prototype copy defects found while building this, worth settling before Step 12:

  • The empty state's Docker steps are redundant and out of order relative to their snippets: "Log in to the registry" is step 1, but the login snippet appears as step 3, after the push snippet.
  • Its support text says "package" even where the heading says "images".
  • The drawer's URL shape (/api/v4/organizations/.../artifact_registry/...) disagrees with the empty state's (ar.gitlab.com/<slug>/...) and with ADR-009. This MR follows ADR-009 via buildRepositoryClientUrl.

Judgement calls a reviewer may want to push back on:

  • Yarn's snippets are Berry throughout (.yarnrc.yml with npmScopes, yarn npm publish). Mixing Berry config with Yarn 1 commands would have been incoherent, but if closed beta should target Yarn 1 instead, this is the place to say so.
  • The Docker sign-in snippet uses echo ${GITLAB_TOKEN} | docker login ... --password-stdin rather than the prototype's bare docker login, so the guidance never suggests typing a credential on the command line.
  • Every copy button in a rendered view is named distinctly, since the button shows only an icon and its title is its whole accessible name. That is more strings than the prototype implies.

Known, not addressed here: this will conflict with !248627 (merged) on locale/gitlab.pot — a single two-line hunk where ArtifactRegistry|Docker CLI lands beside ArtifactRegistry|Download. Verified that regenerating with tooling/bin/gettext_extractor locale/gitlab.pot resolves it with all strings from both MRs intact. No other file overlaps; a full merge test against !248627 (merged)'s HEAD produced that one conflict and nothing else.

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist.


This MR was authored with the assistance of an AI coding agent and reviewed by @zcuddy before submission.

Edited by Zack Cuddy

Merge request reports

Loading
Loading