Artifact registry: Add Edit and Copy URL to the detail header

What does this MR do and why?

Finishes the repository detail header for monolith/S05: it adds the Edit button and the Copy repository URL kebab item, plus the whole copy-URL seam behind that item.

This change is behind the feature flag :artifact_registry_ui (dark, default_enabled: false).

The header region landed with its actions area holding the kebab alone, because the monolith/S07 edit route did not exist at the time and the delete flow only contributed its own item. Both write flows are merged now, so the two merge-order conditionals in the S05 plan resolve and the header can mount what the design shows.

Copy repository URL selects no repository field at all. The URL a user points docker, mvn, or npm at is composed client-side, because Artifact Registry returns no URL on the repository resource — so this MR also lands the seam that composition needs, which is the bulk of the diff.

Changes

  • Edit button in the header actions, ahead of the kebab, routing to the merged S07 edit route (REPOSITORY_EDIT_ROUTE_NAME).
  • Copy repository URL as the first kebab item, above Delete repository. The destructive item is marked out by its variant alone — the design groups every item in one list with no divider.
  • The copy-URL seam:
    • Organizations::ArtifactRegistryHelper adds client_base_url to the repositories mount data, derived as the origin of the already-configured Gitlab.config.artifact_registry.api_url via Gitlab::UrlHelpers.normalized_base_url. No new setting and no gitlab.yml.example change — per ADR-009 the management and client APIs share a domain and differ only by the management API's /api/ prefix.
    • clientBaseUrl joins the SPA provide block.
    • REPOSITORY_FORMAT_PATH_SEGMENTS in constants.js and buildRepositoryClientUrl in utils.js.
  • Copy goes through copyToClipboard from ~/lib/utils/copy_to_clipboard (navigator.clipboard is banned by eslint.config.mjs), reports through a toast, and sends a failed write to Sentry.
  • The edit form's Cancel now returns to the repository being edited, not to the repositories list. An edit is reached from its repository now that the header carries the Edit button. A create has no repository to return to, so the destination is per-mode: repository_form.vue takes a cancelRoute prop alongside the submit label and field set it already takes that way, defaulting to the list.
  • Tests: a new helper spec (the helper had none), the repositories controller request spec updated for the new mount key, and Jest coverage for the builder, the kebab item, the Edit button, and both cancel destinations.

The URL being copied

{origin}/{slug}/{format-segment}/{name}, with the container family collapsing to one segment because a single set of OCI Distribution Spec endpoints serves repositories of both docker and oci:

format segment example
MAVEN maven https://artifact-registry.example.com/acme/maven/my-repo
NPM npm https://artifact-registry.example.com/acme/npm/my-repo
DOCKER container https://artifact-registry.example.com/acme/container/my-repo
OCI container https://artifact-registry.example.com/acme/container/my-repo

An instance that configures no Artifact Registry composes no URL, so the item is left out rather than offering one that would not resolve.

Screenshots or screen recordings

Before After
before-kebab after-kebab

The design being matched, for reference — the prototype at the same viewport. Manage retention (monolith/S08), Manage access (monolith/S09), and View setup instructions (a later monolith/S05 step) stay out of scope; this MR lands the first item and the divider above the last.

prototype-kebab

How to set up and validate locally

  1. Enable the flag: Feature.enable(:artifact_registry_ui) in rails c.
  2. Visit /o/<organization>/-/artifact_registry/acme/repositoriesacme is Organizations::ArtifactRegistry::STUB_SLUG, and the controller renders 404 for any other slug.
  3. The local Apollo resolvers seed an empty store, so create a repository first through New repository → Hosted repository. Pick Docker to exercise the shared container segment.
  4. Back on the list, click the repository name. Note this has to be an in-SPA click — the mock store is in-memory, so a full page load empties it.
  5. Confirm the header renders Edit and the kebab, and that Edit navigates to .../<name>/edit with the form prefilled.
  6. Open the kebab and choose Copy repository URL. Paste it: on a GDK the origin is http://localhost:8080, so a Docker repository my-repo gives http://localhost:8080/acme/container/my-repo.
  7. On the edit view, Cancel returns to the repository detail view. Cancel on the create view still returns to the list.

Verification performed

Copy repository URL composes from the configured Artifact Registry origin, so verifying it locally means setting api_url in config/gitlab.yml (the key ships commented out in gitlab.yml.example) and restarting Rails. Without it the helper emits no clientBaseUrl and the kebab item is left out by design:

artifact_registry:
  api_url: https://artifact-registry.example.com
  • yarn jest ee/spec/frontend/packages_and_registries/artifact_registry — 19 suites, 295 examples, green.
  • bin/rspec ee/spec/helpers/organizations/artifact_registry_helper_spec.rb (12 examples) and ee/spec/requests/organizations/artifact_registry_repositories_controller_spec.rb (13 examples) — green.
  • ESLint, Prettier, and RuboCop clean on the touched files.
  • Driven in a browser against a GDK: the composed URL, the toast (aria-live polite), the Edit navigation, and zero console errors.

Notes for review

  • The client_base_url derivation. api_url has no production default and normalized_base_url returns something like :// for a partial value, so the helper emits nil unless the result is a well-formed http(s) origin. It also reads Gitlab.config.artifact_registry['api_url'] through [] rather than .api_url, because a missing key raises on the reader and this renders on every repositories page.
  • Hiding vs. disabling the Copy item when no origin is configured is my call, not something the design fixes. Say the word if you would rather it render disabled.
  • Save changes still returns to the repositories list, so cancelling an edit now lands somewhere different from saving one. The feedback asked about cancel specifically, so I have not touched save — but it is a one-line change in repositories_edit_form.vue if you want the two to agree.
  • The monolith/S04 list plan's copy-URL step plans the same seam. It has not merged, so this MR lands it and those entries become Modify.

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.

References

Edited by Zack Cuddy

Merge request reports

Loading
Loading