import { spawnSync } from "node:child_process"; import { mkdirSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import assert from "node:assert/strict"; type JsonRecord = Record; function runCli(args: string[], env: NodeJS.ProcessEnv = {}): JsonRecord { const result = spawnSync("bun", ["scripts/cli.ts", ...args], { cwd: process.cwd(), env: { ...process.env, ...env }, encoding: "utf8", timeout: 20_000, }); assert.equal(result.stderr, "", `stderr should be empty: ${result.stderr}`); assert.notEqual(result.stdout.trim(), "", "CLI must not produce empty output"); const parsed = JSON.parse(result.stdout) as JsonRecord; if (result.status !== 0) { assert.equal(parsed.ok, false, `nonzero CLI should return ok=false: ${result.stdout}`); } return parsed; } function makeFakeHwlabRepo(): string { const root = join(tmpdir(), `unidesk-hwlab-cd-wrapper-${process.pid}-${Date.now()}`); mkdirSync(join(root, "scripts"), { recursive: true }); writeFileSync(join(root, "scripts/dev-cd-apply.mjs"), [ "const kubeconfigIndex = process.argv.indexOf('--kubeconfig');", "process.stdout.write(JSON.stringify({", " ok: true,", " status: 'pass',", " mode: process.argv.includes('--dry-run') ? 'dry-run' : 'status',", " command: 'dev-cd-apply',", " mutationAttempted: false,", " prodTouched: false,", " target: {", " ref: 'origin/main',", " promotionCommit: 'abc1234567890abcdef',", " shortCommitId: 'abc1234',", " promotionSource: 'deploy-json',", " publishRequired: false,", " headCommitId: 'abc1234567890abcdef',", " headMatchesTarget: true,", " desiredStateCheck: { status: 'pass', summary: { desiredCommitId: 'abc1234', targetConvergence: 'already_promoted' } },", " artifactBoundary: { status: 'pass', desiredState: { deployCommitId: 'abc1234', catalogCommitId: 'abc1234', deployCommitMatches: true, catalogCommitMatches: true } },", " namespace: 'hwlab-dev'", " },", " deployJson: { path: 'deploy/deploy.json', commitId: 'abc1234', matchesTarget: true },", " artifactCatalog: { path: 'deploy/artifact-catalog.dev.json', commitId: 'abc1234', artifactState: 'published', ciPublished: true, registryVerified: true },", " artifactReport: { path: 'reports/dev-gate/dev-artifacts.json', commitId: 'abc1234' },", " lock: { status: 'absent' },", " liveDelta: { status: 'unknown' },", " kubeconfig: kubeconfigIndex >= 0 ? process.argv[kubeconfigIndex + 1] : null", "}, null, 2));", ].join("\n")); writeFileSync(join(root, "scripts/dev-deploy-apply.mjs"), [ "const dryRun = process.argv.includes('--dry-run');", "const kubeconfigIndex = process.argv.indexOf('--kubeconfig');", "process.stdout.write(JSON.stringify({", " reportVersion: 'v1',", " status: dryRun ? 'pass' : 'blocked',", " commitId: 'abc1234',", " namespace: 'hwlab-dev',", " endpoint: 'http://74.48.78.17:16667',", " blockers: [],", " devDeployApply: {", " conclusion: { status: 'ready', blockerCount: 0 },", " artifactPlan: { expectedArtifactCommit: 'abc1234', deployCommitId: 'abc1234', catalogCommitId: 'abc1234', published: true, registryVerified: true, imageCount: 13, requiredServiceCount: 13, unpublishedServices: [] },", " applyBoundary: { currentMode: 'dry-run', defaultNoWrite: true, mutationAttempted: false, mutationAllowed: false, kubeconfigSource: 'flag:--kubeconfig', writeScope: 'KUBECONFIG=/etc/rancher/k3s/k3s.yaml kubectl apply -k deploy/k8s/dev', noWriteScope: 'server-side dry-run only', forbiddenActions: ['prod-deploy'] },", " applyStep: { status: 'pass', command: 'KUBECONFIG=/etc/rancher/k3s/k3s.yaml kubectl apply --dry-run=server -k deploy/k8s/dev', mutationAttempted: false },", " manualCommands: { status: 'ready' }", " },", " kubeconfig: kubeconfigIndex >= 0 ? process.argv[kubeconfigIndex + 1] : null", "}, null, 2));", ].join("\n")); writeFileSync(join(root, "scripts/deploy-desired-state-plan.mjs"), [ "process.stdout.write(JSON.stringify({", " kind: 'hwlab-deploy-desired-state-plan',", " mode: 'read-only-plan',", " status: 'pass',", " source: { deploy: 'deploy/deploy.json', artifactCatalog: 'deploy/artifact-catalog.dev.json', workloads: 'deploy/k8s/base/workloads.yaml', optionalReport: 'reports/dev-gate/dev-artifacts.json' },", " promotionBoundary: { authoritativeDesiredState: ['deploy/deploy.json', 'deploy/artifact-catalog.dev.json', 'deploy/k8s/base/workloads.yaml'], nonAuthoritativeEvidence: ['reports/dev-gate/dev-artifacts.json'] },", " summary: { desiredCommitId: 'abc1234', desiredImageTag: 'abc1234', artifactState: 'published', ciPublished: true, registryVerified: true, services: 13, workloadContainers: 13, diagnostics: 0, blockers: 0, targetConvergence: 'not_requested' }", "}, null, 2));", ].join("\n")); spawnSync("git", ["init", "-b", "main"], { cwd: root, encoding: "utf8" }); spawnSync("git", ["config", "user.email", "test@example.invalid"], { cwd: root, encoding: "utf8" }); spawnSync("git", ["config", "user.name", "HWLAB CD Test"], { cwd: root, encoding: "utf8" }); spawnSync("git", ["remote", "add", "origin", "git@github.com:pikasTech/HWLAB.git"], { cwd: root, encoding: "utf8" }); spawnSync("git", ["add", "."], { cwd: root, encoding: "utf8" }); spawnSync("git", ["commit", "-m", "fixture"], { cwd: root, encoding: "utf8" }); spawnSync("git", ["update-ref", "refs/remotes/origin/main", "HEAD"], { cwd: root, encoding: "utf8" }); writeFileSync(join(root, ".git", "FETCH_HEAD"), "fixture\n"); return root; } function makeFakeBin(mode: "native" | "desktop" | "stale-default" | "wrong-node" | "missing-secret"): string { const bin = join(tmpdir(), `unidesk-hwlab-cd-bin-${process.pid}-${Date.now()}-${mode}`); mkdirSync(bin, { recursive: true }); const explicitContext = mode === "desktop" ? "docker-desktop" : "default"; const explicitServer = mode === "desktop" ? "https://127.0.0.1:11700" : "https://127.0.0.1:6443"; const explicitNodes = mode === "desktop" ? "desktop-control-plane" : mode === "wrong-node" ? "d602" : "d601"; const defaultContext = mode === "stale-default" ? "docker-desktop" : explicitContext; const defaultServer = mode === "stale-default" ? "https://127.0.0.1:11700" : explicitServer; const defaultNodes = mode === "stale-default" ? "desktop-control-plane" : explicitNodes; writeFileSync(join(bin, "kubectl"), [ "#!/usr/bin/env bash", "set -euo pipefail", "printf 'KUBECONFIG=%s\\n' \"${KUBECONFIG:-}\" >&2", "context=" + JSON.stringify(explicitContext), "server=" + JSON.stringify(explicitServer), "nodes=" + JSON.stringify(explicitNodes), "if [[ \"${KUBECONFIG:-}\" == '' ]]; then", " context=" + JSON.stringify(defaultContext), " server=" + JSON.stringify(defaultServer), " nodes=" + JSON.stringify(defaultNodes), "fi", "if [[ \"$*\" == 'config current-context' ]]; then printf '%s\\n' \"$context\"; exit 0; fi", "if [[ \"$*\" == 'config view --minify -o jsonpath={.clusters[0].cluster.server}' ]]; then printf '%s' \"$server\"; exit 0; fi", "if [[ \"$*\" == 'get nodes -o jsonpath={range .items[*]}{.metadata.name}{\"\\n\"}{end}' ]]; then printf '%s\\n' \"$nodes\"; exit 0; fi", "if [[ \"$*\" == '-n hwlab-dev get lease hwlab-dev-cd-lock -o json' ]]; then printf 'Error from server (NotFound): leases.coordination.k8s.io \"hwlab-dev-cd-lock\" not found\\n' >&2; exit 1; fi", "if [[ \"$*\" == '-n hwlab-dev get deploy -o jsonpath={range .items[*]}{.metadata.name}{\"\\t\"}{range .spec.template.spec.containers[*]}{.name}{\"=\"}{.image}{\",\"}{end}{\"\\n\"}{end}' ]]; then printf 'hwlab-cloud-api\\thwlab-cloud-api=127.0.0.1:5000/hwlab/hwlab-cloud-api:abc1234,\\n'; exit 0; fi", "if [[ \"$*\" == '-n hwlab-dev get secret hwlab-code-agent-provider -o name' && " + JSON.stringify(mode) + " == 'missing-secret' ]]; then printf 'Error from server (NotFound): secrets \"hwlab-code-agent-provider\" not found\\n' >&2; exit 1; fi", "if [[ \"$*\" =~ ^-n\\ hwlab-dev\\ get\\ secret\\ ([^[:space:]]+)\\ -o\\ name$ ]]; then printf 'secret/%s\\n' \"${BASH_REMATCH[1]}\"; exit 0; fi", "if [[ \"$*\" == '-n hwlab-dev describe secret hwlab-cloud-api-dev-db' ]]; then printf 'Name: hwlab-cloud-api-dev-db\\nData\\n====\\ndatabase-url: 48 bytes\\n'; exit 0; fi", "if [[ \"$*\" == '-n hwlab-dev describe secret hwlab-cloud-api-dev-db-admin' ]]; then printf 'Name: hwlab-cloud-api-dev-db-admin\\nData\\n====\\nadmin-url: 48 bytes\\n'; exit 0; fi", "if [[ \"$*\" == '-n hwlab-dev describe secret hwlab-code-agent-provider' ]]; then printf 'Name: hwlab-code-agent-provider\\nData\\n====\\nopenai-api-key: 48 bytes\\n'; exit 0; fi", "printf '{}\\n'", ].join("\n")); spawnSync("chmod", ["+x", join(bin, "kubectl")]); return bin; } const fakeRepo = makeFakeHwlabRepo(); const nativeBin = makeFakeBin("native"); const desktopBin = makeFakeBin("desktop"); const staleDefaultBin = makeFakeBin("stale-default"); const wrongNodeBin = makeFakeBin("wrong-node"); const missingSecretBin = makeFakeBin("missing-secret"); const liveBody = "data:application/json,%7B%22serviceId%22%3A%22hwlab-cloud-web%22%2C%22environment%22%3A%22dev%22%2C%22status%22%3A%22ok%22%2C%22revision%22%3A%22abc1234%22%7D"; const apiBody = "data:application/json,%7B%22serviceId%22%3A%22hwlab-cloud-api%22%2C%22environment%22%3A%22dev%22%2C%22status%22%3A%22ok%22%2C%22revision%22%3A%22abc1234%22%7D"; const help = runCli(["hwlab", "help"]); assert.equal(help.ok, true); assert.equal((help.data as JsonRecord).command, "hwlab cd"); const runnerHistoryRepo = runCli([ "hwlab", "cd", "status", "--env", "dev", "--hwlab-repo", "/home/ubuntu/hwlab", ], { PATH: `${nativeBin}:${process.env.PATH ?? ""}`, }); assert.equal(runnerHistoryRepo.ok, false); const runnerHistoryCandidates = ((runnerHistoryRepo.data as JsonRecord).repo as JsonRecord).candidates as JsonRecord[]; assert.equal(runnerHistoryCandidates[0]?.rejected, true); assert.equal(runnerHistoryCandidates[0]?.rejectionReason, "runner-history-directory-is-not-hwlab-cd-release-truth"); const applyDryRun = runCli([ "hwlab", "cd", "apply", "--env", "dev", "--dry-run", "--hwlab-repo", fakeRepo, ], { PATH: `${nativeBin}:${process.env.PATH ?? ""}`, }); assert.equal(applyDryRun.ok, true); const dryRunData = applyDryRun.data as JsonRecord; assert.equal(dryRunData.dryRun, true); assert.equal(dryRunData.mutation, false); assert.equal(((dryRunData.d601NativeK3sGuard as JsonRecord).injectedEnv as JsonRecord).KUBECONFIG, "/etc/rancher/k3s/k3s.yaml"); assert.equal((dryRunData.d601NativeK3sGuard as JsonRecord).requiredNodePresent, true); assert.equal((dryRunData.controlledDryRun as JsonRecord).commandOk, true); assert.equal((dryRunData.secretRefPreflight as JsonRecord).status, "pass"); assert.equal(((dryRunData.controlledDryRun as JsonRecord).controlledEntrypoint), "scripts/dev-cd-apply.mjs"); assert.equal(((dryRunData.hostCommanderOnlyLiveApply as JsonRecord).commandShape as unknown[]).includes("scripts/dev-cd-apply.mjs"), true); assert.equal(JSON.stringify(dryRunData).includes("sk-secret"), false); const preflight = runCli([ "hwlab", "cd", "preflight", "--env", "dev", "--hwlab-repo", fakeRepo, ], { PATH: `${nativeBin}:${process.env.PATH ?? ""}`, UNIDESK_HWLAB_CD_TEST_FRONTEND_LIVE_URL: liveBody, UNIDESK_HWLAB_CD_TEST_API_LIVE_URL: apiBody, }); assert.equal(preflight.ok, true); const preflightData = preflight.data as JsonRecord; assert.equal(preflightData.mutation, false); assert.equal((preflightData.secretRefPreflight as JsonRecord).status, "pass"); assert.equal((preflightData.liveWorkloads as JsonRecord).status, "observed"); const realApply = runCli([ "hwlab", "cd", "apply", "--env", "dev", "--hwlab-repo", fakeRepo, ], { PATH: `${nativeBin}:${process.env.PATH ?? ""}`, }); assert.equal(realApply.ok, false); assert.equal((realApply.data as JsonRecord).error, "host-commander-only-real-apply"); const status = runCli([ "hwlab", "cd", "status", "--env", "dev", "--hwlab-repo", fakeRepo, ], { PATH: `${nativeBin}:${process.env.PATH ?? ""}`, UNIDESK_HWLAB_CD_TEST_FRONTEND_LIVE_URL: liveBody, UNIDESK_HWLAB_CD_TEST_API_LIVE_URL: apiBody, }); assert.equal(status.ok, true); const statusData = status.data as JsonRecord; assert.equal(((statusData.d601NativeK3sGuard as JsonRecord).injectedEnv as JsonRecord).KUBECONFIG, "/etc/rancher/k3s/k3s.yaml"); assert.equal((statusData.liveRevisions as JsonRecord).status, "observed"); assert.ok(typeof statusData.dumpDir === "string" && String(statusData.dumpDir).includes(".state/hwlab-cd")); const staleDefaultOk = runCli([ "hwlab", "cd", "apply", "--env", "dev", "--dry-run", "--hwlab-repo", fakeRepo, ], { PATH: `${staleDefaultBin}:${process.env.PATH ?? ""}`, KUBECONFIG: "", }); assert.equal(staleDefaultOk.ok, true); const staleDefaultGuard = (staleDefaultOk.data as JsonRecord).d601NativeK3sGuard as JsonRecord; assert.equal(staleDefaultGuard.status, "pass"); assert.equal(staleDefaultGuard.refusal, false); assert.equal((staleDefaultGuard.defaultKubectlDiagnostic as JsonRecord).status, "stale-forbidden-default"); assert.deepEqual((staleDefaultGuard.defaultKubectlDiagnostic as JsonRecord).refusalSignals, ["docker-desktop", "desktop-control-plane", "127.0.0.1:11700"]); const desktopRefusal = runCli([ "hwlab", "cd", "apply", "--env", "dev", "--dry-run", "--hwlab-repo", fakeRepo, ], { PATH: `${desktopBin}:${process.env.PATH ?? ""}`, }); assert.equal(desktopRefusal.ok, false); assert.equal((desktopRefusal.data as JsonRecord).error, "native-k3s-guard-refused"); assert.deepEqual((desktopRefusal.data as JsonRecord).d601NativeK3sGuard && ((desktopRefusal.data as JsonRecord).d601NativeK3sGuard as JsonRecord).refusalSignals, ["docker-desktop", "desktop-control-plane", "127.0.0.1:11700"]); const wrongNodeBlocked = runCli([ "hwlab", "cd", "apply", "--env", "dev", "--dry-run", "--hwlab-repo", fakeRepo, ], { PATH: `${wrongNodeBin}:${process.env.PATH ?? ""}`, }); assert.equal(wrongNodeBlocked.ok, false); const wrongNodeGuard = (wrongNodeBlocked.data as JsonRecord).d601NativeK3sGuard as JsonRecord; assert.equal(wrongNodeGuard.status, "blocked"); assert.equal(wrongNodeGuard.requiredNodePresent, false); assert.equal(((wrongNodeBlocked.data as JsonRecord).blockers as JsonRecord[]).some((blocker) => blocker.scope === "d601-native-k3s-guard"), true); const missingSecretBlocked = runCli([ "hwlab", "cd", "apply", "--env", "dev", "--dry-run", "--hwlab-repo", fakeRepo, ], { PATH: `${missingSecretBin}:${process.env.PATH ?? ""}`, }); assert.equal(missingSecretBlocked.ok, false); const missingSecretData = missingSecretBlocked.data as JsonRecord; assert.equal((missingSecretData.secretRefPreflight as JsonRecord).status, "blocked"); assert.equal((missingSecretData.controlledDryRun as JsonRecord).status, "skipped"); assert.equal((missingSecretData.blockers as JsonRecord[]).some((blocker) => blocker.scope === "secretref:hwlab-code-agent-provider/openai-api-key"), true); assert.equal(JSON.stringify(missingSecretData).includes("sk-secret"), false); console.log(JSON.stringify({ ok: true, checked: "hwlab-cd-wrapper-contract" }));