56 lines
3.0 KiB
JavaScript
56 lines
3.0 KiB
JavaScript
#!/usr/bin/env node
|
|
// Responsibility: apply the YAML-owned HWLAB Pipeline provenance contract during the normal control-plane postprocess.
|
|
import { readFileSync, writeFileSync } from "node:fs";
|
|
import { createRequire } from "node:module";
|
|
|
|
const require = createRequire(`${process.cwd()}/package.json`);
|
|
const YAML = require("yaml");
|
|
|
|
export function applyHwlabPipelineProvenance(manifest, annotations, expectedKeys) {
|
|
if (!isRecord(manifest) || manifest.kind !== "Pipeline") return false;
|
|
const entries = isRecord(annotations) ? Object.entries(annotations) : [];
|
|
const actualKeys = entries.map(([key]) => key).sort();
|
|
const sortedExpectedKeys = Array.isArray(expectedKeys) ? [...expectedKeys].sort() : [];
|
|
if (JSON.stringify(actualKeys) !== JSON.stringify(sortedExpectedKeys) || entries.some(([, value]) => typeof value !== "string" || value.length === 0)) {
|
|
throw new Error("overlay.pipelineProvenanceAnnotations must match the shared four-key contract");
|
|
}
|
|
manifest.metadata = isRecord(manifest.metadata) ? manifest.metadata : {};
|
|
manifest.metadata.annotations = { ...(isRecord(manifest.metadata.annotations) ? manifest.metadata.annotations : {}), ...annotations };
|
|
return true;
|
|
}
|
|
|
|
export function postprocessHwlabPipelineProvenance({ pipelinePath, overlay, expectedKeys }) {
|
|
const documents = YAML.parseAllDocuments(readFileSync(pipelinePath, "utf8"));
|
|
const manifests = documents.map((document) => document.toJS()).filter((document) => document !== null);
|
|
const changed = manifests.filter((manifest) => applyHwlabPipelineProvenance(manifest, overlay.pipelineProvenanceAnnotations, expectedKeys));
|
|
if (changed.length !== 1) throw new Error(`expected exactly one Pipeline in ${pipelinePath}; observed ${changed.length}`);
|
|
writeFileSync(pipelinePath, `${manifests.map((manifest) => YAML.stringify(manifest).trimEnd()).join("\n---\n")}\n`);
|
|
return { pipelinePath, pipelineName: changed[0]?.metadata?.name ?? null, annotationCount: expectedKeys.length };
|
|
}
|
|
|
|
function isRecord(value) {
|
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
}
|
|
|
|
function requiredArgument(name) {
|
|
const index = process.argv.indexOf(name);
|
|
const value = index === -1 ? undefined : process.argv[index + 1];
|
|
if (typeof value !== "string" || value.length === 0 || value.startsWith("--")) throw new Error(`${name} is required`);
|
|
return value;
|
|
}
|
|
|
|
function decodedJsonEnv(name) {
|
|
const encoded = process.env[name];
|
|
if (typeof encoded !== "string" || encoded.length === 0) throw new Error(`${name} is required`);
|
|
return JSON.parse(Buffer.from(encoded, "base64").toString("utf8"));
|
|
}
|
|
|
|
if (process.argv.includes("--pipeline")) {
|
|
const result = postprocessHwlabPipelineProvenance({
|
|
pipelinePath: requiredArgument("--pipeline"),
|
|
overlay: decodedJsonEnv("UNIDESK_RUNTIME_GITOPS_OVERLAY_B64"),
|
|
expectedKeys: decodedJsonEnv("UNIDESK_PIPELINE_PROVENANCE_KEYS_B64"),
|
|
});
|
|
console.error(JSON.stringify({ event: "unidesk-runtime-pipeline-provenance", ok: true, ...result }));
|
|
}
|