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.js provides organizationGid and slug, 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

Screenshots or screen recordings

Before After
Screenshot_2026-07-13_at_11.16.38_pm 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.

ar-s04-step3-repositories-empty-state

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 item

The 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

  1. Enable the flag for an organization in the Rails console:

    Feature.enable(:ui_for_organizations)
    Feature.enable(:artifact_registry_ui, Organizations::Organization.first)
  2. Visit the repositories area at the SPA base, for example http://gdk.test:3000/o/default/-/artifact_registry/acme/repositories.

  3. Confirm the breadcrumb trail reads Default / Repositories, that the document title is Repositories · Default · GitLab with no repeated segment, and that the page renders the "Repositories" heading and the empty state with no errors in the browser console.

  4. Visit an unregistered path under the same base, for example .../repositories/does-not-exist, and confirm the in-app not-found fallback still renders.

  5. 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)

Edited by Rahul Chanila

Merge request reports

Loading
Loading