1
0
mirror of https://github.com/quay/quay.git synced 2026-01-27 18:42:52 +03:00
Files
quay/web/playwright/utils/container.ts
Shaon H 8485956048 fix(web): display layers for multi-arch manifests (PROJQUAY-10020) (#4761)
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>
2026-01-05 09:42:32 -08:00

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}`,
);
}