import { createHash } from "node:crypto"; import { execFileSync } from "node:child_process"; import { readFileSync } from "node:fs"; 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", }; const sourceCommitKeys = [ "pipelinesascode.tekton.dev/sha", "pipelinesascode.tekton.dev/commit", "agentrun.pikastech.local/source-commit", "unidesk.ai/source-commit", "hwlab.pikastech.local/source-commit", ]; const knownDriftPathKeys = new Set([ "accessModes", "annotations", "apiVersion", "args", "command", "computeResources", "default", "description", "dnsPolicy", "env", "envFrom", "executionMode", "finally", "fsGroup", "generateName", "hostNetwork", "image", "kind", "labels", "length", "metadata", "mode", "mountPath", "name", "namespace", "onError", "operator", "optional", "params", "pipeline", "pipelineRef", "pipelineSpec", "podTemplate", "readinessProbe", "readOnly", "requests", "resources", "results", "retries", "runAfter", "script", "secret", "secretName", "securityContext", "serviceAccountName", "sidecars", "spec", "steps", "storage", "subPath", "taskRunTemplate", "taskSpec", "tasks", "timeout", "timeouts", "type", "value", "values", "volumeClaimTemplate", "volumeMounts", "volumes", "when", "workspaces", "workingDir", "wrapper", ]); function record(value) { return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {}; } function stableValue(value) { if (Array.isArray(value)) return value.map(stableValue); if (value !== null && typeof value === "object") { return Object.fromEntries(Object.keys(value).sort().map((key) => [key, stableValue(value[key])])); } if (value === undefined) return { __type: "undefined" }; if (typeof value === "bigint") return { __type: "bigint", value: String(value) }; return value; } function valueEvidence(value) { const type = value === null ? "null" : Array.isArray(value) ? "array" : typeof value; const serialized = JSON.stringify(stableValue(value)); const text = serialized === undefined ? String(value) : serialized; const length = typeof value === "string" ? Buffer.byteLength(value, "utf8") : Array.isArray(value) ? value.length : value !== null && typeof value === "object" ? Object.keys(value).length : Buffer.byteLength(text, "utf8"); const sha256 = createHash("sha256").update(text).digest("hex"); return `type=${type},length=${length},sha256=${sha256}`; } function safeRuntimeReason(reason) { const text = typeof reason === "string" ? reason : String(reason); const fixed = new Set([ "source-commit-not-specified", "source-commit-run-not-found", "source-commit-identity-conflict", "pipeline-get-not-found", "pipelinerun-list-not-found", "pipelinerun-list-invalid-shape", ]); if (fixed.has(text)) return text; if (/^source-commit-run-conflict:\d+$/u.test(text)) return text; if (/^(?:pipeline-get|pipelinerun-list)-command-failed:(?:unknown|\d+):type=string,length=\d+,sha256=[0-9a-f]{64}$/u.test(text)) return text; if (/^runtime-observer-input-error:type=string,length=\d+,sha256=[0-9a-f]{64}$/u.test(text)) return text; return `runtime-observer-reason:${valueEvidence(text)}`; } function unavailable(reason, sourceCommit = null, candidateCount = 0) { return { status: "unavailable", name: null, canonicalSha256: null, firstDrift: null, provenanceAligned: false, configRef: null, effectiveConfigSha256: null, sourceCommit, reason: safeRuntimeReason(reason), candidateCount, }; } function missing(reason, sourceCommit = null) { return { ...unavailable(reason, sourceCommit, 0), status: "missing" }; } function isAdmissionDefault(path, value) { const normalized = path.map((segment) => typeof segment === "number" ? "*" : segment).join("."); const emptyRecord = value !== null && typeof value === "object" && !Array.isArray(value) && Object.keys(value).length === 0; const atTaskSpec = (suffix) => [ `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; } export function canonicalizePacPipelineSpec(value, path = []) { if (Array.isArray(value)) return value.map((item, index) => canonicalizePacPipelineSpec(item, [...path, index])); if (value === null || typeof value !== "object") return value; const output = {}; for (const key of Object.keys(value).sort()) { const child = value[key]; const childPath = [...path, key]; if (isAdmissionDefault(childPath, child)) continue; output[key] = canonicalizePacPipelineSpec(child, childPath); } return output; } export function canonicalPacPipelineSha256(value) { return `sha256:${createHash("sha256").update(JSON.stringify(canonicalizePacPipelineSpec(value))).digest("hex")}`; } function firstCanonicalDrift(expected, actual, path = "$") { if (Object.is(expected, actual)) return null; if (Array.isArray(expected) || Array.isArray(actual)) { if (!Array.isArray(expected) || !Array.isArray(actual)) return { path, expected: valueEvidence(expected), actual: valueEvidence(actual) }; if (expected.length !== actual.length) return { path: `${path}.length`, expected: valueEvidence(expected.length), actual: valueEvidence(actual.length) }; for (let index = 0; index < expected.length; index += 1) { const drift = firstCanonicalDrift(expected[index], actual[index], `${path}[${index}]`); if (drift !== null) return drift; } return null; } const expectedRecord = expected !== null && typeof expected === "object"; const actualRecord = actual !== null && typeof actual === "object"; if (expectedRecord || actualRecord) { if (!expectedRecord || !actualRecord) return { path, expected: valueEvidence(expected), actual: valueEvidence(actual) }; const keys = [...new Set([...Object.keys(expected), ...Object.keys(actual)])].sort(); for (const key of keys) { const expectedOwnsKey = Object.prototype.hasOwnProperty.call(expected, key); const actualOwnsKey = Object.prototype.hasOwnProperty.call(actual, key); const childPath = knownDriftPathKeys.has(key) ? `${path}.${key}` : `${path}.[key:${valueEvidence(key)}]`; if (!expectedOwnsKey || !actualOwnsKey) { return { path: childPath, expected: valueEvidence(expected[key]), actual: valueEvidence(actual[key]) }; } const drift = firstCanonicalDrift(expected[key], actual[key], childPath); if (drift !== null) return drift; } return null; } return { path, expected: valueEvidence(expected), actual: valueEvidence(actual) }; } function firstDrift(expected, actual) { return firstCanonicalDrift(canonicalizePacPipelineSpec(expected), canonicalizePacPipelineSpec(actual)); } function kubectlJson(args, label) { try { return { kind: "ok", value: JSON.parse(execFileSync("kubectl", args, { encoding: "utf8", timeout: 30_000, maxBuffer: 64 * 1024 * 1024 })), }; } catch (error) { const stderr = typeof error?.stderr === "string" ? error.stderr : Buffer.isBuffer(error?.stderr) ? error.stderr.toString("utf8") : ""; const stdout = typeof error?.stdout === "string" ? error.stdout : Buffer.isBuffer(error?.stdout) ? error.stdout.toString("utf8") : ""; const message = stderr.trim().split(/\r?\n/u)[0] || stdout.trim().split(/\r?\n/u)[0] || String(error?.message ?? "command failed"); if (/\bNotFound\b|\bnot found\b/u.test(message)) return { kind: "not-found", reason: `${label}-not-found` }; const status = Number.isInteger(error?.status) ? error.status : "unknown"; return { kind: "unavailable", reason: `${label}-command-failed:${status}:${valueEvidence(message)}` }; } } function manifestProvenance(manifest) { const annotations = record(record(manifest?.metadata).annotations); return { configRef: typeof annotations[provenanceKeys.configRef] === "string" ? annotations[provenanceKeys.configRef] : null, effectiveConfigSha256: typeof annotations[provenanceKeys.effectiveConfigSha256] === "string" ? annotations[provenanceKeys.effectiveConfigSha256] : null, renderer: typeof annotations[provenanceKeys.renderer] === "string" ? annotations[provenanceKeys.renderer] : null, mode: typeof annotations[provenanceKeys.mode] === "string" ? annotations[provenanceKeys.mode] : null, }; } function manifestSourceCommits(manifest) { const metadata = record(manifest?.metadata); const labels = record(metadata.labels); const annotations = record(metadata.annotations); return [...new Set(sourceCommitKeys.flatMap((key) => [labels[key], annotations[key]]) .filter((value) => typeof value === "string" && /^[0-9a-f]{40}$/iu.test(value)) .map((value) => value.toLowerCase()))]; } function safeManifestName(value) { return typeof value === "string" && value.length <= 253 && /^[a-z0-9](?:[-a-z0-9.]*[a-z0-9])?$/u.test(value) ? value : null; } function safeAlignedConfigRef(observed, expected) { return observed === expected && typeof expected === "string" && expected.length <= 512 && /^[A-Za-z0-9_./-]+#[A-Za-z0-9_.-]+$/u.test(expected) ? expected : null; } function safeAlignedSha256(observed, expected) { return observed === expected && typeof expected === "string" && /^sha256:[0-9a-f]{64}$/u.test(expected) ? expected : null; } function pipelineRunWrapper(manifest) { const spec = record(manifest?.spec); const hasPipelineSpec = Object.prototype.hasOwnProperty.call(spec, "pipelineSpec"); const hasPipelineRef = Object.prototype.hasOwnProperty.call(spec, "pipelineRef"); const observedMode = manifestProvenance(manifest).mode; const mode = observedMode === "embedded-pipeline-spec" || observedMode === "remote-pipeline-annotation" ? observedMode : "invalid"; const executionMode = hasPipelineSpec && !hasPipelineRef ? "pipeline-spec" : "invalid"; const wrapperSpec = { ...spec }; delete wrapperSpec.pipelineSpec; delete wrapperSpec.pipelineRef; return { mode, executionMode, spec: wrapperSpec }; } function observedItem(input, manifest, spec, sourceCommit = null, candidateCount = 0, wrapper = null) { const observedProvenance = manifestProvenance(manifest); const provenanceAligned = observedProvenance.configRef === input.provenance.configRef && observedProvenance.effectiveConfigSha256 === input.provenance.effectiveConfigSha256 && observedProvenance.renderer === input.provenance.renderer && observedProvenance.mode === input.provenance.mode; const comparePipelineSpec = wrapper === null || wrapper.executionMode === "pipeline-spec"; const specDrift = comparePipelineSpec ? firstDrift(input.desiredSpec, spec) : null; const wrapperDrift = wrapper === null || input.desiredWrapper === null ? null : firstDrift(input.desiredWrapper, wrapper); const drift = specDrift ?? wrapperDrift; return { status: drift === null ? "aligned" : "drifted", name: safeManifestName(record(manifest.metadata).name), canonicalSha256: canonicalPacPipelineSha256( wrapper === null ? spec : wrapper.executionMode === "pipeline-spec" ? { pipelineSpec: spec, wrapper } : { wrapper }, ), firstDrift: drift, provenanceAligned, configRef: safeAlignedConfigRef(observedProvenance.configRef, input.provenance.configRef), effectiveConfigSha256: safeAlignedSha256(observedProvenance.effectiveConfigSha256, input.provenance.effectiveConfigSha256), renderer: observedProvenance.renderer === input.provenance.renderer ? input.provenance.renderer : null, mode: observedProvenance.mode === input.provenance.mode ? input.provenance.mode : null, sourceCommit, reason: null, candidateCount, }; } export function selectPipelineRunBySourceCommit(items, pipelineRunPrefix, sourceCommit) { if (sourceCommit === null) return { kind: "unavailable", reason: "source-commit-not-specified", run: null, candidates: [] }; const candidates = items.filter((run) => { const name = String(record(run?.metadata).name ?? ""); return name.startsWith(pipelineRunPrefix) && manifestSourceCommits(run).includes(sourceCommit); }); if (candidates.length === 0) return { kind: "missing", reason: "source-commit-run-not-found", run: null, candidates: [] }; if (candidates.some((run) => { const commits = manifestSourceCommits(run); return commits.length !== 1 || commits[0] !== sourceCommit; })) return { kind: "unavailable", reason: "source-commit-identity-conflict", run: null, candidates }; const ordered = [...candidates].sort((left, right) => { const time = String(record(right?.metadata).creationTimestamp ?? "").localeCompare(String(record(left?.metadata).creationTimestamp ?? "")); return time !== 0 ? time : String(record(right?.metadata).name ?? "").localeCompare(String(record(left?.metadata).name ?? "")); }); return { kind: "ok", reason: null, run: ordered[0], candidates: ordered }; } export function observePacSourceArtifactRuntime(input, readKubectlJson = kubectlJson) { const liveResult = readKubectlJson(["get", "pipeline", input.pipeline, "-n", input.namespace, "-o", "json"], "pipeline-get"); const live = liveResult.kind === "ok" ? observedItem(input, liveResult.value, record(liveResult.value.spec)) : liveResult.kind === "not-found" ? missing(liveResult.reason) : unavailable(liveResult.reason); let embedded; if (input.sourceCommit === null) { embedded = unavailable("source-commit-not-specified"); } else { const runsResult = readKubectlJson(["get", "pipelinerun", "-n", input.namespace, "-o", "json"], "pipelinerun-list"); if (runsResult.kind !== "ok") { embedded = runsResult.kind === "not-found" ? missing(runsResult.reason, input.sourceCommit) : unavailable(runsResult.reason, input.sourceCommit); } else { const items = Array.isArray(runsResult.value?.items) ? runsResult.value.items : null; if (items === null) { embedded = unavailable("pipelinerun-list-invalid-shape", input.sourceCommit); } else { const selected = selectPipelineRunBySourceCommit(items, input.pipelineRunPrefix, input.sourceCommit); if (selected.kind === "ok") { const signatures = selected.candidates.map((run) => JSON.stringify({ canonicalSha256: (() => { const wrapper = pipelineRunWrapper(run); return canonicalPacPipelineSha256(wrapper.executionMode === "pipeline-spec" ? { pipelineSpec: record(record(run.spec).pipelineSpec), wrapper } : { wrapper }); })(), provenance: manifestProvenance(run), })); embedded = new Set(signatures).size === 1 ? observedItem( input, selected.run, record(record(selected.run.spec).pipelineSpec), input.sourceCommit, selected.candidates.length, pipelineRunWrapper(selected.run), ) : unavailable(`source-commit-run-conflict:${selected.candidates.length}`, input.sourceCommit, selected.candidates.length); } else { embedded = selected.kind === "missing" ? missing(selected.reason, input.sourceCommit) : unavailable(selected.reason, input.sourceCommit, selected.candidates.length); } } } } return { live, embedded }; } if (typeof process.env.PAC_SOURCE_ARTIFACT_INPUT_FILE === "string") { try { const inputText = readFileSync(process.env.PAC_SOURCE_ARTIFACT_INPUT_FILE, "utf8"); const input = JSON.parse(inputText); process.stdout.write(JSON.stringify(observePacSourceArtifactRuntime(input))); } catch (error) { const evidence = valueEvidence(String(error?.message ?? error)); process.stdout.write(JSON.stringify({ live: unavailable(`runtime-observer-input-error:${evidence}`), embedded: unavailable(`runtime-observer-input-error:${evidence}`), })); } }