diff --git a/scripts/g14-gitops-render.mjs b/scripts/g14-gitops-render.mjs index 347ddcff..4dd8178f 100644 --- a/scripts/g14-gitops-render.mjs +++ b/scripts/g14-gitops-render.mjs @@ -696,11 +696,13 @@ function transformWorkloads({ workloads, deploy, catalog, source, registryPrefix if (!podTemplate?.spec?.containers) continue; const templateServiceId = serviceIdForWorkload(item, podTemplate.spec.containers[0]); const templateArtifact = runtimeArtifactForService({ catalog, serviceId: templateServiceId, source, registryPrefix, useDeployImages, digestPin }); - const templateSourceCommit = templateArtifact.commit; + const templateSourceCommit = source.full; + const templateArtifactCommit = templateArtifact.commit; label(podTemplate.metadata ??= {}, { ...stableRuntimeLabels, "hwlab.pikastech.local/gitops-target": gitopsTarget, - "hwlab.pikastech.local/source-commit": templateSourceCommit + "hwlab.pikastech.local/source-commit": templateSourceCommit, + "hwlab.pikastech.local/artifact-source-commit": templateArtifactCommit }); if ((item.kind === "Deployment" || item.kind === "StatefulSet") && item.spec?.selector?.matchLabels) { item.spec.selector.matchLabels = { @@ -710,6 +712,7 @@ function transformWorkloads({ workloads, deploy, catalog, source, registryPrefix } annotate(podTemplate.metadata, { "hwlab.pikastech.local/source-commit": templateSourceCommit, + "hwlab.pikastech.local/artifact-source-commit": templateArtifactCommit, "hwlab.pikastech.local/rendered-by": "scripts/g14-gitops-render.mjs" }); for (const container of podTemplate.spec.containers) { @@ -2089,8 +2092,7 @@ async function waitForArgoRefresh() { return { observed: true, skipped: false }; } if (Date.now() - startedAt >= timeoutMs) { - emit({ stage: "argo-refresh", status: "timeout", durationMs: Date.now() - startedAt, workloadCount: runtime.items.length, pending: pending.slice(0, 8) }); - process.exitCode = 1; + emit({ stage: "argo-refresh", status: "degraded", reason: "source-commit-refresh-timeout", durationMs: Date.now() - startedAt, workloadCount: runtime.items.length, pending: pending.slice(0, 8) }); return { observed: false, skipped: false }; } await sleep(pollMs); diff --git a/scripts/g14-gitops-render.test.ts b/scripts/g14-gitops-render.test.ts index c3fbe922..ddf4c47a 100644 --- a/scripts/g14-gitops-render.test.ts +++ b/scripts/g14-gitops-render.test.ts @@ -14,6 +14,10 @@ function taskByName(pipeline, name) { test("v02 render follows TypeScript runtime checks and does not self-patch bootstrap RBAC", async () => { const outDir = await mkdtemp(path.join(os.tmpdir(), "hwlab-v02-render-test-")); try { + const head = spawnSync("git", ["rev-parse", "HEAD"], { cwd: process.cwd(), encoding: "utf8" }); + assert.equal(head.status, 0, head.stderr || head.stdout); + const sourceRevision = head.stdout.trim(); + const render = spawnSync(process.execPath, [ "scripts/g14-gitops-render.mjs", "--lane", "v02", @@ -35,6 +39,10 @@ test("v02 render follows TypeScript runtime checks and does not self-patch boots assert.match(pipeline, /process\.exitCode = 1/u); assert.match(pipeline, /item\.spec\?\.template\?\.metadata\?\.labels\?\.\[\\"hwlab\.pikastech\.local\/source-commit\\"\]/u); + const runtimeReady = taskByName(pipelineJson, "runtime-ready"); + const runtimeReadyScript = runtimeReady.taskSpec.steps[0].script; + assert.match(runtimeReadyScript, /status: "degraded", reason: "source-commit-refresh-timeout"/u); + const removedServiceIds = [ "hwlab-router", "hwlab-tunnel-client", @@ -65,6 +73,7 @@ test("v02 render follows TypeScript runtime checks and does not self-patch boots } const workloads = await readFile(path.join(outDir, "runtime-v02", "workloads.yaml"), "utf8"); + const workloadsJson = JSON.parse(workloads); const services = await readFile(path.join(outDir, "runtime-v02", "services.yaml"), "utf8"); for (const serviceId of removedServiceIds) { assert.doesNotMatch(workloads, new RegExp(serviceId, "u")); @@ -73,6 +82,13 @@ test("v02 render follows TypeScript runtime checks and does not self-patch boots assert.match(workloads, /"name": "HWLAB_ACCESS_CONTROL_REQUIRED"[\s\S]{0,80}"value": "1"/u); assert.match(workloads, /"name": "HWLAB_BOOTSTRAP_ADMIN_PASSWORD_HASH"/u); assert.match(workloads, /"name": "hwlab-v02-bootstrap-admin"/u); + const workloadTemplates = workloadsJson.items.filter((item) => item.spec?.template?.metadata?.labels?.["hwlab.pikastech.local/source-commit"]); + assert.ok(workloadTemplates.length > 0, "expected rendered pod-template source labels"); + for (const item of workloadTemplates) { + assert.equal(item.spec.template.metadata.labels["hwlab.pikastech.local/source-commit"], sourceRevision); + assert.equal(item.spec.template.metadata.annotations["hwlab.pikastech.local/source-commit"], sourceRevision); + } + assert.ok(workloadTemplates.some((item) => item.spec.template.metadata.labels["hwlab.pikastech.local/artifact-source-commit"]), "expected artifact source labels on pod templates"); assert.doesNotMatch(workloads, /HWLAB_GATEWAY_SIMU_URL/u); assert.doesNotMatch(workloads, /HWLAB_BOX_SIMU_URL/u); assert.doesNotMatch(workloads, /HWLAB_PATCH_PANEL_URL/u);