fix: 统一 HWLAB PaC Pipeline provenance 合同

This commit is contained in:
Codex
2026-07-11 08:17:50 +02:00
parent 764ff398c3
commit 2f93a44115
13 changed files with 362 additions and 65 deletions
@@ -0,0 +1,29 @@
import { type HwlabRuntimeLaneSpec, hwlabRuntimeLaneConfigPath } from "../hwlab-node-lanes";
import { pipelineProvenanceAnnotations, type PipelineProvenanceContract } from "../pipeline-provenance";
import { stableJsonSha256 } from "../stable-json";
export interface HwlabRuntimePipelineProvenance extends PipelineProvenanceContract {
readonly renderer: "hwlab-runtime-lane";
readonly mode: "remote-pipeline-annotation";
}
export function hwlabRuntimePipelineProvenance(spec: HwlabRuntimeLaneSpec): HwlabRuntimePipelineProvenance {
const declared = spec.pipelineProvenance;
if (declared === undefined) {
throw new Error(`${hwlabRuntimeLaneConfigPath()}#lanes.${spec.lane}.targets.${spec.nodeId}.pipelineProvenance is required`);
}
const expectedConfigRef = `${hwlabRuntimeLaneConfigPath()}#lanes.${spec.lane}.targets.${spec.nodeId}`;
if (declared.configRef !== expectedConfigRef) {
throw new Error(`pipelineProvenance.configRef must equal selected target owner ${expectedConfigRef}`);
}
return {
configRef: declared.configRef,
effectiveConfigSha256: stableJsonSha256(spec),
renderer: declared.renderer,
mode: declared.mode,
};
}
export function hwlabRuntimePipelineProvenanceAnnotations(spec: HwlabRuntimeLaneSpec): Record<string, string> {
return pipelineProvenanceAnnotations(hwlabRuntimePipelineProvenance(spec));
}
+18 -5
View File
@@ -41,11 +41,13 @@ import { webObserveShort, webObserveText } from "./web-probe-observe";
import { readNodeRuntimeStatusPolicy, runNodeRuntimeStatusProbe, skippedNodeRuntimeStatusCommand, type NodeRuntimeStatusPolicy, type NodeRuntimeStatusWorkerEvent } from "./control-plane-status-observed";
import { hwlabRuntimeActiveExternalPostgres } from "../hwlab-node-lanes";
import { nodeRuntimeDeployYamlOverlayShellScript } from "./deploy-overlay";
import { pipelineProvenanceAnnotationKeys } from "../pipeline-provenance";
const runtimeGitopsObservabilityNativeScript = readFileSync(rootPath("scripts/native/hwlab/runtime-gitops-observability.mjs"), "utf8").trimEnd();
const runtimeGitopsPipelineGuardNativeScript = readFileSync(rootPath("scripts/native/hwlab/runtime-gitops-pipeline-guard.mjs"), "utf8").trimEnd();
const runtimeGitopsPostprocessNativeScript = readFileSync(rootPath("scripts/native/hwlab/runtime-gitops-postprocess.mjs"), "utf8").trimEnd();
const runtimeGitopsVerifyNativeScript = readFileSync(rootPath("scripts/native/hwlab/runtime-gitops-verify.mjs"), "utf8").trimEnd();
const runtimePipelineProvenanceNativeScript = readFileSync(rootPath("scripts/native/hwlab/runtime-pipeline-provenance.mjs"), "utf8").trimEnd();
export function nodeRuntimeGitMirrorJobName(mirror: NodeRuntimeGitMirrorTargetSpec, action: "sync" | "flush"): string {
const prefix = action === "sync" ? mirror.syncJobPrefix : mirror.flushJobPrefix;
@@ -2521,11 +2523,6 @@ export function nodeRuntimePipelinePostprocessScript(): string[] {
" doc.metadata = doc.metadata || {};",
" if (typeof overlay.pipelineName === 'string' && overlay.pipelineName.length > 0) doc.metadata.name = overlay.pipelineName;",
" doc.metadata.annotations = doc.metadata.annotations || {};",
" const provenance = overlay.sourceArtifactProvenance || {};",
" if (provenance.configRef) doc.metadata.annotations['unidesk.ai/owning-config-ref'] = provenance.configRef;",
" if (provenance.effectiveConfigSha256) doc.metadata.annotations['unidesk.ai/effective-config-sha256'] = provenance.effectiveConfigSha256;",
" if (provenance.renderer) doc.metadata.annotations['unidesk.ai/source-artifact-renderer'] = provenance.renderer;",
" if (provenance.mode) doc.metadata.annotations['unidesk.ai/source-artifact-mode'] = provenance.mode;",
" doc.metadata.annotations['hwlab.pikastech.local/download-profile'] = overlay.downloadProfileId;",
" doc.metadata.annotations['hwlab.pikastech.local/network-profile'] = overlay.networkProfileId;",
" for (const task of doc.spec?.tasks || []) {",
@@ -2836,10 +2833,26 @@ export function nodeRuntimePipelinePostprocessScript(): string[] {
"patchArgoYaml(path.join(renderDir, 'argocd', 'project.yaml'));",
"patchArgoYaml(path.join(renderDir, 'argocd', overlay.argoApplicationFile));",
"NODE",
...nodeRuntimePipelineProvenancePostprocessScript(),
...runtimeGitopsPipelineGuardScript(),
];
}
export function nodeRuntimePipelineProvenancePostprocessScript(): string[] {
const expectedKeys = Buffer.from(JSON.stringify(Object.values(pipelineProvenanceAnnotationKeys).sort()), "utf8").toString("base64");
return [
"runtime_gitops_guard_dir=\"$render_dir/.unidesk-runtime-gitops\"",
"mkdir -p \"$runtime_gitops_guard_dir\"",
...writeRuntimeGitopsNativeScript("runtime-pipeline-provenance.mjs", runtimePipelineProvenanceNativeScript, "UNIDESK_RUNTIME_PIPELINE_PROVENANCE_MJS"),
[
"UNIDESK_RUNTIME_GITOPS_OVERLAY_B64=\"$overlay_b64\"",
`UNIDESK_PIPELINE_PROVENANCE_KEYS_B64=${shellQuote(expectedKeys)}`,
"node \"$runtime_gitops_guard_dir/runtime-pipeline-provenance.mjs\"",
"--pipeline \"$render_dir/$(node -e 'const o=JSON.parse(Buffer.from(process.argv[1],\"base64\").toString(\"utf8\")); process.stdout.write(o.tektonDir)' \"$overlay_b64\")/pipeline.yaml\"",
].join(" "),
];
}
function runtimeGitopsPipelineGuardScript(): string[] {
return [
"runtime_gitops_guard_dir=\"$render_dir/.unidesk-runtime-gitops\"",
+3 -8
View File
@@ -13,7 +13,7 @@ import { runCommand, type CommandResult } from "../command";
import { startJob } from "../jobs";
import { classifySshTcpPoolFailure } from "../ssh";
import { HWLAB_NODE_CONTROL_PLANE_CONFIG_PATH, hwlabNodeControlPlaneCiGitWorkspaceSecret, hwlabNodeControlPlaneInfraHelp, runHwlabNodeControlPlaneInfra } from "../hwlab-node-control-plane";
import { hwlabRuntimeLaneConfigPath, hwlabRuntimeLaneIds, hwlabRuntimeLaneSpec, hwlabRuntimeLaneSpecForNode, hwlabRuntimeNodeIds, isHwlabRuntimeLane, type HwlabRuntimeLane, type HwlabRuntimeLaneSpec, type HwlabRuntimeObservabilityRecordingRuleSpec, type HwlabRuntimeObservabilitySpec, type HwlabRuntimeObservabilityWarningAlertSpec, type HwlabRuntimePublicExposureSpec, type HwlabRuntimeWebProbeAlertThresholdsSpec, type HwlabRuntimeWebProbeProjectManagementSpec } from "../hwlab-node-lanes";
import { hwlabRuntimeLaneIds, hwlabRuntimeLaneSpec, hwlabRuntimeLaneSpecForNode, hwlabRuntimeNodeIds, isHwlabRuntimeLane, type HwlabRuntimeLane, type HwlabRuntimeLaneSpec, type HwlabRuntimeObservabilityRecordingRuleSpec, type HwlabRuntimeObservabilitySpec, type HwlabRuntimeObservabilityWarningAlertSpec, type HwlabRuntimePublicExposureSpec, type HwlabRuntimeWebProbeAlertThresholdsSpec, type HwlabRuntimeWebProbeProjectManagementSpec } from "../hwlab-node-lanes";
import { nodeWebProbeScriptRunnerSource } from "../hwlab-node-web-probe-runner-source";
import { nodeWebObserveAnalyzerSource } from "../hwlab-node-web-observe-analyzer-source";
import { nodeWebObserveRunnerSource } from "../hwlab-node-web-observe-runner-source";
@@ -40,7 +40,7 @@ import { assertKnownOptions, nodeWebProbeAutoCommandTimeoutSeconds, normalizeNod
import { resolveNodeWebProbeCliOrigin } from "./web-probe-origin";
import { hwlabRuntimeActiveExternalPostgres } from "../hwlab-node-lanes";
import { resolveEgressProxySourceRef } from "../egress-proxy-sources";
import { stableJsonSha256 } from "../stable-json";
import { hwlabRuntimePipelineProvenanceAnnotations } from "./pipeline-provenance";
export function nodeRuntimeRenderOverlay(spec: HwlabRuntimeLaneSpec): Record<string, unknown> {
const gitSshProxy = httpProxyEndpoint(spec.networkProfile.proxy.http);
@@ -75,12 +75,7 @@ export function nodeRuntimeRenderOverlay(spec: HwlabRuntimeLaneSpec): Record<str
},
};
return {
sourceArtifactProvenance: {
configRef: `${hwlabRuntimeLaneConfigPath}#lanes.${spec.lane}.targets.${spec.nodeId}`,
effectiveConfigSha256: stableJsonSha256(spec),
renderer: "hwlab-runtime-lane",
mode: "remote-pipeline-annotation",
},
pipelineProvenanceAnnotations: hwlabRuntimePipelineProvenanceAnnotations(spec),
nodeId: spec.nodeId,
lane: spec.lane,
sourceBranch: spec.sourceBranch,