Add the artifact registry repositories list route and page shell
What does this MR do and why?
Makes the Artifact Registry repositories list reachable. Registers it as the
root route of the repositories SPA, wires the SPA breadcrumb over the shared
spa_breadcrumbs.vue, provides the organization global ID and the slug from the
mount dataset, and adds the page component with its heading and its empty state.
The step issues no query, so the empty state is what the route renders. Every other result state, and every behavior that reacts to a query or a route-query change, waits for the step that supplies its trigger: the page is a route component and the route declares no props, so a state driven by an input would be reachable only from a spec or a story. The loading, populated, error, and no-permission states, the result-state live region, and the keyset cursor reset all arrive with the table, the pager, and the query in the next step.
The breadcrumb and the document title
The page's HAML now titles the view s_('ArtifactRegistry|Repositories') rather
than _('Artifact registry'). The server-rendered trail is the organization plus
the view's title, so this is what makes the final crumb name the list instead of
naming the area and linking to the list itself. The list route correspondingly
carries no meta.text: a route text would repeat that name in the trail, and the
router's afterEach hook would prepend it to a document title that already opens
with it.
With the server trail complete, the slice needs no breadcrumbs component of its
own, so repositories/index.js passes the shared
vue_shared/spa/components/spa_breadcrumbs.vue straight to
injectVueAppBreadcrumbs, the way initSinglePageApplication already does. The
collapsed section below records the Vue 3 prop leak that a local wrapper
reintroduces, which is why this MR carries a second commit.
One thing a reviewer may reasonably question, and it is intentional:
repositories/index.jsprovidesorganizationGidandslug, which nothing injects yet. The table and the toolbar steps consume them; providing them with the bootstrap keeps those steps free of a Rails-side edge.
The slice stays dark behind artifact_registry_ui, whose gated controller
renders a not-found for the whole request while the flag is off, so it carries no
changelog entry.
References
- Epic: Repositories list (monolith/S04)
- Feature flag:
artifact_registry_ui(ee/config/feature_flags/wip/artifact_registry_ui.yml), disabled by default
Screenshots or screen recordings
| Before | After |
|---|---|
![]() |
see !247731 (comment 3621537417) |
Why the breadcrumb wiring changed: a Vue 3 prop leak (resolved)
Resolved in 5155f5c4, which deletes the wrapper the leak depended on. Kept here
because it is the rationale for titling the view and dropping the route text.
Symptom. The Artifact Registry repositories breadcrumb renders differently for different developers on the same commit (f948d085). Author sees Default / Artifact registry / Repositories; reviewer sees Default / Repositories. Both are local GDKs on the same branch, and no review app exists for the MR, so it isn't an environment or data difference in the usual sense.
Cause. A prop leak that only manifests under Vue 3.
injectVueAppBreadcrumbs passes two props (app/assets/javascripts/lib/utils/breadcrumbs.js:70-72):
allStaticBreadcrumbs: items.slice(), // full server trail
staticBreadcrumbs: items.slice(0, -1), // trail minus its last itemThe server trail for this page is 2 items, [Default, Artifact registry], built from push_organization_breadcrumbs (app/helpers/organizations/organization_helper.rb:89) plus @breadcrumb_title set by page_title _('Artifact registry') in the page's HAML.
ee/app/assets/javascripts/packages_and_registries/artifact_registry/components/breadcrumbs.vue declares only staticBreadcrumbs and binds it to :all-static-breadcrumbs, deliberately trimming the last crumb so the router supplies it. But allStaticBreadcrumbs is left undeclared, so it becomes a fallthrough attr, and the component's single root is , which does declare that exact prop (spa_breadcrumbs.vue:10-13).
- Vue 2: the stray attr never reaches the child's props. The explicit binding stands. 2 crumbs.
- Vue 3: the fallthrough attr merges onto the root vnode and resolves as that prop, overriding the explicit binding. The untrimmed trail wins. 3 crumbs.
Why it split. The author's GDK sets vite.vue_version: 3 in gdk.yml:22-24 (confirmed: VUE_VERSION=3 in the running Vite process, and [V] Using Vue.js 3 (compiler 2) in the Vite log). vite.config.js:33 defaults to '2', so the reviewer was on Vue 2. Net effect: the breadcrumb output depends on a per-developer GDK setting.
How to set up and validate locally
-
Enable the flag for an organization in the Rails console:
Feature.enable(:ui_for_organizations) Feature.enable(:artifact_registry_ui, Organizations::Organization.first) -
Visit the repositories area at the SPA base, for example
http://gdk.test:3000/o/default/-/artifact_registry/acme/repositories. -
Confirm the breadcrumb trail reads Default / Repositories, that the document title is
Repositories · Default · GitLabwith no repeated segment, and that the page renders the "Repositories" heading and the empty state with no errors in the browser console. -
Visit an unregistered path under the same base, for example
.../repositories/does-not-exist, and confirm the in-app not-found fallback still renders. -
Run the area's Jest suite:
yarn jest ee/spec/frontend/packages_and_registries/artifact_registry
MR acceptance checklist
Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.
Related to #602638 (closed)

