mirror of
https://github.com/quay/quay.git
synced 2026-01-27 18:42:52 +03:00
When viewing a child manifest of a multi-architecture image, the Layers tab was showing "No layers found" because manifestData was fetched for the parent manifest list digest (which has no layers) but never re-fetched when the user selected a specific architecture. Fix: Make the Layers component fetch its own manifest data based on the digest prop using a new useManifestByDigest hook. This ensures layers are fetched for the correct child manifest digest. Changes: - Add useManifestByDigest hook using React Query - Update Layers component to fetch manifest data internally - Remove manifestData prop from Layers in TagDetailsTabs - Add Playwright e2e tests for multi-arch manifest layers 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
121 lines
3.2 KiB
TypeScript
121 lines
3.2 KiB
TypeScript
/**
|
|
* Container utilities for Playwright e2e tests
|
|
*
|
|
* These utilities provide container operations for pushing test images
|
|
* to the registry during e2e tests. Supports both podman and docker.
|
|
*/
|
|
|
|
import {exec} from 'child_process';
|
|
import {promisify} from 'util';
|
|
import {API_URL} from './config';
|
|
|
|
const execAsync = promisify(exec);
|
|
|
|
// Extract registry host from API_URL (e.g., 'localhost:8080')
|
|
const REGISTRY_HOST = new URL(API_URL).host;
|
|
|
|
// Cache the detected container runtime
|
|
let containerRuntime: string | null = null;
|
|
|
|
/**
|
|
* Detect available container runtime (podman or docker)
|
|
*
|
|
* @returns 'podman', 'docker', or null if neither is available
|
|
*/
|
|
async function detectContainerRuntime(): Promise<string | null> {
|
|
if (containerRuntime !== null) {
|
|
return containerRuntime;
|
|
}
|
|
|
|
// Prefer podman, fall back to docker
|
|
for (const runtime of ['podman', 'docker']) {
|
|
try {
|
|
await execAsync(`${runtime} --version`);
|
|
containerRuntime = runtime;
|
|
return runtime;
|
|
} catch {
|
|
// Try next runtime
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Push a test image to the registry using podman or docker
|
|
*
|
|
* Uses busybox as a minimal test image
|
|
*
|
|
* @example
|
|
* ```typescript
|
|
* await pushImage('myorg', 'myrepo', 'latest', 'testuser', 'password');
|
|
* ```
|
|
*/
|
|
export async function pushImage(
|
|
namespace: string,
|
|
repo: string,
|
|
tag: string,
|
|
username: string,
|
|
password: string,
|
|
): Promise<void> {
|
|
const runtime = await detectContainerRuntime();
|
|
if (!runtime) {
|
|
throw new Error('No container runtime available (podman or docker)');
|
|
}
|
|
|
|
const image = `${REGISTRY_HOST}/${namespace}/${repo}:${tag}`;
|
|
const tlsFlag = runtime === 'podman' ? '--tls-verify=false' : '';
|
|
|
|
// Login to registry
|
|
await execAsync(
|
|
`${runtime} login ${REGISTRY_HOST} -u ${username} -p ${password} ${tlsFlag}`.trim(),
|
|
);
|
|
|
|
const busyboxImage = 'quay.io/prometheus/busybox:latest';
|
|
|
|
// Pull busybox and tag it
|
|
await execAsync(`${runtime} pull ${busyboxImage}`);
|
|
await execAsync(`${runtime} tag ${busyboxImage} ${image}`);
|
|
|
|
// Push to registry
|
|
await execAsync(`${runtime} push ${image} ${tlsFlag}`.trim());
|
|
|
|
// Cleanup local image
|
|
await execAsync(`${runtime} rmi ${image}`);
|
|
}
|
|
|
|
/**
|
|
* Check if a container runtime (podman or docker) is available
|
|
*
|
|
* @returns true if podman or docker is available
|
|
*/
|
|
export async function isContainerRuntimeAvailable(): Promise<boolean> {
|
|
return (await detectContainerRuntime()) !== null;
|
|
}
|
|
|
|
/**
|
|
* Push a multi-architecture manifest list to the registry.
|
|
*
|
|
* Uses quay.io/prometheus/busybox:latest as the source image.
|
|
*
|
|
* @example
|
|
* ```typescript
|
|
* await pushMultiArchImage('myorg', 'myrepo', 'manifestlist', 'testuser', 'password');
|
|
* ```
|
|
*/
|
|
export async function pushMultiArchImage(
|
|
namespace: string,
|
|
repo: string,
|
|
tag: string,
|
|
username: string,
|
|
password: string,
|
|
): Promise<void> {
|
|
const targetImage = `${REGISTRY_HOST}/${namespace}/${repo}:${tag}`;
|
|
const sourceImage = 'quay.io/prometheus/busybox:latest';
|
|
|
|
// Use skopeo to copy the entire multi-arch manifest list in one command
|
|
// --all flag copies all architectures and the manifest list
|
|
await execAsync(
|
|
`skopeo copy --all docker://${sourceImage} docker://${targetImage} --dest-tls-verify=false --dest-creds=${username}:${password}`,
|
|
);
|
|
}
|