Draft: Artifact Registry: Add the repository edit flow and a shared write form
What does this MR do and why?
This change is behind the feature flag :artifact_registry_ui
Implements monolith/S07 Step 7 — the Vue edit flow. A hosted repository can now be edited from the repositories list. Like the create flow, it runs against Apollo local state rather than a real API, so the write flows are exercisable ahead of the backend.
Targets 602638-repositories-list-step-4 (!248084 (merged)), because the edit entry point lives on that list's rows. Retarget to master once that merges.
Changes
A shared, action-agnostic write form. repositories_create_form.vue was simultaneously the route view, the form, and the owner of the mutation. repositories/components/repository_form.vue now holds the fields, validation, errors alert, and submit/cancel row — and owns no Apollo, no toast, no cache update, and no routing. Create and edit each wrap it and drive the action.
The two flows differ by capability rather than by mode: nameReadOnly renders the immutable name without offering to change it, and showFormat omits the field edit must not show. There is deliberately no mode: 'create' | 'edit' prop — the form never learns which action it is serving, which is what keeps the two experiences identical by construction.
The edit view. A full page at :name/edit, not a modal. It derives its states from the prefill exactly as the list does: skeleton while resolving, the not-found component alone when the repository does not resolve, and the service-unavailable alert with the heading intact when the read fails. The form is not rendered until the prefill lands, because it seeds its own state from the repository once, at creation. The route param is the identity throughout, so nothing can drift between what was read and what is written.
A per-row actions menu. Nothing in the app linked to a write flow before this, so the row_actions_menu.vue that monolith/S07 owns arrives with the edit flow, hosted in a new actions column. Its Edit item carries a route location rather than a click handler, so it renders as a real link and opens in a new tab. The toggle's accessible name carries the repository name, because the kebab repeats once per row. Delete is monolith/S07 Step 8 and drops in beside it with no restructuring.
Local-state GraphQL. A single-repository read and an update mutation, plus their typedefs and mock resolvers. The update input carries slug and name as identity and visibility/description as the only writable fields — name and format are immutable at Artifact Registry (ADR-009, ADR-022), so neither is writable and format is not in the input at all.
No cache update callback. The mutation payload selects name, which cache_config.js keys the type on, so Apollo normalizes the result onto the entry the list already holds and patches it in place. Evicting would drop a valid page to no purpose.
Review feedback from !247972 (merged), four of five folded in:
| Suggestion | Status |
|---|---|
Use errors-alert instead of the hand-rolled alert |
ErrorsAlert above the fields. createAlert is left for top-level failures the form cannot act on |
REPOSITORY_NEW_TITLE → "New hosted repository" |
|
| The page heading needs a description | |
| The format field should have icons | GlFormSelect → GlCollapsibleListbox rendering each format's logo. A native select cannot render images |
| Drop the single-option visibility radio |
Screenshots or screen recordings
| Create (format listbox) | List (row actions) | Edit |
|---|---|---|
![]() |
![]() |
![]() |
The create heading and its description are new, as is the format listbox with its logos. On the list, the Actions column and its kebab are new. The edit page is entirely new: the name is read-only and says why, and the format field is absent because it cannot be changed.
How to set up and validate locally
- Enable the flag:
echo "Feature.enable(:artifact_registry_ui)" \| bundle exec rails c - Go to
/o/<your-org>/-/artifact_registry/acme/repositories/new/hosted. - Open the Format listbox — each format shows its ecosystem logo. Pick one, name the repository, add a description, and Create repository. You land on the list with a toast, and the row now carries an Actions kebab.
- Open the kebab → Edit. The form prefills; Name is read-only and explains why; Format is not shown; Save changes replaces "Create repository".
- Change the description → Save changes → toast → back on the list.
- Open the kebab → Edit again. The new description is prefilled. This is the cache-patch proof — see the caveat below.
- Try
:name/editfor a name that does not exist: the in-SPA not-found state renders, and no heading (so the view never confirms the namespace exists).
description and visibility has a single value. The toast and re-opening the form are the confirmation. And a full page reload wipes the mock store, because it is a module-level array — so a deep link to :name/edit after a reload always renders not-found. Keep step 3 onwards in one page session.
Agent Notes
Implemented by an AI coding agent, reviewed by @zcuddy before submission.
Please read before reviewing:
-
Four new Vue 3 spec failures, sharing one root cause with this branch's existing four. The format-logo assertions in
repository_form_spec.jsfail underVUE_VERSION=3becausejest.config.base.js's transform pattern^.+\.(svg\|gif\|png\|mp4)$does not matchdocker.svg?url. That is the same reasonrepositories_table_spec.js's logo assertions are red on the target branch, and the same fix (already proposed in !248093 (closed)) clears all eight. Deliberately not carried here, to avoid a conflicting edit to a shared config file. Vue 2 is 192/192 green. @rchanila — worth landing that config fix on the target branch first. -
The listbox inherits the icon problem already raised on the target branch. The logos are the wide logo-plus-text SVGs, not logo-only. Because both surfaces read
REPOSITORY_FORMAT_LOGOS, swapping to logo-only assets fixes the table and this listbox at once — no extra work needed here. -
create_button.vueis not in scope./new/hostedis still URL-only. It belongs to the S04 toolbar, which has not shipped. Flagged, not built. -
The no-
update-callback decision was verified, not assumed. An@client-only mutation does normalize its payload onto the keyed entity — checked directly against the cache rather than inferred from the docs. -
A spec detail worth a look: the edit spec backs its resolvers with a small store rather than fixed values. This matters — the read is an
@clientfield, so Apollo re-runs it on every cache broadcast, and a resolver that always answered the pre-edit repository would silently undo the patch the spec is asserting.
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.


