feat(catalog): verify deterministic release manifests before Git export
CI / check (push) Failing after 22s
CI / deploy (push) Skipped
CI / publish-container (push) Skipped

This commit is contained in:
Simo committed 2026-09-13 17:48:23 +02:00
1 parent 422be3ce2d
commit a320e47c29
9 files changed
+491 -9

No files matched your search

+39 -2
View File
@@ -4,6 +4,16 @@ import { promises as fs } from "node:fs";
import { hostname } from "node:os";
import path from "node:path";
import { promisify } from "node:util";
import {
digestFile,
type FileDigest,
verifyStagedRelease,
} from "@/features/catalog/releases/files";
import {
CATALOG_RELEASE_MANIFEST_PATH,
type ReleaseManifest,
validateReleaseManifest,
} from "@/features/catalog/releases/manifest";
import { gitProcessEnvironment } from "./git-process-environment";
const exec = promisify(execFile);
@@ -115,14 +125,14 @@ export async function publishCatalogFiles(options: {
const { stdout } = await exec("git", args, {
cwd: checkout,
timeout: 120_000,
maxBuffer: 16 * 1024 * 1024,
maxBuffer: (args.includes("ls-files") ? 128 : 16) * 1024 * 1024,
env: {
...gitProcessEnvironment(),
...options.env,
GIT_TERMINAL_PROMPT: "0",
},
});
return stdout.trim();
return args.includes("-z") ? stdout : stdout.trim();
};
for (const file of files) catalogTarget(checkout, file.target);
if ((await git("remote", "get-url", "origin")) !== remote)
@@ -172,6 +182,33 @@ export async function publishCatalogFiles(options: {
...files.slice(i, i + 50).map((f) => f.target),
);
}
// Validate the actual index objects, including transformations by Git filters.
const manifestFile = files.find(
(file) => file.target === CATALOG_RELEASE_MANIFEST_PATH,
);
if (manifestFile) {
const manifest: ReleaseManifest = JSON.parse(
await fs.readFile(manifestFile.source, "utf8"),
);
const expected = new Map<string, FileDigest>();
for (const file of files) {
if (expected.has(file.target))
throw new Error("Duplicate catalog target");
expected.set(file.target, await digestFile(file.source));
}
if (manifest.files.length !== files.length - 1)
throw new Error("Catalog release file set mismatch");
await validateReleaseManifest(manifest, async (target) => {
const digest = expected.get(target);
if (!digest) throw new Error("Catalog release file set mismatch");
return digest;
});
verifyStagedRelease(
await git("ls-files", "--stage", "-z"),
await git("rev-parse", "--show-object-format"),
expected,
);
}
if (await git("diff", "--cached", "--name-only")) {
await git(
"-c",