feat: release 2.7.0-rc2

This commit is contained in:
2026-10-06 15:31:51 +00:00 Unverified
parent cd9fb512cd
commit c8de9ddcba
42 changed files with 6127 additions and 593 deletions
+75 -9
View File
@@ -10,6 +10,13 @@ const ACCEPT = [
"application/vnd.docker.distribution.manifest.list.v2+json",
"application/vnd.docker.distribution.manifest.v2+json",
].join(", ");
const MAX_MANIFEST_BYTES = 4 * 1024 * 1024;
const MANIFEST_MEDIA_TYPES = new Set([
"application/vnd.oci.image.index.v1+json",
"application/vnd.oci.image.manifest.v1+json",
"application/vnd.docker.distribution.manifest.list.v2+json",
"application/vnd.docker.distribution.manifest.v2+json",
]);
export type RegistryCredentials = { username: string; password: string };
export type RegistryFetch = (
@@ -103,10 +110,67 @@ function bearerParameters(
}
async function responseError(response: Response): Promise<Error> {
const detail = (await response.text()).slice(0, 512).trim();
return new Error(
`Registry request failed (${response.status})${detail ? `: ${detail}` : ""}`,
);
await response.body?.cancel().catch(() => {});
return new Error(`Registry request failed (${response.status})`);
}
async function readManifestBody(response: Response): Promise<Uint8Array> {
const reader = response.body?.getReader();
if (!reader) throw new Error("Registry returned an invalid manifest");
const chunks: Uint8Array[] = [];
let length = 0;
try {
while (true) {
const { done, value } = await reader.read();
if (done) break;
length += value.byteLength;
if (length > MAX_MANIFEST_BYTES) {
await reader.cancel();
throw new Error("Registry manifest exceeds the size limit");
}
chunks.push(value);
}
} finally {
reader.releaseLock();
}
const body = new Uint8Array(length);
let offset = 0;
for (const chunk of chunks) {
body.set(chunk, offset);
offset += chunk.byteLength;
}
let manifest: unknown;
try {
manifest = JSON.parse(
new TextDecoder("utf-8", { fatal: true }).decode(body),
);
} catch {
throw new Error("Registry returned an invalid manifest");
}
if (
!manifest ||
typeof manifest !== "object" ||
Array.isArray(manifest) ||
(manifest as { schemaVersion?: unknown }).schemaVersion !== 2
)
throw new Error("Registry returned an invalid manifest");
const record = manifest as Record<string, unknown>;
const mediaType = record.mediaType;
if (typeof mediaType !== "string" || !MANIFEST_MEDIA_TYPES.has(mediaType))
throw new Error("Registry returned an invalid manifest");
const isIndex =
mediaType.endsWith("image.index.v1+json") ||
mediaType.endsWith("manifest.list.v2+json");
if (
isIndex
? !Array.isArray(record.manifests)
: !record.config ||
typeof record.config !== "object" ||
Array.isArray(record.config) ||
!Array.isArray(record.layers)
)
throw new Error("Registry returned an invalid manifest");
return body;
}
export async function resolveRegistryDigest(
@@ -191,17 +255,19 @@ export async function resolveRegistryDigest(
}
if (!response.ok) throw await responseError(response);
const body = new Uint8Array(await response.arrayBuffer());
const body = await readManifestBody(response);
const advertised = response.headers
.get("docker-content-digest")
?.trim()
?.toLowerCase();
let digest: Sha256Digest;
const digest =
`sha256:${createHash("sha256").update(body).digest("hex")}` as Sha256Digest;
if (advertised !== undefined) {
assertSha256Digest(advertised);
digest = advertised;
} else {
digest = `sha256:${createHash("sha256").update(body).digest("hex")}`;
if (advertised !== digest)
throw new Error(
"Registry manifest digest does not match Docker-Content-Digest",
);
}
if (cacheTtlMs && cacheMaxEntries) {
digestCache.delete(cacheKey);