From a385c2ad0bf58dd91fe0bf8f40ba397a1d65bdf3 Mon Sep 17 00:00:00 2001 From: Codex Date: Fri, 29 May 2026 02:31:09 +0800 Subject: [PATCH] fix: unblock v02 gitops ts reconciler --- scripts/g14-gitops-render.mjs | 14 +++++++++++++ scripts/g14-gitops-render.test.ts | 33 +++++++++++++++++++++++++++++++ scripts/src/check-plan.mjs | 1 + 3 files changed, 48 insertions(+) create mode 100644 scripts/g14-gitops-render.test.ts diff --git a/scripts/g14-gitops-render.mjs b/scripts/g14-gitops-render.mjs index 639706a6..0185aeac 100644 --- a/scripts/g14-gitops-render.mjs +++ b/scripts/g14-gitops-render.mjs @@ -1320,6 +1320,15 @@ function apiPath(item) { return "/apis/" + encode(parts[0]) + "/" + encode(parts[1]) + "/namespaces/" + encode(ns) + "/" + plural + "/" + encode(name); } +function reconcilePolicy(item) { + const ns = item.metadata?.namespace || namespace; + const crossNamespaceRbac = (item.kind === "Role" || item.kind === "RoleBinding") && ns !== namespace; + if (crossNamespaceRbac && process.env.HWLAB_RECONCILE_CROSS_NAMESPACE_RBAC !== "1") { + return { action: "skip", reason: "cross-namespace-rbac-bootstrap-managed", namespace: ns }; + } + return { action: "apply", namespace: ns }; +} + function request(method, path, body) { const payload = body ? JSON.stringify(body) : ""; const headers = { Authorization: "Bearer " + token }; @@ -1344,6 +1353,11 @@ function request(method, path, body) { const results = []; for (const file of manifestFiles) { for (const item of itemsFrom(file)) { + const policy = reconcilePolicy(item); + if (policy.action === "skip") { + results.push({ file, kind: item.kind, name: item.metadata?.name || null, namespace: policy.namespace, status: "skipped", reason: policy.reason }); + continue; + } const path = apiPath(item); if (!path) { results.push({ file, kind: item.kind, name: item.metadata?.name || null, status: "skipped-cluster-scoped" }); diff --git a/scripts/g14-gitops-render.test.ts b/scripts/g14-gitops-render.test.ts new file mode 100644 index 00000000..c54dd30e --- /dev/null +++ b/scripts/g14-gitops-render.test.ts @@ -0,0 +1,33 @@ +import assert from "node:assert/strict"; +import { spawnSync } from "node:child_process"; +import { mkdtemp, readFile, rm } from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; +import test from "node:test"; + +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 render = spawnSync(process.execPath, [ + "scripts/g14-gitops-render.mjs", + "--lane", "v02", + "--catalog-path", "deploy/artifact-catalog.v02.json", + "--image-tag-mode", "full", + "--source-branch", "v0.2", + "--gitops-branch", "v0.2-gitops", + "--out", outDir, + "--source-revision", "HEAD" + ], { cwd: process.cwd(), encoding: "utf8" }); + assert.equal(render.status, 0, render.stderr || render.stdout); + + const pipeline = await readFile(path.join(outDir, "tekton-v02", "pipeline.yaml"), "utf8"); + assert.match(pipeline, /bun test cmd\/hwlab-codex-api-responses-forwarder\/main\.test\.ts/u); + assert.doesNotMatch(pipeline, /cmd\/hwlab-codex-api-responses-forwarder\/main\.mjs/u); + + const reconciler = await readFile(path.join(outDir, "tekton-v02", "control-plane-reconciler.yaml"), "utf8"); + assert.match(reconciler, /cross-namespace-rbac-bootstrap-managed/u); + assert.match(reconciler, /HWLAB_RECONCILE_CROSS_NAMESPACE_RBAC/u); + } finally { + await rm(outDir, { recursive: true, force: true }); + } +}); diff --git a/scripts/src/check-plan.mjs b/scripts/src/check-plan.mjs index 5e11e3f9..a76de297 100644 --- a/scripts/src/check-plan.mjs +++ b/scripts/src/check-plan.mjs @@ -110,6 +110,7 @@ export const checkProfiles = Object.freeze({ { id: "check-091-deploy-refresh-artifact-catalog-test", group: "deploy", command: ["node","--check","scripts/refresh-artifact-catalog.test.mjs"] }, { id: "check-092-artifact-artifact-publish", group: "artifact", command: ["node","--check","scripts/artifact-publish.mjs"] }, { id: "check-093-artifact-g14-artifact-publish", group: "artifact", command: ["node","--check","scripts/g14-artifact-publish.mjs"] }, + { id: "check-093a-artifact-g14-gitops-render-test", group: "artifact", command: ["node","scripts/run-bun.mjs","test","scripts/g14-gitops-render.test.ts"] }, { id: "check-094-artifact-dev-runtime-base-image", group: "artifact", command: ["node","--check","scripts/dev-runtime-base-image.mjs"] }, { id: "check-095-artifact-dev-artifact-services", group: "artifact", command: ["node","--check","scripts/src/dev-artifact-services.mjs"] }, { id: "check-096-artifact-registry-capabilities", group: "artifact", command: ["node","--check","scripts/src/registry-capabilities.mjs"] },