fix: 保持 UniDesk Host skip 只读
This commit is contained in:
@@ -23,12 +23,20 @@ delivery:
|
|||||||
changeDetection:
|
changeDetection:
|
||||||
runtimePaths:
|
runtimePaths:
|
||||||
- .tekton/unidesk-host-pac.yaml
|
- .tekton/unidesk-host-pac.yaml
|
||||||
|
- config/hwlab-node-lanes.yaml
|
||||||
- config/platform-infra/pipelines-as-code.yaml
|
- config/platform-infra/pipelines-as-code.yaml
|
||||||
- config/unidesk-host-k8s.yaml
|
- config/unidesk-host-k8s.yaml
|
||||||
- scripts/native/cicd/build-unidesk-host-image.sh
|
- scripts/native/cicd/build-unidesk-host-image.sh
|
||||||
- scripts/native/cicd/prepare-unidesk-host-release.mjs
|
- scripts/native/cicd/prepare-unidesk-host-release.mjs
|
||||||
- scripts/native/cicd/publish-unidesk-host-gitops.mjs
|
- scripts/native/cicd/publish-unidesk-host-gitops.mjs
|
||||||
- scripts/native/deploy/render-unidesk-host-service.mjs
|
- scripts/native/deploy/render-unidesk-host-service.mjs
|
||||||
|
- scripts/native/hwlab/runtime-gitops-observability.mjs
|
||||||
|
- scripts/native/hwlab/runtime-gitops-postprocess.mjs
|
||||||
|
- scripts/native/hwlab/runtime-gitops-scripts-configmap.mjs
|
||||||
|
- scripts/native/hwlab/runtime-gitops-verify.mjs
|
||||||
|
- scripts/src/config.ts
|
||||||
|
- scripts/src/hwlab-node-lanes.ts
|
||||||
|
- scripts/src/yaml-composition.ts
|
||||||
- src/components/microservices/todo-note/Dockerfile
|
- src/components/microservices/todo-note/Dockerfile
|
||||||
- src/components/microservices/todo-note/bun.lock
|
- src/components/microservices/todo-note/bun.lock
|
||||||
- src/components/microservices/todo-note/package.json
|
- src/components/microservices/todo-note/package.json
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ const writeUrl = required(gitops.writeUrl, "delivery.gitops.writeUrl");
|
|||||||
const branch = required(gitops.branch, "delivery.gitops.branch");
|
const branch = required(gitops.branch, "delivery.gitops.branch");
|
||||||
const manifestPath = safeManifestPath(gitops.manifestPath);
|
const manifestPath = safeManifestPath(gitops.manifestPath);
|
||||||
const releaseStatePath = safeReleaseStatePath(gitops.releaseStatePath);
|
const releaseStatePath = safeReleaseStatePath(gitops.releaseStatePath);
|
||||||
const gitopsResources = renderGitOpsResources(gitops, sourceRoot);
|
const gitopsResources = action === "skip" ? [] : renderGitOpsResources(gitops, sourceRoot);
|
||||||
let digest = null;
|
let digest = null;
|
||||||
let digestRef = null;
|
let digestRef = null;
|
||||||
let manifest = null;
|
let manifest = null;
|
||||||
@@ -190,6 +190,26 @@ if (action === "skip") {
|
|||||||
digest = existingState.digest;
|
digest = existingState.digest;
|
||||||
digestRef = existingState.digestRef;
|
digestRef = existingState.digestRef;
|
||||||
runtimeSourceCommit = existingState.sourceCommit;
|
runtimeSourceCommit = existingState.sourceCommit;
|
||||||
|
const gitopsCommit = run("git", ["rev-parse", "HEAD"], worktree).stdout.trim();
|
||||||
|
process.stdout.write(`${JSON.stringify({
|
||||||
|
ok: true,
|
||||||
|
phase: "gitops-publish",
|
||||||
|
action,
|
||||||
|
reason,
|
||||||
|
status: "skipped",
|
||||||
|
imageStatus: "skipped",
|
||||||
|
sourceCommit,
|
||||||
|
baselineSourceCommit,
|
||||||
|
runtimeSourceCommit,
|
||||||
|
digest,
|
||||||
|
digestRef,
|
||||||
|
gitopsCommit,
|
||||||
|
resources: [],
|
||||||
|
changed: false,
|
||||||
|
pushAttempts: 0,
|
||||||
|
valuesPrinted: false,
|
||||||
|
})}\n`);
|
||||||
|
return;
|
||||||
} else if (action === "build" && manifest !== null) {
|
} else if (action === "build" && manifest !== null) {
|
||||||
mkdirSync(dirname(targetPath), { recursive: true });
|
mkdirSync(dirname(targetPath), { recursive: true });
|
||||||
writeFileSync(targetPath, manifest, "utf8");
|
writeFileSync(targetPath, manifest, "utf8");
|
||||||
|
|||||||
@@ -0,0 +1,115 @@
|
|||||||
|
import { afterEach, describe, expect, test } from "bun:test";
|
||||||
|
import { spawnSync } from "node:child_process";
|
||||||
|
import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
||||||
|
import { tmpdir } from "node:os";
|
||||||
|
import { dirname, join } from "node:path";
|
||||||
|
|
||||||
|
import { rootPath } from "../../src/config";
|
||||||
|
|
||||||
|
const temporaryRoots: string[] = [];
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
for (const root of temporaryRoots.splice(0)) rmSync(root, { recursive: true, force: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("UniDesk Host GitOps publisher", () => {
|
||||||
|
test("skip preserves the GitOps branch and every managed resource", () => {
|
||||||
|
const root = mkdtempSync(join(tmpdir(), "unidesk-host-gitops-skip-"));
|
||||||
|
temporaryRoots.push(root);
|
||||||
|
const seed = join(root, "seed");
|
||||||
|
const bare = join(root, "gitops.git");
|
||||||
|
const releaseDir = join(root, "release");
|
||||||
|
const publisherWorktree = join(root, "publisher");
|
||||||
|
const configPath = join(root, "config.yaml");
|
||||||
|
const statePath = "deploy/gitops-state/unidesk-host/todo-note.json";
|
||||||
|
const resourcePath = "deploy/gitops/unidesk-host/hwlab-runtime-gitops-scripts.yaml";
|
||||||
|
const runtimeSourceCommit = "a".repeat(40);
|
||||||
|
const sourceCommit = "b".repeat(40);
|
||||||
|
const digest = `sha256:${"c".repeat(64)}`;
|
||||||
|
|
||||||
|
mkdirSync(seed);
|
||||||
|
git(["init", "-b", "unidesk-host-gitops"], seed);
|
||||||
|
identity(seed);
|
||||||
|
write(seed, "deploy/gitops/unidesk-host/todo-note.yaml", "existing service manifest\n");
|
||||||
|
write(seed, resourcePath, "stale configmap must remain unchanged\n");
|
||||||
|
write(seed, statePath, `${JSON.stringify({
|
||||||
|
version: 1,
|
||||||
|
kind: "UniDeskHostReleaseState",
|
||||||
|
serviceRef: "services.todoNote",
|
||||||
|
sourceCommit: runtimeSourceCommit,
|
||||||
|
digest,
|
||||||
|
digestRef: `registry.example/todo-note@${digest}`,
|
||||||
|
}, null, 2)}\n`);
|
||||||
|
git(["add", "."], seed);
|
||||||
|
git(["commit", "-m", "seed gitops"], seed);
|
||||||
|
const originalHead = git(["rev-parse", "HEAD"], seed).stdout.trim();
|
||||||
|
git(["init", "--bare", bare], root);
|
||||||
|
git(["remote", "add", "origin", `file://${bare}`], seed);
|
||||||
|
git(["push", "origin", "unidesk-host-gitops"], seed);
|
||||||
|
|
||||||
|
mkdirSync(releaseDir);
|
||||||
|
writeFileSync(join(releaseDir, "action"), "skip\n");
|
||||||
|
writeFileSync(join(releaseDir, "reason"), "runtime-inputs-unchanged\n");
|
||||||
|
writeFileSync(join(releaseDir, "baseline-source-commit"), `${runtimeSourceCommit}\n`);
|
||||||
|
writeFileSync(configPath, Bun.YAML.stringify({
|
||||||
|
delivery: {
|
||||||
|
enabled: true,
|
||||||
|
serviceRef: "services.todoNote",
|
||||||
|
image: { repository: "registry.example/todo-note" },
|
||||||
|
gitops: {
|
||||||
|
readUrl: `file://${bare}`,
|
||||||
|
writeUrl: `file://${bare}`,
|
||||||
|
branch: "unidesk-host-gitops",
|
||||||
|
manifestPath: "deploy/gitops/unidesk-host/todo-note.yaml",
|
||||||
|
releaseStatePath: statePath,
|
||||||
|
resources: [{
|
||||||
|
id: "hwlab-runtime-gitops-scripts",
|
||||||
|
renderer: "hwlab-runtime-gitops-scripts",
|
||||||
|
configRef: "config/hwlab-node-lanes.yaml#lanes.v03.targets.NC01",
|
||||||
|
manifestPath: resourcePath,
|
||||||
|
namespace: "hwlab-ci",
|
||||||
|
}],
|
||||||
|
author: { name: "UniDesk Test", email: "unidesk-test@example.invalid" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
const result = spawnSync("bun", [
|
||||||
|
"scripts/native/cicd/publish-unidesk-host-gitops.mjs",
|
||||||
|
"--config", configPath,
|
||||||
|
"--source-root", rootPath(),
|
||||||
|
"--metadata", join(root, "unused-metadata.json"),
|
||||||
|
"--release-dir", releaseDir,
|
||||||
|
"--source-commit", sourceCommit,
|
||||||
|
"--worktree", publisherWorktree,
|
||||||
|
], { cwd: rootPath(), encoding: "utf8", timeout: 30_000 });
|
||||||
|
expect(result.status, result.stderr).toBe(0);
|
||||||
|
const output = JSON.parse(result.stdout) as Record<string, any>;
|
||||||
|
expect(output).toMatchObject({ action: "skip", status: "skipped", changed: false, pushAttempts: 0, resources: [] });
|
||||||
|
expect(output.gitopsCommit).toBe(originalHead);
|
||||||
|
expect(git(["rev-parse", "refs/heads/unidesk-host-gitops"], bare).stdout.trim()).toBe(originalHead);
|
||||||
|
expect(show(bare, resourcePath)).toBe("stale configmap must remain unchanged\n");
|
||||||
|
expect(JSON.parse(show(bare, statePath)).sourceCommit).toBe(runtimeSourceCommit);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function write(root: string, relativePath: string, content: string) {
|
||||||
|
const path = join(root, relativePath);
|
||||||
|
mkdirSync(dirname(path), { recursive: true });
|
||||||
|
writeFileSync(path, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
function identity(root: string) {
|
||||||
|
git(["config", "user.name", "UniDesk Test"], root);
|
||||||
|
git(["config", "user.email", "unidesk-test@example.invalid"], root);
|
||||||
|
}
|
||||||
|
|
||||||
|
function show(bare: string, relativePath: string): string {
|
||||||
|
return git(["show", `refs/heads/unidesk-host-gitops:${relativePath}`], bare).stdout;
|
||||||
|
}
|
||||||
|
|
||||||
|
function git(args: string[], cwd: string) {
|
||||||
|
const result = spawnSync("git", args, { cwd, encoding: "utf8" });
|
||||||
|
if (result.status !== 0) throw new Error(`git ${args.join(" ")} failed: ${result.stderr || result.stdout}`);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
@@ -316,6 +316,17 @@ describe("HWLAB YAML-owned Pipeline provenance", () => {
|
|||||||
"runtime-gitops-verify.mjs",
|
"runtime-gitops-verify.mjs",
|
||||||
]);
|
]);
|
||||||
const hostConfig = Bun.YAML.parse(readFileSync(rootPath("config", "unidesk-host-k8s.yaml"), "utf8")) as Record<string, any>;
|
const hostConfig = Bun.YAML.parse(readFileSync(rootPath("config", "unidesk-host-k8s.yaml"), "utf8")) as Record<string, any>;
|
||||||
|
const runtimePaths = new Set(hostConfig.delivery.changeDetection.runtimePaths as string[]);
|
||||||
|
for (const inputPath of [
|
||||||
|
"config/hwlab-node-lanes.yaml",
|
||||||
|
"scripts/native/hwlab/runtime-gitops-observability.mjs",
|
||||||
|
"scripts/native/hwlab/runtime-gitops-postprocess.mjs",
|
||||||
|
"scripts/native/hwlab/runtime-gitops-scripts-configmap.mjs",
|
||||||
|
"scripts/native/hwlab/runtime-gitops-verify.mjs",
|
||||||
|
"scripts/src/config.ts",
|
||||||
|
"scripts/src/hwlab-node-lanes.ts",
|
||||||
|
"scripts/src/yaml-composition.ts",
|
||||||
|
]) expect(runtimePaths.has(inputPath), inputPath).toBe(true);
|
||||||
expect(hostConfig.delivery.gitops.resources).toContainEqual({
|
expect(hostConfig.delivery.gitops.resources).toContainEqual({
|
||||||
id: "hwlab-nc01-v03-runtime-gitops-scripts",
|
id: "hwlab-nc01-v03-runtime-gitops-scripts",
|
||||||
renderer: "hwlab-runtime-gitops-scripts",
|
renderer: "hwlab-runtime-gitops-scripts",
|
||||||
|
|||||||
Reference in New Issue
Block a user