Let the install snippet builders take real coordinates
What does this MR do and why?
This is a behaviour-preserving maintenance change to the Artifact Registry Vue SPA. Nothing rendered changes for any user, on any surface, today.
setupSnippetSections builds every install snippet from module placeholders — com.company, app, 1.0.0 for Maven and Gradle, and @scope/package for npm. That is correct for both of its importers. The repository setup drawer (setup_snippets.vue) and the artifacts empty state (artifacts_empty_state.vue) are repository-scoped and have no artifact to name, so a placeholder is the only honest thing either can show.
The upcoming S06 version-detail page needs the same snippets built from a version's own coordinates, so a reader can copy a dependency declaration that resolves rather than one they have to edit first. This MR makes that change and adds one guard: it refuses any coordinate that is not a string built from the characters a real Maven, npm, or semver value can carry, which also catches the null GraphQL sends for an unset field. The guard exists because these builders now interpolate caller-supplied values into a pom.xml, two Gradle DSLs, and a shell command, so the page's MR stays about the page.
Part of the S06 version-detail plan — this is the snippets.js half of Step 14, split out because it has no predecessor and can land ahead of the rest of the slice.
How it works
snippets.js— the install builders take optional coordinates:groupId,artifactId, andversionfor Maven and Gradle,packageNameandversionfor npm. Each defaults to the placeholder that builder already used, so both existing importers pass nothing and render byte-identically.setupSnippetSectionsforwards them and is otherwise unchanged.- Rewriting the placeholders at the call site was the alternative, and it was rejected: it would put snippet formatting outside the module that owns it, and a second builder would duplicate the tool matrix.
- The npm
versiondeliberately has no default, unlike Maven's. npm's placeholder@scope/packageis a bare package name with no version, so defaulting a version would pin@scope/package@1.0.0into the setup drawer and change an already-merged snippet. A version is appended only when the caller supplies one. - The publish section is untouched. It names the repository rather than an artifact, so none of these placeholders reach it.
setupSnippetSectionsrefuses any coordinate that is not a string built from the characters Maven, npm, and semver coordinates use, so anull, numeric, or boolean value is refused the same as a hostile one. A refused coordinate returns no sections — the same result a missing repository URL already produces. The guard runs before the builder is chosen, so it fails closed rather than depending on a per-format table of which template reads what.
Test coverage
The point of the spec changes is the behaviour-preservation proof, in two halves:
snippets_spec.js— a newwhen no artifact coordinates are givenblock asserts the exact placeholder output, byte for byte, for the Maven dependency block, both Gradle DSLs, and all three npm clients. A newwhen a version names its own coordinatesblock asserts the parameterized output for the same set, using the coordinates the design prototype shows (com.company.payment:core:2.4.1and@company/design-system@4.2.0), plus the case where a package name arrives without a version, plus an assertion that a publish section built with coordinates istoStrictEqualone built without.snippets_spec.jsalso carries the guard's specs: one test per unsafe character across all four coordinate parameters, plusnull, numeric, and boolean coordinates, each asserting no sections come back. The rows covering real coordinate shapes — dotted, hyphenated, and underscored, plus prerelease, build-metadata, and SNAPSHOT versions — assert the exact dependency block, so a builder that drops one of them fails.setup_snippets_spec.js— the component half: the drawer still renders the placeholder Maven coordinates and an unpinnednpm install @scope/package.
artifacts_empty_state_spec.js is not touched, though the plan's Files list names it. That importer renders only the publish section, which reaches none of these placeholders, so an assertion there would cover a path this change cannot affect. Its existing specs pass unchanged.
Screenshots or screen recordings
The two importers on master versus this branch, driven in the GDK against the AR local stub (org default, slug acme, repositories maven-releases and npm-internal). Every install snippet still renders the module placeholders, because neither importer passes coordinates.
The three pairs below are byte-identical PNGs — same MD5 on each side, captured from the same viewport at the same element bounds:
| Tool | MD5 (both sides) |
|---|---|
| Maven | 8c8585bd9be1b8bb4a488a20778bd657 |
| Gradle (Groovy) | c489471777ddfc73284858921dfa75d4 |
| npm | 763feedaa9ecec90326783b63c6a39c8 |
Setup drawer — Maven, Install tab
com.company / app / 1.0.0 unchanged.
Before (master) |
After (this branch) |
|---|---|
![]() |
![]() |
Setup drawer — Gradle (Groovy), Install tab
implementation 'com.company:app:1.0.0' unchanged.
Before (master) |
After (this branch) |
|---|---|
![]() |
![]() |
Setup drawer — npm, Install tab
npm install @scope/package — still unpinned. This is the pair that matters most, since it is the one case where a naive default would have leaked @1.0.0 into a merged snippet.
Before (master) |
After (this branch) |
|---|---|
![]() |
![]() |
The second importer is not screenshotted, and here is why
artifacts_empty_state.vue renders only the publish section, which reaches none of the placeholders this MR touches — snippets_spec.js asserts a publish section built with coordinates is toStrictEqual one built without.
It is also not reachable in the GDK today: the backend stub marks empty-maven barren, but get_repository_packages.query.graphql selects packages as an @client field, and mock_resolvers.js seeds artifacts for every repository regardless. The detail page shows 0 Packages in the sidebar beside 16 populated rows. Unrelated to this MR, and worth a separate look at the mock.
Test plan
-
snippets_spec.js— 134 passing (was 116), Vue 2 and Vue 3 -
setup_snippets_spec.js— 12 passing (was 10), Vue 2 and Vue 3 -
artifacts_empty_state_spec.js,setup_drawer_spec.js,tool_selector_spec.js— pass unchanged, both Vue versions -
eslint --max-warnings 0clean on all three changed files - No new i18n strings, so no
gitlab.potchange
ee/spec/frontend/packages_and_registries/artifact_registry/repositories/detail/repository_detail_spec.js currently fails on master (38 failures, findSidebar(...).findComponent is not a function). Verified identical with and without these changes.





