/***************************************************************
*
* Facilitates the collection of build outputs and the creation of build provenance metadata.
* * *
* - Do not handle raw file parsing or content validation.
* - Do not manage build orchestration or execution flow.
* - Do not interact with external systems beyond file I/O.
*
*
* *
- RFC-1097: step 6 — compass.migrate codemod run
Mechanical v1 to v2 header migration across the workspace: 942 files rewritten — CHANGE_SUMMARY windows collapsed into history, forbidden v1 blocks stripped, KEY_DECISIONS seeded from @ai-invariant comments (5 files) or TODO placeholders (103 files), blocks reordered to canonical order.
- RFC-1097: sweep — werkstatt-engine clean
Sweep batch 4: 73 Compass headers on headerless engine files (certification, component-runtime, isolation, evolution, testing), real KEY_DECISIONS on 75 files (kernel, cache, dht, swim, gitmesh, runtime), ~80 purpose expansions (CONTRACT-02/PURPOSE-02), non-goals on 13 CONTRACT-03 files, CS-07 history literal fix repo-wide (253 files). Policy: .template.ts/.template.astro excludedPaths. werkstatt-engine now 0 diagnostics.
***************************************************************/
/**
* Build artifact collection and provenance creation.
* Computes hashes of dist files and creates signed build metadata.
*/
import path from "node:path";
import { byteHash, byteHashFile } from "@warpgogol/werkstatt-engine/fingerprint";
import { discoverManagedFiles } from "./discover.ts";
import { discoverDistFiles } from "./internal-dist.ts";
import { getHeadSha, getRepoUrl } from "./git.ts";
import { writeJsonFile } from "./json.ts";
import { outputsPath, provenancePath } from "./paths.ts";
import type { BuildProvenance, OutputsFile } from "./types.ts";
export async function collectBuildOutputs(cwd: string, distDir = "dist"): Promise {
const buildId = new Date().toISOString().replace(/[:.]/g, "-");
const outputs: Record = {};
const files = await discoverDistFiles(cwd, distDir);
for (const file of files) {
outputs[file] = await byteHashFile(path.join(cwd, file));
}
return { buildId, outputs };
}
export async function buildInputsDigest(cwd: string): Promise {
const files = await discoverManagedFiles(cwd);
const entries = await Promise.all(
files.map(async (file) => `${file}\t${await byteHashFile(path.join(cwd, file))}`),
);
const combined = entries.join("\n");
return byteHash(combined);
}
export async function createBuildProvenance(args: {
cwd: string;
builder: string;
outputs: OutputsFile;
}): Promise {
const started = new Date().toISOString();
const outputsDigest = byteHash(JSON.stringify(args.outputs.outputs));
const inputsDigest = await buildInputsDigest(args.cwd);
const finished = new Date().toISOString();
return {
buildId: args.outputs.buildId,
sourceRepo: (await getRepoUrl(args.cwd)) ?? "unknown",
sourceCommit: (await getHeadSha(args.cwd)) ?? "unknown",
builder: args.builder,
buildStartedAt: started,
buildFinishedAt: finished,
inputsDigest,
outputsDigest,
};
}
export async function writeBuildArtifacts(args: {
cwd: string;
outputs: OutputsFile;
provenance: BuildProvenance;
}): Promise {
await writeJsonFile(outputsPath(args.cwd), args.outputs);
await writeJsonFile(provenancePath(args.cwd), args.provenance);
}