Merge pull request #1961 from pikasTech/fix/hwlab-pac-place-scripts-argmax
fix(pac): 收敛 HWLAB runtime GitOps 脚本体积
This commit is contained in:
@@ -123,6 +123,7 @@ lanes:
|
||||
configRef: config/hwlab-node-lanes.yaml#lanes.v03.targets.NC01
|
||||
renderer: hwlab-runtime-lane
|
||||
mode: remote-pipeline-annotation
|
||||
maxInlineScriptBytes: 32768
|
||||
git:
|
||||
url: git@github.com:pikasTech/HWLAB.git
|
||||
readUrl: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-HWLAB.git
|
||||
|
||||
@@ -87,14 +87,12 @@ function hasNoBuildNoRolloutEarlyExit(script) {
|
||||
}
|
||||
|
||||
function injectRuntimeGitopsCommands(script) {
|
||||
if (hasPostprocess(script) && hasVerify(script)) return script;
|
||||
const overlayEnv = `UNIDESK_RUNTIME_GITOPS_OVERLAY_B64=${shellSingle(Buffer.from(JSON.stringify({
|
||||
runtimePath: overlay.runtimePath,
|
||||
observability: overlay.observability,
|
||||
}), "utf8").toString("base64"))}`;
|
||||
const overlayEnv = "UNIDESK_RUNTIME_GITOPS_OVERLAY_FILE=/etc/unidesk-cicd-runtime-gitops/runtime-gitops-overlay.json";
|
||||
const migratedScript = String(script).replace(/UNIDESK_RUNTIME_GITOPS_OVERLAY_B64='[^'\n]*'/gu, overlayEnv);
|
||||
if (hasPostprocess(migratedScript) && hasVerify(migratedScript)) return migratedScript;
|
||||
const postprocess = `${overlayEnv} node /etc/unidesk-cicd-runtime-gitops/runtime-gitops-postprocess.mjs`;
|
||||
const verify = `${overlayEnv} node /etc/unidesk-cicd-runtime-gitops/runtime-gitops-verify.mjs`;
|
||||
return String(script).replace(
|
||||
return migratedScript.replace(
|
||||
/(node scripts\/run-bun\.mjs scripts\/gitops-render\.mjs[^\n]*--use-deploy-images[^\n]*)/g,
|
||||
(match) => {
|
||||
if (match.includes("--check")) return hasVerify(script) ? match : verify;
|
||||
@@ -127,7 +125,12 @@ function ensureRuntimeGitopsScriptsMount(taskSpec, step) {
|
||||
|
||||
function writeScriptsConfigMap(targetPath, namespace) {
|
||||
if (scriptsDir === null) throw new Error("--scripts-dir is required with --scripts-configmap");
|
||||
const data = {};
|
||||
const data = {
|
||||
"runtime-gitops-overlay.json": `${JSON.stringify({
|
||||
runtimePath: requiredOverlayString("runtimePath"),
|
||||
observability: objectOr(overlay.observability),
|
||||
})}\n`,
|
||||
};
|
||||
for (const name of ["runtime-gitops-observability.mjs", "runtime-gitops-postprocess.mjs", "runtime-gitops-verify.mjs"]) {
|
||||
data[name] = readFileSync(path.join(scriptsDir, name), "utf8");
|
||||
}
|
||||
@@ -193,10 +196,6 @@ function objectOr(value) {
|
||||
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
||||
}
|
||||
|
||||
function shellSingle(value) {
|
||||
return `'${String(value).replaceAll("'", "'\\''")}'`;
|
||||
}
|
||||
|
||||
function requireYaml() {
|
||||
try {
|
||||
return requireFromScript("yaml");
|
||||
|
||||
@@ -257,8 +257,10 @@ function listYamlFiles(root) {
|
||||
}
|
||||
|
||||
function readOverlay() {
|
||||
const file = process.env.UNIDESK_RUNTIME_GITOPS_OVERLAY_FILE;
|
||||
if (file) return JSON.parse(readFileSync(file, "utf8"));
|
||||
const encoded = process.env.UNIDESK_RUNTIME_GITOPS_OVERLAY_B64;
|
||||
if (!encoded) throw new Error("UNIDESK_RUNTIME_GITOPS_OVERLAY_B64 is required");
|
||||
if (!encoded) throw new Error("UNIDESK_RUNTIME_GITOPS_OVERLAY_FILE or UNIDESK_RUNTIME_GITOPS_OVERLAY_B64 is required");
|
||||
return JSON.parse(Buffer.from(encoded, "base64").toString("utf8"));
|
||||
}
|
||||
|
||||
|
||||
@@ -102,8 +102,10 @@ function listYamlFiles(root) {
|
||||
}
|
||||
|
||||
function readOverlay() {
|
||||
const file = process.env.UNIDESK_RUNTIME_GITOPS_OVERLAY_FILE;
|
||||
if (file) return JSON.parse(readFileSync(file, "utf8"));
|
||||
const encoded = process.env.UNIDESK_RUNTIME_GITOPS_OVERLAY_B64;
|
||||
if (!encoded) throw new Error("UNIDESK_RUNTIME_GITOPS_OVERLAY_B64 is required");
|
||||
if (!encoded) throw new Error("UNIDESK_RUNTIME_GITOPS_OVERLAY_FILE or UNIDESK_RUNTIME_GITOPS_OVERLAY_B64 is required");
|
||||
return JSON.parse(Buffer.from(encoded, "base64").toString("utf8"));
|
||||
}
|
||||
|
||||
|
||||
@@ -651,6 +651,7 @@ export interface HwlabRuntimePipelineProvenanceSpec {
|
||||
readonly configRef: string;
|
||||
readonly renderer: "hwlab-runtime-lane";
|
||||
readonly mode: "remote-pipeline-annotation";
|
||||
readonly maxInlineScriptBytes: number;
|
||||
}
|
||||
|
||||
export interface HwlabRuntimeLaneSpec {
|
||||
@@ -1083,6 +1084,7 @@ export function parseHwlabRuntimePipelineProvenance(
|
||||
configRef,
|
||||
renderer: enumStringField(raw, "renderer", path, ["hwlab-runtime-lane"]),
|
||||
mode: enumStringField(raw, "mode", path, ["remote-pipeline-annotation"]),
|
||||
maxInlineScriptBytes: boundedIntegerField(raw, "maxInlineScriptBytes", path, 1024, 131072),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -238,6 +238,80 @@ describe("HWLAB YAML-owned Pipeline provenance", () => {
|
||||
}, ownerPath, configRef)).toThrow("mode must be one of remote-pipeline-annotation");
|
||||
});
|
||||
|
||||
test("runtime GitOps guard keeps Tekton scripts below the owning YAML budget", () => {
|
||||
const temporary = mkdtempSync(resolve(tmpdir(), "unidesk-hwlab-runtime-gitops-guard-"));
|
||||
try {
|
||||
const spec = hwlabRuntimeLaneSpecForNode("v03", "NC01");
|
||||
const budget = spec.pipelineProvenance?.maxInlineScriptBytes;
|
||||
expect(budget).toBeNumber();
|
||||
const overlay = nodeRuntimeRenderOverlay(spec);
|
||||
const pipelinePath = resolve(temporary, "pipeline.yaml");
|
||||
const configMapPath = resolve(temporary, "runtime-gitops-scripts.yaml");
|
||||
const scriptsDir = rootPath("scripts", "native", "hwlab");
|
||||
writeFileSync(pipelinePath, Bun.YAML.stringify({
|
||||
apiVersion: "tekton.dev/v1",
|
||||
kind: "Pipeline",
|
||||
metadata: { name: spec.pipeline, namespace: "hwlab-ci" },
|
||||
spec: { tasks: [{
|
||||
name: "gitops-promote",
|
||||
taskSpec: { steps: [{
|
||||
name: "render",
|
||||
script: "node scripts/run-bun.mjs scripts/gitops-render.mjs --use-deploy-images\nnode scripts/run-bun.mjs scripts/gitops-render.mjs --use-deploy-images --check\n",
|
||||
}] },
|
||||
}] },
|
||||
}));
|
||||
const result = spawnSync("node", [
|
||||
rootPath("scripts", "native", "hwlab", "runtime-gitops-pipeline-guard.mjs"),
|
||||
"--pipeline", pipelinePath,
|
||||
"--scripts-configmap", configMapPath,
|
||||
"--namespace", "hwlab-ci",
|
||||
"--scripts-dir", scriptsDir,
|
||||
], {
|
||||
cwd: rootPath(),
|
||||
env: { ...process.env, UNIDESK_RUNTIME_GITOPS_OVERLAY_B64: Buffer.from(JSON.stringify(overlay), "utf8").toString("base64") },
|
||||
encoding: "utf8",
|
||||
timeout: 30_000,
|
||||
});
|
||||
expect(result.status).toBe(0);
|
||||
const pipeline = Bun.YAML.parse(readFileSync(pipelinePath, "utf8")) as Record<string, any>;
|
||||
const scripts = pipeline.spec.tasks.flatMap((task: Record<string, any>) => task.taskSpec.steps.map((step: Record<string, any>) => String(step.script ?? "")));
|
||||
expect(Math.max(...scripts.map((script: string) => Buffer.byteLength(script)))).toBeLessThanOrEqual(budget as number);
|
||||
expect(scripts.join("\n")).toContain("UNIDESK_RUNTIME_GITOPS_OVERLAY_FILE=/etc/unidesk-cicd-runtime-gitops/runtime-gitops-overlay.json");
|
||||
expect(scripts.join("\n")).not.toContain(Buffer.from(JSON.stringify(overlay.observability), "utf8").toString("base64"));
|
||||
const configMap = Bun.YAML.parse(readFileSync(configMapPath, "utf8")) as Record<string, any>;
|
||||
expect(JSON.parse(configMap.data["runtime-gitops-overlay.json"])).toEqual({ runtimePath: overlay.runtimePath, observability: overlay.observability });
|
||||
expect(Object.keys(configMap.data).sort()).toEqual([
|
||||
"runtime-gitops-observability.mjs",
|
||||
"runtime-gitops-overlay.json",
|
||||
"runtime-gitops-postprocess.mjs",
|
||||
"runtime-gitops-verify.mjs",
|
||||
]);
|
||||
const legacyPayload = Buffer.from(JSON.stringify({ runtimePath: overlay.runtimePath, observability: overlay.observability }), "utf8").toString("base64");
|
||||
const legacyPipeline = Bun.YAML.parse(readFileSync(pipelinePath, "utf8")) as Record<string, any>;
|
||||
const legacyStep = legacyPipeline.spec.tasks[0].taskSpec.steps[0];
|
||||
legacyStep.script = String(legacyStep.script).replaceAll(
|
||||
"UNIDESK_RUNTIME_GITOPS_OVERLAY_FILE=/etc/unidesk-cicd-runtime-gitops/runtime-gitops-overlay.json",
|
||||
`UNIDESK_RUNTIME_GITOPS_OVERLAY_B64='${legacyPayload}'`,
|
||||
);
|
||||
writeFileSync(pipelinePath, Bun.YAML.stringify(legacyPipeline));
|
||||
const migrated = spawnSync("node", [
|
||||
rootPath("scripts", "native", "hwlab", "runtime-gitops-pipeline-guard.mjs"),
|
||||
"--pipeline", pipelinePath,
|
||||
], {
|
||||
cwd: rootPath(),
|
||||
env: { ...process.env, UNIDESK_RUNTIME_GITOPS_OVERLAY_B64: Buffer.from(JSON.stringify(overlay), "utf8").toString("base64") },
|
||||
encoding: "utf8",
|
||||
timeout: 30_000,
|
||||
});
|
||||
expect(migrated.status).toBe(0);
|
||||
const migratedText = readFileSync(pipelinePath, "utf8");
|
||||
expect(migratedText).toContain("UNIDESK_RUNTIME_GITOPS_OVERLAY_FILE=/etc/unidesk-cicd-runtime-gitops/runtime-gitops-overlay.json");
|
||||
expect(migratedText).not.toContain(legacyPayload);
|
||||
} finally {
|
||||
rmSync(temporary, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test("normal control-plane postprocess writes exactly the shared four provenance fields", () => {
|
||||
const temporary = mkdtempSync(resolve(tmpdir(), "unidesk-hwlab-pipeline-provenance-"));
|
||||
try {
|
||||
@@ -491,7 +565,16 @@ describe("runtime source-commit selection", () => {
|
||||
configRef: "config/hwlab-node-lanes.yaml#lanes.v03.targets.NC01",
|
||||
renderer: "hwlab-runtime-lane",
|
||||
mode: "remote-pipeline-annotation",
|
||||
maxInlineScriptBytes: 32768,
|
||||
});
|
||||
expect([
|
||||
spec.codeAgentRuntime?.kafkaEventBridge.features.directPublish,
|
||||
spec.codeAgentRuntime?.kafkaEventBridge.features.liveKafkaSse,
|
||||
spec.codeAgentRuntime?.kafkaEventBridge.features.kafkaRefreshReplay,
|
||||
spec.codeAgentRuntime?.kafkaEventBridge.features.transactionalProjector,
|
||||
spec.codeAgentRuntime?.kafkaEventBridge.features.projectionOutboxRelay,
|
||||
spec.codeAgentRuntime?.kafkaEventBridge.features.projectionRealtime,
|
||||
]).toEqual([false, false, false, true, true, true]);
|
||||
expect(remoteAnnotations).toEqual(pipelineProvenanceAnnotations(remoteProvenance));
|
||||
expect(Object.keys(remoteAnnotations).sort()).toEqual(Object.values(pipelineProvenanceAnnotationKeys).sort());
|
||||
expect(readFileSync(rootPath("scripts", "src", "hwlab-node", "render.ts"), "utf8")).not.toContain("overlay.sourceArtifactProvenance");
|
||||
|
||||
Reference in New Issue
Block a user