feat(catalog): verify deterministic release manifests before Git export
This commit is contained in:
1 parent
422be3ce2d
commit
a320e47c29
9 files changed
+491
-9
No files matched your search
@@ -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",
|
||||
|
||||
Reference in new issue
Block a user