fix: expose deploy artifact matrix plan fields
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
import { spawnSync } from "node:child_process";
|
||||
|
||||
function assertCondition(condition: unknown, message: string, detail: unknown = {}): void {
|
||||
if (!condition) throw new Error(`${message}: ${JSON.stringify(detail)}`);
|
||||
}
|
||||
|
||||
function asRecord(value: unknown, label: string): Record<string, unknown> {
|
||||
assertCondition(typeof value === "object" && value !== null && !Array.isArray(value), `${label} must be an object`, value);
|
||||
return value as Record<string, unknown>;
|
||||
}
|
||||
|
||||
function runDeployPlan(environment: "dev" | "prod", serviceId: string): Record<string, unknown> {
|
||||
const result = spawnSync("bun", ["scripts/cli.ts", "deploy", "plan", "--env", environment, "--service", serviceId], {
|
||||
cwd: process.cwd(),
|
||||
encoding: "utf8",
|
||||
maxBuffer: 8 * 1024 * 1024,
|
||||
});
|
||||
assertCondition(result.status === 0, `deploy plan should exit 0 for ${environment}/${serviceId}`, {
|
||||
status: result.status,
|
||||
stdout: result.stdout.slice(-2000),
|
||||
stderr: result.stderr.slice(-2000),
|
||||
});
|
||||
const envelope = asRecord(JSON.parse(result.stdout) as unknown, "cli envelope");
|
||||
assertCondition(envelope.ok === true, `deploy plan envelope should be ok for ${environment}/${serviceId}`, envelope);
|
||||
const data = asRecord(envelope.data, "deploy plan data");
|
||||
const services = Array.isArray(data.services) ? data.services : [];
|
||||
assertCondition(services.length === 1, `deploy plan should return one service for ${environment}/${serviceId}`, data);
|
||||
return asRecord(services[0], "service plan");
|
||||
}
|
||||
|
||||
function listIncludes(value: unknown, expected: string): boolean {
|
||||
return Array.isArray(value) && value.some((item) => item === expected);
|
||||
}
|
||||
|
||||
const findjob = runDeployPlan("dev", "findjob");
|
||||
const findjobArtifact = asRecord(findjob.artifactConsumer, "findjob artifactConsumer");
|
||||
const findjobTarget = asRecord(findjob.target, "findjob target");
|
||||
assertCondition(findjobArtifact.consumerKind === "d601-direct-compose", "findjob dev must be a D601 direct Compose artifact consumer", findjobArtifact);
|
||||
assertCondition(findjobArtifact.noRuntimeSourceBuild === true, "findjob dry-run must declare no runtime source build", findjobArtifact);
|
||||
assertCondition(findjobTarget.runtimeHost === "D601", "findjob target should be D601", findjobTarget);
|
||||
assertCondition(findjobTarget.composeService === "server", "findjob compose service should be server", findjobTarget);
|
||||
assertCondition(listIncludes(findjobTarget.forbiddenActions, "docker compose build"), "findjob plan should forbid Compose build", findjobTarget);
|
||||
|
||||
const mdtodo = runDeployPlan("prod", "mdtodo");
|
||||
const mdtodoArtifact = asRecord(mdtodo.artifactConsumer, "mdtodo artifactConsumer");
|
||||
const mdtodoTarget = asRecord(mdtodo.target, "mdtodo target");
|
||||
assertCondition(mdtodoArtifact.consumerKind === "d601-k3s-managed", "mdtodo prod must be a D601 k3s-managed artifact consumer", mdtodoArtifact);
|
||||
assertCondition(mdtodoArtifact.noRuntimeSourceBuild === true, "mdtodo dry-run must declare no runtime source build", mdtodoArtifact);
|
||||
assertCondition(mdtodoTarget.namespace === "unidesk", "mdtodo prod target namespace should be unidesk", mdtodoTarget);
|
||||
assertCondition(listIncludes(mdtodoTarget.forbiddenActions, "NodePort"), "mdtodo plan should forbid NodePort", mdtodoTarget);
|
||||
|
||||
const metNonlinear = runDeployPlan("prod", "met-nonlinear");
|
||||
const metArtifact = asRecord(metNonlinear.artifactConsumer, "met-nonlinear artifactConsumer");
|
||||
assertCondition(metArtifact.consumerKind === "d601-direct-compose", "met-nonlinear should remain D601 direct Compose", metArtifact);
|
||||
assertCondition(metArtifact.dryRunOnly === true, "met-nonlinear must remain dry-run only", metArtifact);
|
||||
assertCondition(String(metArtifact.blockedReason ?? "").includes("runtime-verification-blocked"), "met-nonlinear blocked reason should mention runtime verification", metArtifact);
|
||||
|
||||
const k3sctl = runDeployPlan("prod", "k3sctl-adapter");
|
||||
const k3sctlArtifact = asRecord(k3sctl.artifactConsumer, "k3sctl-adapter artifactConsumer");
|
||||
const k3sctlTarget = asRecord(k3sctl.target, "k3sctl-adapter target");
|
||||
assertCondition(k3sctlArtifact.consumerKind === "d601-direct-compose", "k3sctl-adapter should be D601 direct Compose", k3sctlArtifact);
|
||||
assertCondition(k3sctlArtifact.dryRunOnly === true, "k3sctl-adapter must be dry-run only for worker automation", k3sctlArtifact);
|
||||
assertCondition(String(k3sctlArtifact.blockedReason ?? "").includes("supervisor"), "k3sctl-adapter blocked reason should mention supervisor confirmation", k3sctlArtifact);
|
||||
assertCondition(k3sctlTarget.composeService === "k3sctl-adapter", "k3sctl target service should be k3sctl-adapter", k3sctlTarget);
|
||||
|
||||
process.stdout.write(`${JSON.stringify({
|
||||
ok: true,
|
||||
checks: [
|
||||
"deploy plan distinguishes D601 direct Compose from D601 k3s-managed artifact consumers",
|
||||
"deploy plan exposes no-runtime-source-build and forbidden build/public-port actions",
|
||||
"met-nonlinear remains runtime-verification-blocked",
|
||||
"k3sctl-adapter remains supervisor-only dry-run",
|
||||
],
|
||||
}, null, 2)}\n`);
|
||||
Reference in New Issue
Block a user