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; 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; }