Add the manifests table to the artifact version list

What does this MR do and why?

Note

Targets !250074 (merged), which in turn targets !249937 (merged). Both merge first.

This change is behind the feature flag :artifact_registry_ui

The previous step added a versions table to the artifact version list page for Maven and npm, leaving Docker and OCI rendering a header and nothing else. A container's versions are its manifests, a different contract resource read from a different endpoint. This fills that gap, so all four formats render content, with a manifests table of three columns: Digest, Size, and Published.

Design column What happened
Digest Ships — 12 characters, no sha256: prefix, as the design renders it
Size Ships
Published Ships
Type Next step, once media_type, artifact_type, and subject_digest are read
Tags Phase 2
Referrers A later step ships the referrer preference; the count column is not in the contract
Row menu A later step ships View pull command and Copy digest
Platforms Not in the contract — a manifest carries no platform triple
Downloads Not in the contract — no download count
Source Not in the contract for a manifest, and descoped for a version — Source is out of the beta

A version does carry created_by, project_id, and git_commit_sha, so a Source column was once planned for the versions table. That has since been descoped: Source is out of the beta, so neither table gets one.

Worth noting on Size: the contract states it is the whole manifest tree rooted at that manifest, computed at push time, and warns it can double-count a blob shared with another manifest. It is rendered per row and never totalled.

Changes

  • New manifests query. graphql/queries/get_artifact_manifests.query.graphql, the counterpart of the versions document, repeating artifactId on the @client parent for the same variable-pruning reason, and added to graphql/queries_spec.js's guard table.

  • New types. ArtifactRegistryManifest (id, digest, size, createdAt) and ArtifactRegistryManifestConnection, plus a manifests field on ArtifactRegistryImage. Declared once, unlike the previous step's versions, because an image is a plain type rather than a union. size is typed String! because the contract types it int64 and GraphQL's BigInt scalar serializes as a string, which is the shape humanSize already expects.

  • Mock resolver. A manifests resolver on the image type, reusing the existing delay, paginate, and artifactKey helpers. The previous step's versionsNewestFirst is renamed newestFirst and shared, since Artifact Registry lists both resources sort=created_at&order=desc by default.

  • Seed data. A manifest ladder on each seeded image, held under storedManifests rather than manifests, or Apollo falls back to writing the bare array where a connection belongs. Digests are generated as 64 hex characters so they satisfy the contract's ^sha256:[0-9a-f]{64}$ pattern; the design proposal's mock digests are not hex. Manifest ids come from a third sequence of their own, so adding them cannot renumber the artifact ids the repository table links to.

  • New table. repositories/versions/manifests_table.vue — the shortened digest as monospace text beside a ClipboardButton carrying the full canonical digest, size through the existing humanSize, published through TimeAgoTooltip.

  • A new shortDigest helper, because nothing existing fits. The monolith already shortens digests in four places, and they disagree:

    Site Characters kept
    Container registry 7
    Harbor 7
    Dependency proxy 7
    Google Artifact Registry 12

    truncateSha in ~/lib/utils/text_utility.js cannot serve: it takes the first 8 characters of whatever it is handed and does not strip an algorithm prefix, so it returns 'sha256:'. The new helper follows Google Artifact Registry's 12 characters, which is also what the design renders, and splits on the : rather than assuming an offset of 7, so a digest carrying some other algorithm still reads as a digest.

  • Format switch. versions_section.vue takes a format and renders the versions or the manifests table, the way the merged detail/artifacts_section.vue takes a format and hands it to one table. Its row prop is now format-neutral.

  • One connection query, not two. The previous step gave the page a versions query skipped for containers. A page is either versions or manifests and never both, which is the shape the merged repository_detail.vue already solves with one query whose query() returns the images or the packages document by format. That block is generalized the same way rather than duplicated: one document switch, one error flag, one loading computed, one skip(). Every populated page now reads a connection, so the terminal announcement is always the version-list one, and the now-dead "Artifact details for %{name} loaded." string is removed.

  • Specs. The new table, the section's format switch, the switching query across all four formats, the resolver's ordering and digest form, the new helper, and a seed guard that every manifest digest is distinct.

Screenshots or screen recordings

docker manifests

How to set up and validate locally

  1. Enable the flag: Feature.enable(:artifact_registry_ui).
  2. Visit /o/<organization>/-/artifact_registry/acme/repositories.
  3. Open payment-service (Docker), then oci-artifacts (OCI), and click an artifact name in each. Confirm the manifest table renders newest-first with a 12-character digest, a human-readable size, and a relative publication time.
  4. Click a copy button and confirm the clipboard holds the full sha256: digest rather than the shortened form.
  5. Open payment-core (Maven) and ui-components (npm) and confirm their versions table still renders unchanged — that is the regression risk of generalizing the previous step's query.

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.

Edited by Zack Cuddy

Merge request reports

Loading
Loading