Files
pikasTech-unidesk/scripts/src/platform-infra-pipelines-as-code-source-artifact.ts
T

910 lines
44 KiB
TypeScript

import { createHash } from "node:crypto";
import { cpSync, existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, realpathSync, renameSync, rmSync, statSync, symlinkSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { dirname, isAbsolute, relative, resolve, sep } from "node:path";
import { spawnSync } from "node:child_process";
import { rootPath } from "./config";
import { AGENTRUN_CONFIG_PATH, resolveAgentRunLaneTarget } from "./agentrun-lanes";
import { renderAgentRunPipelineManifest } from "./agentrun-manifests";
import { hwlabRuntimeLaneSpecForNode, isHwlabRuntimeLane, type HwlabRuntimeLaneSpec } from "./hwlab-node-lanes";
import { nodeRuntimeGitopsRoot } from "./hwlab-node/cleanup";
import { nodeRuntimePipelinePostprocessScript } from "./hwlab-node/render";
import { nodeRuntimeRenderOverlay } from "./hwlab-node/web-probe";
import { applyNodeRuntimeDeployYamlOverlay } from "./hwlab-node/deploy-overlay";
import type { RenderedCliResult } from "./output";
import { renderedCliResult } from "./agentrun/render";
import { stableJsonSha256 } from "./stable-json";
export type PacSourceArtifactMode = "embedded-pipeline-spec" | "remote-pipeline-annotation";
export type PacSourceArtifactRenderer = "agentrun-control-plane" | "hwlab-runtime-lane";
export type PacSourceArtifactAction = "plan" | "check" | "write" | "status" | "verify-runtime";
export interface PacSourceArtifactSpec {
readonly mode: PacSourceArtifactMode;
readonly renderer: PacSourceArtifactRenderer;
readonly configRef: string;
readonly pipelineRunPath: string;
readonly pipelinePath: string | null;
readonly maxKeepRuns: number;
}
export interface PacSourceArtifactBinding {
readonly target: { readonly id: string };
readonly consumer: {
readonly id: string;
readonly node: string;
readonly lane: string;
readonly namespace: string;
readonly pipeline: string;
readonly pipelineRunPrefix: string;
readonly sourceArtifact: PacSourceArtifactSpec;
};
readonly repository: {
readonly id: string;
readonly cloneUrl: string;
readonly owner: string;
readonly repo: string;
readonly params: Readonly<Record<string, string>>;
};
}
export interface PacSourceArtifactOptions {
readonly action: PacSourceArtifactAction;
readonly targetId: string;
readonly consumerId: string;
readonly sourceWorktree: string;
readonly sourceCommit: string | null;
readonly confirm: boolean;
readonly json: boolean;
readonly full: boolean;
}
export interface PacSourceArtifactRuntimeObservation {
readonly live: PacSourceArtifactRuntimeItem;
readonly embedded: PacSourceArtifactRuntimeItem;
}
export interface PacSourceArtifactRuntimeItem {
readonly status: "aligned" | "drifted" | "missing" | "unavailable";
readonly name: string | null;
readonly canonicalSha256: string | null;
readonly firstDrift: PacSourceArtifactDrift | null;
readonly provenanceAligned: boolean;
readonly configRef: string | null;
readonly effectiveConfigSha256: string | null;
readonly sourceCommit: string | null;
readonly reason: string | null;
readonly candidateCount: number;
}
export interface PacSourceArtifactDrift {
readonly path: string;
readonly expected: string;
readonly actual: string;
}
export type PacSourceArtifactRuntimeObserver = (input: {
readonly binding: PacSourceArtifactBinding;
readonly desiredSpec: Record<string, unknown>;
readonly provenance: PacSourceArtifactProvenance;
readonly sourceCommit: string | null;
}) => Promise<PacSourceArtifactRuntimeObservation>;
interface PacSourceArtifactProvenance {
readonly configRef: string;
readonly effectiveConfigSha256: string;
readonly renderer: PacSourceArtifactRenderer;
}
interface DesiredArtifact {
readonly pipeline: Record<string, unknown>;
readonly pipelineRun: Record<string, unknown>;
readonly provenance: PacSourceArtifactProvenance;
readonly desiredSpec: Record<string, unknown>;
readonly files: readonly { readonly path: string; readonly content: string }[];
}
interface SourceInspection {
readonly status: "aligned" | "drifted" | "missing";
readonly aligned: boolean;
readonly pipelineCanonicalSha256: string | null;
readonly pipelineRunCanonicalSha256: string | null;
readonly firstDrift: PacSourceArtifactDrift | null;
readonly provenanceAligned: boolean;
}
const provenanceKeys = {
configRef: "unidesk.ai/owning-config-ref",
effectiveConfigSha256: "unidesk.ai/effective-config-sha256",
renderer: "unidesk.ai/source-artifact-renderer",
mode: "unidesk.ai/source-artifact-mode",
} as const;
export function parsePacSourceArtifactOptions(args: readonly string[]): PacSourceArtifactOptions {
const action = args[0];
if (!isSourceArtifactAction(action)) {
throw new Error("source-artifact requires one of plan, check, write, status, verify-runtime");
}
let targetId: string | null = null;
let consumerId: string | null = null;
let sourceWorktree: string | null = null;
let sourceCommit: string | null = null;
let confirm = false;
let json = false;
let full = false;
for (let index = 1; index < args.length; index += 1) {
const arg = args[index];
if (arg === "--confirm") {
confirm = true;
continue;
}
if (arg === "--json") {
json = true;
continue;
}
if (arg === "--full") {
full = true;
continue;
}
if (arg === "--target" || arg === "--consumer" || arg === "--source-worktree" || arg === "--source-commit") {
const value = args[index + 1];
if (value === undefined || value.startsWith("--")) throw new Error(`${arg} requires a value`);
if (arg === "--target") targetId = value;
else if (arg === "--consumer") consumerId = value;
else if (arg === "--source-worktree") sourceWorktree = value;
else sourceCommit = value.toLowerCase();
index += 1;
continue;
}
throw new Error(`unsupported source-artifact option: ${arg}`);
}
if (targetId === null) throw new Error("source-artifact requires explicit --target");
if (consumerId === null) throw new Error("source-artifact requires explicit --consumer");
if (sourceWorktree === null) throw new Error("source-artifact requires explicit --source-worktree");
if (!isAbsolute(sourceWorktree)) throw new Error("--source-worktree must be an absolute path");
if (sourceCommit !== null && !/^[0-9a-f]{40}$/u.test(sourceCommit)) throw new Error("--source-commit must be a full 40-character git SHA");
if (sourceCommit !== null && action !== "status" && action !== "verify-runtime") throw new Error("--source-commit is accepted only by source-artifact status or verify-runtime");
if (action === "verify-runtime" && sourceCommit === null) throw new Error("source-artifact verify-runtime requires explicit --source-commit");
if (action === "write" && !confirm) throw new Error("source-artifact write requires --confirm");
if (action !== "write" && confirm) throw new Error("--confirm is accepted only by source-artifact write");
return { action, targetId, consumerId, sourceWorktree, sourceCommit, confirm, json, full };
}
export async function runPacSourceArtifact(
binding: PacSourceArtifactBinding,
options: PacSourceArtifactOptions,
observeRuntime?: PacSourceArtifactRuntimeObserver,
): Promise<Record<string, unknown>> {
if (binding.target.id.toLowerCase() !== options.targetId.toLowerCase()) {
throw new Error(`source-artifact target ${options.targetId} does not match resolved target ${binding.target.id}`);
}
if (binding.consumer.id.toLowerCase() !== options.consumerId.toLowerCase()) {
throw new Error(`source-artifact consumer ${options.consumerId} does not match resolved consumer ${binding.consumer.id}`);
}
const sourceWorktree = validateSourceWorktree(options.sourceWorktree, binding);
const desired = renderDesiredArtifact(binding, sourceWorktree);
let source = inspectSourceArtifact(sourceWorktree, binding, desired);
const written: string[] = [];
if (options.action === "write") {
written.push(...writeArtifactFilesAtomically(sourceWorktree, desired.files));
source = inspectSourceArtifact(sourceWorktree, binding, desired);
}
const needsRuntime = options.action === "status" || options.action === "verify-runtime";
const runtime = needsRuntime
? observeRuntime === undefined
? unavailableRuntimeObservation()
: await observeRuntime({ binding, desiredSpec: desired.desiredSpec, provenance: desired.provenance, sourceCommit: options.sourceCommit })
: null;
const sourceOk = source.aligned && source.provenanceAligned;
const runtimeOk = runtime !== null
&& runtime.live.status === "aligned"
&& runtime.live.provenanceAligned
&& runtime.embedded.status === "aligned"
&& runtime.embedded.provenanceAligned;
const ok = options.action === "check"
? sourceOk
: options.action === "verify-runtime"
? sourceOk && runtimeOk
: options.action === "write"
? sourceOk
: true;
const desiredSpecSha256 = canonicalSha256(desired.desiredSpec);
return {
ok,
action: `platform-infra-pipelines-as-code-source-artifact-${options.action}`,
mutation: options.action === "write" && written.length > 0,
target: binding.target.id,
consumer: binding.consumer.id,
node: binding.consumer.node,
lane: binding.consumer.lane,
mode: binding.consumer.sourceArtifact.mode,
renderer: binding.consumer.sourceArtifact.renderer,
sourceWorktree,
files: {
pipelineRun: binding.consumer.sourceArtifact.pipelineRunPath,
pipeline: binding.consumer.sourceArtifact.pipelinePath,
written,
},
provenance: desired.provenance,
desired: {
canonicalSha256: desiredSpecSha256,
pipeline: binding.consumer.pipeline,
namespace: binding.consumer.namespace,
},
source,
runtime,
runtimeTarget: { sourceCommit: options.sourceCommit },
runtimeAlignment: runtime === null
? "not-requested"
: runtimeOk
? "aligned"
: runtime.live.status === "missing" || runtime.embedded.status === "missing"
? "missing"
: runtime.live.status === "unavailable" || runtime.embedded.status === "unavailable"
? "unavailable"
: "pending",
boundary: {
desiredAuthority: "owning YAML plus domain renderer",
liveExportAllowed: false,
sourceWorktreeExplicit: true,
canonicalAdmissionDefaultsRemoved: [
"tasks[].taskSpec.metadata={}",
"tasks[].taskSpec.spec=null",
"tasks[].taskSpec.params[].type=string",
"tasks[].taskSpec.results[].type=string",
"tasks[].taskSpec.steps[].computeResources={}",
"tasks[].taskSpec.sidecars[].computeResources={}",
],
valuesPrinted: false,
},
next: sourceOk
? options.action === "verify-runtime"
? null
: `After the consumer PR rolls out, run source-artifact verify-runtime for ${binding.consumer.id} with --source-commit <full-sha>.`
: options.action === "write"
? "Generated files are still inconsistent; inspect firstDrift and do not publish."
: `Run source-artifact write --confirm against the explicit ${sourceWorktree} worktree.`,
};
}
export function renderPacSourceArtifactResult(result: Record<string, unknown>): RenderedCliResult {
const files = record(result.files);
const desired = record(result.desired);
const source = record(result.source);
const runtime = result.runtime === null ? null : record(result.runtime);
const live = runtime === null ? null : record(runtime.live);
const embedded = runtime === null ? null : record(runtime.embedded);
const firstDrift = source.firstDrift === null ? null : record(source.firstDrift);
const lines = [
"PAC SOURCE ARTIFACT",
`target=${text(result.target)} consumer=${text(result.consumer)} node=${text(result.node)} lane=${text(result.lane)}`,
`mode=${text(result.mode)} renderer=${text(result.renderer)} mutation=${text(result.mutation)}`,
`pipelineRun=${text(files.pipelineRun)} pipeline=${text(files.pipeline ?? "-")}`,
`desired=${shortSha(desired.canonicalSha256)} source=${shortSha(source.pipelineCanonicalSha256)} aligned=${text(source.aligned)} provenance=${text(source.provenanceAligned)}`,
`firstDrift=${firstDrift === null ? "-" : `${text(firstDrift.path)} expected=${text(firstDrift.expected)} actual=${text(firstDrift.actual)}`}`,
`runtime=${text(result.runtimeAlignment)} live=${text(live?.name)}@${shortSha(live?.canonicalSha256)} embedded=${text(embedded?.name)}@${shortSha(embedded?.canonicalSha256)} commit=${text(embedded?.sourceCommit ?? record(result.runtimeTarget).sourceCommit)} candidates=${text(embedded?.candidateCount)}`,
`runtimeReason=live:${text(live?.reason)} embedded:${text(embedded?.reason)}`,
`written=${Array.isArray(files.written) && files.written.length > 0 ? files.written.join(",") : "-"}`,
];
if (typeof result.next === "string") lines.push(`next=${result.next}`);
return renderedCliResult(result.ok !== false, String(result.action), `${lines.join("\n")}\n`);
}
export function canonicalizePacPipelineSpec(value: unknown, path: readonly (string | number)[] = []): unknown {
if (Array.isArray(value)) return value.map((item, index) => canonicalizePacPipelineSpec(item, [...path, index]));
if (!isRecord(value)) return value;
const output: Record<string, unknown> = {};
for (const key of Object.keys(value).sort()) {
const child = value[key];
const childPath = [...path, key];
if (isProvenTektonAdmissionDefault(childPath, child)) continue;
output[key] = canonicalizePacPipelineSpec(child, childPath);
}
return output;
}
export function canonicalSha256(value: unknown): string {
return `sha256:${createHash("sha256").update(JSON.stringify(canonicalizePacPipelineSpec(value))).digest("hex")}`;
}
export function firstPacSourceArtifactDrift(expected: unknown, actual: unknown, path = "$"): PacSourceArtifactDrift | null {
const left = canonicalizePacPipelineSpec(expected);
const right = canonicalizePacPipelineSpec(actual);
return firstCanonicalDrift(left, right, path);
}
function renderDesiredArtifact(binding: PacSourceArtifactBinding, sourceWorktree: string): DesiredArtifact {
const sourceArtifact = binding.consumer.sourceArtifact;
const rendered = sourceArtifact.renderer === "agentrun-control-plane"
? renderAgentRunDesiredPipeline(binding)
: renderHwlabDesiredPipeline(binding, sourceWorktree);
const pipeline = withPipelineProvenance(rendered.pipeline, rendered.provenance, sourceArtifact.mode);
const desiredSpec = requiredRecord(pipeline.spec, "rendered Pipeline.spec");
assertPipelineIdentity(pipeline, binding);
if (sourceArtifact.renderer === "hwlab-runtime-lane") assertHwlabPipelineContracts(desiredSpec);
const pipelineRun = sourceArtifact.mode === "embedded-pipeline-spec"
? embeddedPipelineRun(binding, desiredSpec, rendered.provenance)
: remotePipelineRun(binding, pipeline, rendered.provenance);
const files = sourceArtifact.mode === "embedded-pipeline-spec"
? [{ path: sourceArtifact.pipelineRunPath, content: yaml(pipelineRun) }]
: [
{ path: requiredString(sourceArtifact.pipelinePath, "sourceArtifact.pipelinePath"), content: `${JSON.stringify(pipeline)}\n` },
{ path: sourceArtifact.pipelineRunPath, content: yaml(pipelineRun) },
];
return { pipeline, pipelineRun, provenance: rendered.provenance, desiredSpec, files };
}
function renderAgentRunDesiredPipeline(binding: PacSourceArtifactBinding): { pipeline: Record<string, unknown>; provenance: PacSourceArtifactProvenance } {
const { spec } = resolveAgentRunLaneTarget({ node: binding.consumer.node, lane: binding.consumer.lane });
const expectedConfigRef = `${AGENTRUN_CONFIG_PATH}#controlPlane.lanes.${spec.lane}`;
assertConfigRef(binding.consumer.sourceArtifact.configRef, expectedConfigRef);
assertBindingIdentity(binding, {
node: spec.nodeId,
lane: spec.lane,
namespace: spec.ci.namespace,
pipeline: spec.ci.pipeline,
pipelineRunPrefix: spec.ci.pipelineRunPrefix,
serviceAccount: spec.ci.serviceAccountName,
sourceBranch: spec.source.branch,
gitopsBranch: spec.gitops.branch,
});
return {
pipeline: renderAgentRunPipelineManifest(spec),
provenance: {
configRef: expectedConfigRef,
effectiveConfigSha256: stableJsonSha256(spec),
renderer: "agentrun-control-plane",
},
};
}
function renderHwlabDesiredPipeline(binding: PacSourceArtifactBinding, sourceWorktree: string): { pipeline: Record<string, unknown>; provenance: PacSourceArtifactProvenance } {
if (!isHwlabRuntimeLane(binding.consumer.lane)) throw new Error(`HWLAB source artifact lane ${binding.consumer.lane} is not declared`);
const spec = hwlabRuntimeLaneSpecForNode(binding.consumer.lane, binding.consumer.node);
const expectedConfigRef = `config/hwlab-node-lanes.yaml#lanes.${spec.lane}.targets.${spec.nodeId}`;
assertConfigRef(binding.consumer.sourceArtifact.configRef, expectedConfigRef);
assertBindingIdentity(binding, {
node: spec.nodeId,
lane: spec.lane,
namespace: "hwlab-ci",
pipeline: spec.pipeline,
pipelineRunPrefix: spec.pipelineRunPrefix,
serviceAccount: spec.serviceAccountName,
sourceBranch: spec.sourceBranch,
gitopsBranch: spec.gitopsBranch,
});
const pipeline = renderHwlabPipelineFromOwningYaml(spec, sourceWorktree);
return {
pipeline,
provenance: {
configRef: expectedConfigRef,
effectiveConfigSha256: stableJsonSha256(spec),
renderer: "hwlab-runtime-lane",
},
};
}
function renderHwlabPipelineFromOwningYaml(spec: HwlabRuntimeLaneSpec, sourceWorktree: string): Record<string, unknown> {
const temporaryRoot = mkdtempSync(resolve(tmpdir(), "unidesk-pac-source-artifact-"));
const temporarySource = resolve(temporaryRoot, "source");
const output = resolve(temporaryRoot, "rendered");
const sourceCommit = git(sourceWorktree, ["rev-parse", "HEAD"]);
try {
copySourceWorktreeForRender(sourceWorktree, temporarySource);
const overlayRecord = nodeRuntimeRenderOverlay(spec);
const deployPath = resolve(temporarySource, "deploy", "deploy.yaml");
const deploy = parseYamlRecord(readFileSync(deployPath, "utf8"), deployPath);
writeFileSync(deployPath, `${Bun.YAML.stringify(applyNodeRuntimeDeployYamlOverlay(deploy, overlayRecord)).trim()}\n`);
const renderArgs = [
"scripts/run-bun.mjs",
"scripts/gitops-render.mjs",
"--lane", spec.lane,
"--node", spec.nodeId,
"--gitops-root", nodeRuntimeGitopsRoot(spec),
"--catalog-path", spec.catalogPath,
"--image-tag-mode", "full",
"--source-revision", sourceCommit,
"--source-repo", spec.gitUrl,
"--source-branch", spec.sourceBranch,
"--gitops-branch", spec.gitopsBranch,
"--git-read-url", spec.gitReadUrl,
"--git-write-url", spec.gitWriteUrl,
"--registry-prefix", spec.registryPrefix,
"--runtime-endpoint", spec.publicApiUrl,
"--web-endpoint", spec.publicWebUrl,
"--out", output,
];
runChecked("node", renderArgs, temporarySource, 120_000, "HWLAB owning YAML renderer");
const overlay = Buffer.from(JSON.stringify(overlayRecord), "utf8").toString("base64");
const postprocess = [
"set -eu",
`render_dir=${shellQuote(output)}`,
`overlay_b64=${shellQuote(overlay)}`,
...nodeRuntimePipelinePostprocessScript(),
].join("\n");
runChecked("sh", ["-c", postprocess], temporarySource, 120_000, "HWLAB UniDesk domain postprocess/verify renderer");
const path = resolve(output, spec.tektonDir, "pipeline.yaml");
if (!existsSync(path)) throw new Error(`HWLAB domain renderer did not produce ${path}`);
return parseYamlRecord(readFileSync(path, "utf8"), path);
} finally {
rmSync(temporaryRoot, { recursive: true, force: true });
}
}
function copySourceWorktreeForRender(sourceWorktree: string, destination: string): void {
const excluded = new Set([".git", ".worktree", ".state", "node_modules", "coverage", "tmp", ".tmp"]);
cpSync(sourceWorktree, destination, {
recursive: true,
filter: (source) => {
const rel = relative(sourceWorktree, source);
if (rel === "") return true;
return !excluded.has(rel.split(sep)[0] ?? "");
},
});
const nodeModules = resolve(sourceWorktree, "node_modules");
if (existsSync(nodeModules)) symlinkSync(nodeModules, resolve(destination, "node_modules"), "dir");
}
function embeddedPipelineRun(binding: PacSourceArtifactBinding, desiredSpec: Record<string, unknown>, provenance: PacSourceArtifactProvenance): Record<string, unknown> {
return {
apiVersion: "tekton.dev/v1",
kind: "PipelineRun",
metadata: {
name: `${binding.consumer.pipelineRunPrefix}-{{ revision }}`,
namespace: binding.consumer.namespace,
annotations: pipelineRunAnnotations(binding, provenance),
labels: pipelineRunLabels(binding, "agentrun"),
},
spec: {
pipelineSpec: desiredSpec,
taskRunTemplate: taskRunTemplate(binding),
params: pipelineRunParams(binding, desiredSpec),
workspaces: pipelineRunWorkspaces(binding, desiredSpec),
},
};
}
function remotePipelineRun(binding: PacSourceArtifactBinding, pipeline: Record<string, unknown>, provenance: PacSourceArtifactProvenance): Record<string, unknown> {
const desiredSpec = requiredRecord(pipeline.spec, "rendered Pipeline.spec");
const pipelinePath = requiredString(binding.consumer.sourceArtifact.pipelinePath, "sourceArtifact.pipelinePath");
const annotations = {
...pipelineRunAnnotations(binding, provenance),
"pipelinesascode.tekton.dev/pipeline": pipelinePath,
};
if (provenance.renderer === "hwlab-runtime-lane") {
if (!isHwlabRuntimeLane(binding.consumer.lane)) throw new Error(`HWLAB source artifact lane ${binding.consumer.lane} is not declared`);
const spec = hwlabRuntimeLaneSpecForNode(binding.consumer.lane, binding.consumer.node);
Object.assign(annotations, {
"hwlab.pikastech.local/ci-contract": "tekton-native-primitive-tasks",
"hwlab.pikastech.local/download-profile": spec.downloadProfileId,
"hwlab.pikastech.local/gitops-branch": spec.gitopsBranch,
"hwlab.pikastech.local/network-profile": spec.networkProfileId,
"hwlab.pikastech.local/node": spec.nodeId,
"hwlab.pikastech.local/policy": "native-per-service-taskrun-image-publish",
"hwlab.pikastech.local/runtime-path": spec.runtimePath,
"hwlab.pikastech.local/source-branch": spec.sourceBranch,
"hwlab.pikastech.local/source-config": binding.consumer.sourceArtifact.pipelineRunPath,
});
}
return {
apiVersion: "tekton.dev/v1",
kind: "PipelineRun",
metadata: {
generateName: `${binding.consumer.pipelineRunPrefix}-`,
namespace: binding.consumer.namespace,
annotations,
labels: pipelineRunLabels(binding, "hwlab"),
},
spec: {
timeouts: { pipeline: binding.repository.params.pipeline_timeout },
taskRunTemplate: taskRunTemplate(binding),
pipelineRef: { name: binding.consumer.pipeline },
workspaces: pipelineRunWorkspaces(binding, desiredSpec),
params: pipelineRunParams(binding, desiredSpec),
},
};
}
function pipelineRunAnnotations(binding: PacSourceArtifactBinding, provenance: PacSourceArtifactProvenance): Record<string, string> {
const branch = requiredParam(binding, "source_branch");
return {
"pipelinesascode.tekton.dev/on-event": "[push]",
"pipelinesascode.tekton.dev/on-target-branch": `[${branch}]`,
"pipelinesascode.tekton.dev/on-cel-expression": `event == 'push' && target_branch == '${branch}' && node == '${binding.consumer.node}'`,
"pipelinesascode.tekton.dev/max-keep-runs": String(binding.consumer.sourceArtifact.maxKeepRuns),
[provenanceKeys.configRef]: provenance.configRef,
[provenanceKeys.effectiveConfigSha256]: provenance.effectiveConfigSha256,
[provenanceKeys.renderer]: provenance.renderer,
[provenanceKeys.mode]: binding.consumer.sourceArtifact.mode,
};
}
function pipelineRunLabels(binding: PacSourceArtifactBinding, partOf: "agentrun" | "hwlab"): Record<string, string> {
return partOf === "agentrun"
? {
"app.kubernetes.io/part-of": "agentrun",
"agentrun.pikastech.local/lane": "v0.2",
"agentrun.pikastech.local/node": binding.consumer.node,
"agentrun.pikastech.local/source-commit": "{{ revision }}",
"agentrun.pikastech.local/trigger": "pipelines-as-code",
}
: {
"app.kubernetes.io/name": `${binding.consumer.id}-pac`,
"app.kubernetes.io/part-of": "hwlab",
"unidesk.ai/source-commit": "{{ revision }}",
"hwlab.pikastech.local/gitops-target": binding.consumer.lane,
"hwlab.pikastech.local/source-commit": "{{ revision }}",
"hwlab.pikastech.local/trigger": "pipelines-as-code",
};
}
function taskRunTemplate(binding: PacSourceArtifactBinding): Record<string, unknown> {
return {
serviceAccountName: `{{ service_account }}`,
podTemplate: {
hostNetwork: true,
dnsPolicy: "ClusterFirstWithHostNet",
securityContext: { fsGroup: 1000 },
},
};
}
function pipelineRunParams(binding: PacSourceArtifactBinding, desiredSpec: Record<string, unknown>): Record<string, unknown>[] {
const params = arrayRecords(desiredSpec.params, "Pipeline.spec.params");
return params.map((param) => {
const name = requiredString(param.name, "Pipeline.spec.params[].name");
if (name === "revision" || name === "source-commit") return { name, value: "{{ revision }}" };
if (name === "source-stage-ref") return { name, value: "{{ source_snapshot_prefix }}/{{ revision }}" };
if (name === "git-url") return { name, value: "{{ repo_url }}" };
const repositoryParam = name.replaceAll("-", "_");
if (Object.prototype.hasOwnProperty.call(binding.repository.params, repositoryParam)) {
return { name, value: `{{ ${repositoryParam} }}` };
}
if (Object.prototype.hasOwnProperty.call(param, "default")) return { name, value: param.default };
throw new Error(`Pipeline param ${name} has no default or PaC repository parameter`);
});
}
function pipelineRunWorkspaces(binding: PacSourceArtifactBinding, desiredSpec: Record<string, unknown>): Record<string, unknown>[] {
return arrayRecords(desiredSpec.workspaces, "Pipeline.spec.workspaces").map((workspace) => {
const name = requiredString(workspace.name, "Pipeline.spec.workspaces[].name");
if (name === "source") {
return {
name,
volumeClaimTemplate: {
spec: {
accessModes: ["ReadWriteOnce"],
resources: { requests: { storage: requiredParam(binding, "workspace_pvc_size") } },
},
},
};
}
if (name === "git-ssh") return { name, secret: { secretName: requiredParam(binding, "git_ssh_secret") } };
throw new Error(`unsupported Pipeline workspace ${name}; declare a renderer mapping before generating the source artifact`);
});
}
function withPipelineProvenance(pipeline: Record<string, unknown>, provenance: PacSourceArtifactProvenance, mode: PacSourceArtifactMode): Record<string, unknown> {
const next = structuredClone(pipeline);
const metadata = requiredRecord(next.metadata, "rendered Pipeline.metadata");
const annotations = isRecord(metadata.annotations) ? metadata.annotations : {};
metadata.annotations = {
...annotations,
[provenanceKeys.configRef]: provenance.configRef,
[provenanceKeys.effectiveConfigSha256]: provenance.effectiveConfigSha256,
[provenanceKeys.renderer]: provenance.renderer,
[provenanceKeys.mode]: mode,
};
return next;
}
function inspectSourceArtifact(sourceWorktree: string, binding: PacSourceArtifactBinding, desired: DesiredArtifact): SourceInspection {
const sourceArtifact = binding.consumer.sourceArtifact;
const pipelineRunPath = safeArtifactPath(sourceWorktree, sourceArtifact.pipelineRunPath);
const pipelinePath = sourceArtifact.pipelinePath === null ? null : safeArtifactPath(sourceWorktree, sourceArtifact.pipelinePath);
if (!existsSync(pipelineRunPath) || (pipelinePath !== null && !existsSync(pipelinePath))) {
return { status: "missing", aligned: false, pipelineCanonicalSha256: null, pipelineRunCanonicalSha256: null, firstDrift: { path: "$", expected: "generated artifact", actual: "missing" }, provenanceAligned: false };
}
const actualPipelineRun = parseYamlRecord(readFileSync(pipelineRunPath, "utf8"), sourceArtifact.pipelineRunPath);
const actualPipeline = sourceArtifact.mode === "remote-pipeline-annotation"
? parseYamlRecord(readFileSync(requiredString(pipelinePath, "pipelinePath"), "utf8"), requiredString(sourceArtifact.pipelinePath, "pipelinePath"))
: {
apiVersion: "tekton.dev/v1",
kind: "Pipeline",
metadata: desired.pipeline.metadata,
spec: requiredRecord(requiredRecord(actualPipelineRun.spec, "PipelineRun.spec").pipelineSpec, "PipelineRun.spec.pipelineSpec"),
};
const pipelineDrift = firstPacSourceArtifactDrift(desired.pipeline, actualPipeline);
const pipelineRunDrift = firstPacSourceArtifactDrift(desired.pipelineRun, actualPipelineRun);
const actualProvenance = sourceArtifact.mode === "remote-pipeline-annotation"
? provenanceFromManifest(actualPipeline)
: provenanceFromManifest(actualPipelineRun);
const wrapperProvenance = provenanceFromManifest(actualPipelineRun);
const provenanceAligned = provenanceEquals(actualProvenance, desired.provenance) && provenanceEquals(wrapperProvenance, desired.provenance);
const aligned = pipelineDrift === null && pipelineRunDrift === null && provenanceAligned;
return {
status: aligned ? "aligned" : "drifted",
aligned,
pipelineCanonicalSha256: canonicalSha256(requiredRecord(actualPipeline.spec, "source Pipeline.spec")),
pipelineRunCanonicalSha256: canonicalSha256(actualPipelineRun),
firstDrift: pipelineDrift ?? pipelineRunDrift ?? (provenanceAligned ? null : { path: "$.metadata.annotations.provenance", expected: JSON.stringify(desired.provenance), actual: JSON.stringify(actualProvenance) }),
provenanceAligned,
};
}
function validateSourceWorktree(input: string, binding: PacSourceArtifactBinding): string {
if (!existsSync(input) || !statSync(input).isDirectory()) throw new Error(`source worktree does not exist or is not a directory: ${input}`);
const worktree = realpathSync(input);
const top = realpathSync(git(worktree, ["rev-parse", "--show-toplevel"]));
if (top !== worktree) throw new Error(`--source-worktree must be the git worktree root; resolved ${top}`);
const remote = git(worktree, ["remote", "get-url", "origin"]);
const repoIdentity = binding.repository.repo.toLowerCase().replace(/\.git$/u, "");
const canonicalIdentity = repoIdentity.replace(/^pikastech-/u, "pikastech/");
const allowedIdentities = new Set([
canonicalIdentity,
`${binding.repository.owner}/${repoIdentity}`.toLowerCase(),
remoteRepositoryIdentity(binding.repository.cloneUrl),
]);
const observedIdentity = remoteRepositoryIdentity(remote);
if (!allowedIdentities.has(observedIdentity)) {
throw new Error(`source worktree origin does not match PaC repository ${binding.repository.repo}; observed ${remote}`);
}
safeArtifactPath(worktree, binding.consumer.sourceArtifact.pipelineRunPath);
if (binding.consumer.sourceArtifact.pipelinePath !== null) safeArtifactPath(worktree, binding.consumer.sourceArtifact.pipelinePath);
return worktree;
}
function remoteRepositoryIdentity(remote: string): string {
const trimmed = remote.trim();
const scp = /^[^/@\s]+@[^:\s]+:(.+)$/u.exec(trimmed);
let path = scp?.[1] ?? "";
if (path.length === 0) {
try {
path = new URL(trimmed).pathname;
} catch {
throw new Error(`git remote must be an SSH or URL repository identity; observed ${remote}`);
}
}
return path.replace(/^\/+|\/+$/gu, "").replace(/\.git$/iu, "").toLowerCase();
}
function writeArtifactFilesAtomically(sourceWorktree: string, files: readonly { readonly path: string; readonly content: string }[]): string[] {
const pending = files.map((file) => ({ ...file, output: safeArtifactPath(sourceWorktree, file.path) }))
.filter((file) => !existsSync(file.output) || readFileSync(file.output, "utf8") !== file.content);
if (pending.length === 0) return [];
for (const file of pending) {
mkdirSync(dirname(file.output), { recursive: true });
safeArtifactPath(sourceWorktree, file.path);
}
const token = `${process.pid}-${Date.now()}`;
const staged = pending.map((file, index) => ({ ...file, temporary: `${file.output}.unidesk-${token}-${index}.tmp` }));
try {
for (const file of staged) writeFileSync(file.temporary, file.content, { flag: "wx" });
for (const file of staged) renameSync(file.temporary, file.output);
} finally {
for (const file of staged) rmSync(file.temporary, { force: true });
}
return pending.map((file) => file.path);
}
function safeArtifactPath(worktree: string, relativePath: string): string {
if (relativePath.length === 0 || isAbsolute(relativePath) || relativePath.split(/[\\/]/u).includes("..")) {
throw new Error(`source artifact path must be a non-empty worktree-relative path without ..: ${relativePath}`);
}
const root = realpathSync(worktree);
const candidate = resolve(root, relativePath);
if (candidate !== root && !candidate.startsWith(`${root}${sep}`)) throw new Error(`source artifact path escapes worktree: ${relativePath}`);
let current = root;
for (const segment of relativePath.split(/[\\/]/u).filter(Boolean)) {
current = resolve(current, segment);
if (!existsSync(current)) continue;
if (lstatSync(current).isSymbolicLink()) throw new Error(`source artifact path crosses symlink: ${relativePath}`);
const resolved = realpathSync(current);
if (resolved !== root && !resolved.startsWith(`${root}${sep}`)) throw new Error(`source artifact path resolves outside worktree: ${relativePath}`);
}
return candidate;
}
function assertConfigRef(actual: string, expected: string): void {
if (actual !== expected) throw new Error(`sourceArtifact.configRef must equal resolved owning selector ${expected}; observed ${actual}`);
const [file, selector] = actual.split("#", 2);
if (file === undefined || selector === undefined || selector.length === 0) throw new Error(`invalid sourceArtifact.configRef: ${actual}`);
const parsed = Bun.YAML.parse(readFileSync(rootPath(...file.split("/")), "utf8")) as unknown;
let value = parsed;
for (const segment of selector.split(".")) {
if (!isRecord(value) || !Object.prototype.hasOwnProperty.call(value, segment)) throw new Error(`sourceArtifact.configRef selector does not exist: ${actual}`);
value = value[segment];
}
if (!isRecord(value)) throw new Error(`sourceArtifact.configRef must resolve to an owning YAML object: ${actual}`);
}
function assertBindingIdentity(binding: PacSourceArtifactBinding, expected: {
node: string;
lane: string;
namespace: string;
pipeline: string;
pipelineRunPrefix: string;
serviceAccount: string;
sourceBranch: string;
gitopsBranch: string;
}): void {
const observed = {
node: binding.consumer.node,
lane: binding.consumer.lane,
namespace: binding.consumer.namespace,
pipeline: binding.consumer.pipeline,
pipelineRunPrefix: binding.consumer.pipelineRunPrefix,
serviceAccount: requiredParam(binding, "service_account"),
sourceBranch: requiredParam(binding, "source_branch"),
gitopsBranch: requiredParam(binding, "gitops_branch"),
};
for (const key of Object.keys(expected) as Array<keyof typeof expected>) {
if (observed[key] !== expected[key]) throw new Error(`PaC ${binding.consumer.id} ${key}=${observed[key]} does not match owning renderer ${expected[key]}`);
}
if (requiredParam(binding, "pipeline_name") !== expected.pipeline) throw new Error(`PaC ${binding.consumer.id} repository pipeline_name does not match ${expected.pipeline}`);
if (requiredParam(binding, "pipeline_run_prefix") !== expected.pipelineRunPrefix) throw new Error(`PaC ${binding.consumer.id} repository pipeline_run_prefix does not match ${expected.pipelineRunPrefix}`);
if (requiredParam(binding, "node") !== expected.node) throw new Error(`PaC ${binding.consumer.id} repository node does not match ${expected.node}`);
}
function assertPipelineIdentity(pipeline: Record<string, unknown>, binding: PacSourceArtifactBinding): void {
if (pipeline.apiVersion !== "tekton.dev/v1" || pipeline.kind !== "Pipeline") throw new Error("domain renderer must return tekton.dev/v1 Pipeline");
const metadata = requiredRecord(pipeline.metadata, "rendered Pipeline.metadata");
if (metadata.name !== binding.consumer.pipeline) throw new Error(`rendered Pipeline name ${String(metadata.name)} does not match consumer ${binding.consumer.pipeline}`);
if (metadata.namespace !== binding.consumer.namespace) throw new Error(`rendered Pipeline namespace ${String(metadata.namespace)} does not match consumer ${binding.consumer.namespace}`);
}
export function assertHwlabPipelineContracts(spec: Record<string, unknown>): void {
const text = JSON.stringify(spec);
const required = [
"unidesk-runtime-gitops-postprocess",
"unidesk-runtime-gitops-verify",
"HWLAB_WORKBENCH_KAFKA_REFRESH_REPLAY_ENABLED",
"HWLAB_WORKBENCH_KAFKA_REFRESH_REPLAY_GROUP_PREFIX",
"HWLAB_WORKBENCH_KAFKA_REFRESH_REPLAY_TIMEOUT_MS",
"HWLAB_WORKBENCH_KAFKA_REFRESH_REPLAY_SCAN_LIMIT",
"HWLAB_WORKBENCH_KAFKA_REFRESH_REPLAY_MATCHED_EVENT_LIMIT",
"HWLAB_WORKBENCH_KAFKA_REFRESH_REPLAY_LIVE_BUFFER_LIMIT",
];
const missing = required.filter((value) => !text.includes(value));
if (missing.length > 0) throw new Error(`HWLAB domain renderer missing required postprocess/verify refresh contract: ${missing.join(", ")}`);
}
function provenanceFromManifest(manifest: Record<string, unknown>): PacSourceArtifactProvenance | null {
const metadata = isRecord(manifest.metadata) ? manifest.metadata : {};
const annotations = isRecord(metadata.annotations) ? metadata.annotations : {};
const configRef = annotations[provenanceKeys.configRef];
const effectiveConfigSha256 = annotations[provenanceKeys.effectiveConfigSha256];
const renderer = annotations[provenanceKeys.renderer];
if (typeof configRef !== "string" || typeof effectiveConfigSha256 !== "string" || (renderer !== "agentrun-control-plane" && renderer !== "hwlab-runtime-lane")) return null;
return { configRef, effectiveConfigSha256, renderer };
}
function provenanceEquals(actual: PacSourceArtifactProvenance | null, expected: PacSourceArtifactProvenance): boolean {
return actual !== null
&& actual.configRef === expected.configRef
&& actual.effectiveConfigSha256 === expected.effectiveConfigSha256
&& actual.renderer === expected.renderer;
}
function firstCanonicalDrift(expected: unknown, actual: unknown, path: string): PacSourceArtifactDrift | null {
if (Object.is(expected, actual)) return null;
if (Array.isArray(expected) || Array.isArray(actual)) {
if (!Array.isArray(expected) || !Array.isArray(actual)) return drift(path, expected, actual);
if (expected.length !== actual.length) return drift(`${path}.length`, expected.length, actual.length);
for (let index = 0; index < expected.length; index += 1) {
const child = firstCanonicalDrift(expected[index], actual[index], `${path}[${index}]`);
if (child !== null) return child;
}
return null;
}
if (isRecord(expected) || isRecord(actual)) {
if (!isRecord(expected) || !isRecord(actual)) return drift(path, expected, actual);
const keys = [...new Set([...Object.keys(expected), ...Object.keys(actual)])].sort();
for (const key of keys) {
if (!Object.prototype.hasOwnProperty.call(expected, key) || !Object.prototype.hasOwnProperty.call(actual, key)) return drift(`${path}.${key}`, expected[key], actual[key]);
const child = firstCanonicalDrift(expected[key], actual[key], `${path}.${key}`);
if (child !== null) return child;
}
return null;
}
return drift(path, expected, actual);
}
function isProvenTektonAdmissionDefault(path: readonly (string | number)[], value: unknown): boolean {
const normalized = path.map((segment) => typeof segment === "number" ? "*" : segment).join(".");
const emptyRecord = isRecord(value) && Object.keys(value).length === 0;
const atTaskSpec = (suffix: string): boolean => [
`tasks.*.taskSpec.${suffix}`,
`spec.tasks.*.taskSpec.${suffix}`,
`spec.pipelineSpec.tasks.*.taskSpec.${suffix}`,
].includes(normalized);
if (atTaskSpec("metadata")) return emptyRecord;
if (atTaskSpec("spec")) return value === null;
if (atTaskSpec("params.*.type")) return value === "string";
if (atTaskSpec("results.*.type")) return value === "string";
if (atTaskSpec("steps.*.computeResources")) return emptyRecord;
if (atTaskSpec("sidecars.*.computeResources")) return emptyRecord;
return false;
}
function drift(path: string, expected: unknown, actual: unknown): PacSourceArtifactDrift {
return { path, expected: compactValue(expected), actual: compactValue(actual) };
}
function compactValue(value: unknown): string {
const raw = JSON.stringify(value);
if (raw === undefined) return String(value);
return raw.length <= 160 ? raw : `${raw.slice(0, 157)}...`;
}
function unavailableRuntimeObservation(): PacSourceArtifactRuntimeObservation {
const item: PacSourceArtifactRuntimeItem = { status: "unavailable", name: null, canonicalSha256: null, firstDrift: null, provenanceAligned: false, configRef: null, effectiveConfigSha256: null, sourceCommit: null, reason: "runtime-observer-not-configured", candidateCount: 0 };
return { live: item, embedded: item };
}
function parseYamlRecord(text: string, label: string): Record<string, unknown> {
const parsed = Bun.YAML.parse(text) as unknown;
if (!isRecord(parsed)) throw new Error(`${label} must contain one YAML object`);
return parsed;
}
function yaml(value: Record<string, unknown>): string {
return `${Bun.YAML.stringify(value).trim()}\n`;
}
function requiredParam(binding: PacSourceArtifactBinding, key: string): string {
return requiredString(binding.repository.params[key], `PaC repository ${binding.repository.id} params.${key}`);
}
function requiredString(value: unknown, label: string): string {
if (typeof value !== "string" || value.length === 0) throw new Error(`${label} must be a non-empty string`);
return value;
}
function requiredRecord(value: unknown, label: string): Record<string, unknown> {
if (!isRecord(value)) throw new Error(`${label} must be an object`);
return value;
}
function arrayRecords(value: unknown, label: string): Record<string, unknown>[] {
if (!Array.isArray(value) || value.some((item) => !isRecord(item))) throw new Error(`${label} must be an array of objects`);
return value as Record<string, unknown>[];
}
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null && !Array.isArray(value);
}
function record(value: unknown): Record<string, unknown> {
return isRecord(value) ? value : {};
}
function isSourceArtifactAction(value: string | undefined): value is PacSourceArtifactAction {
return value === "plan" || value === "check" || value === "write" || value === "status" || value === "verify-runtime";
}
function git(worktree: string, args: readonly string[]): string {
const result = spawnSync("git", ["-C", worktree, ...args], { encoding: "utf8", timeout: 15_000, maxBuffer: 1024 * 1024 });
if (result.status !== 0) throw new Error(`git ${args.join(" ")} failed for ${worktree}: ${(result.stderr || result.stdout).trim().slice(0, 1000)}`);
return result.stdout.trim();
}
function runChecked(command: string, args: readonly string[], cwd: string, timeout: number, label: string): void {
const result = spawnSync(command, [...args], { cwd, encoding: "utf8", timeout, maxBuffer: 32 * 1024 * 1024 });
if (result.status !== 0) throw new Error(`${label} failed: ${(result.stderr || result.stdout).trim().slice(-4000)}`);
}
function shellQuote(value: string): string {
return `'${value.replaceAll("'", `'"'"'`)}'`;
}
function text(value: unknown): string {
if (value === null || value === undefined || value === "") return "-";
return String(value);
}
function shortSha(value: unknown): string {
return typeof value === "string" && value.length > 0 ? value.replace(/^sha256:/u, "").slice(0, 12) : "-";
}