fix: add deploy desired-state plan

This commit is contained in:
HWLAB Code Queue
2026-05-22 06:59:29 +00:00
parent 8e89409dda
commit 9982b40010
8 changed files with 977 additions and 4 deletions
+743
View File
@@ -0,0 +1,743 @@
import { execFile } from "node:child_process";
import { readFile } from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { promisify } from "node:util";
const execFileAsync = promisify(execFile);
const defaultRepoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
const deployPath = "deploy/deploy.json";
const catalogPath = "deploy/artifact-catalog.dev.json";
const workloadsPath = "deploy/k8s/base/workloads.yaml";
const artifactReportPath = "reports/dev-gate/dev-artifacts.json";
const mutableTags = new Set(["latest", "dev", "main", "master", "prod", "production"]);
const mirrorEnvNames = [
"HWLAB_COMMIT_ID",
"HWLAB_IMAGE",
"HWLAB_IMAGE_TAG",
"HWLAB_SKILLS_COMMIT_ID",
"HWLAB_IMAGE_DIGEST"
];
const commitMirrorEnvNames = new Set(["HWLAB_COMMIT_ID", "HWLAB_SKILLS_COMMIT_ID"]);
const imageMirrorEnvNames = new Set(["HWLAB_IMAGE"]);
const tagMirrorEnvNames = new Set(["HWLAB_IMAGE_TAG"]);
const digestMirrorEnvNames = new Set(["HWLAB_IMAGE_DIGEST"]);
const tagPattern = /^[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}$/;
const commitPattern = /^[a-f0-9]{7,40}$/;
function parseArgs(argv) {
const args = { check: false, pretty: false, targetRef: null, targetTag: null, help: false };
for (let index = 0; index < argv.length; index += 1) {
const arg = argv[index];
if (arg === "--check") {
args.check = true;
} else if (arg === "--pretty") {
args.pretty = true;
} else if (arg === "--target-ref") {
args.targetRef = requireValue(argv, index, arg);
index += 1;
} else if (arg === "--target-tag") {
args.targetTag = requireValue(argv, index, arg);
index += 1;
} else if (arg === "--help" || arg === "-h") {
args.help = true;
} else {
throw new Error(`unknown argument: ${arg}`);
}
}
return args;
}
function requireValue(argv, index, arg) {
const value = argv[index + 1];
if (!value || value.startsWith("--")) {
throw new Error(`${arg} requires a value`);
}
return value;
}
function usage() {
return [
"usage: node scripts/deploy-desired-state-plan.mjs [--check] [--pretty] [--target-ref REF] [--target-tag TAG]",
"",
"Read-only DEV desired-state commit/image planner.",
"",
"Options:",
" --check exit non-zero only when desired-state sources are missing, invalid, or partially drifted",
" --pretty print indented JSON",
" --target-ref REF resolve REF with git and review promotion to its short commit tag",
" --target-tag TAG review promotion to an explicit image tag without proving registry existence",
"",
"This command reads repository files only. It does not build, pull, push, kubectl apply, restart services, or touch PROD."
].join("\n");
}
async function readJson(repoRoot, relativePath) {
const raw = await readFile(path.join(repoRoot, relativePath), "utf8");
return JSON.parse(raw);
}
async function readOptionalJson(repoRoot, relativePath) {
try {
return await readJson(repoRoot, relativePath);
} catch {
return null;
}
}
function parseImageRef(image) {
if (typeof image !== "string" || image.length === 0) {
return { valid: false, repository: null, tag: null, reason: "image must be a non-empty string" };
}
if (image.includes("@")) {
return { valid: false, repository: null, tag: null, reason: "digest-qualified image refs are not desired-state tags" };
}
const slashIndex = image.lastIndexOf("/");
const colonIndex = image.lastIndexOf(":");
if (colonIndex <= slashIndex) {
return { valid: false, repository: image, tag: null, reason: "image ref is missing a tag" };
}
const repository = image.slice(0, colonIndex);
const tag = image.slice(colonIndex + 1);
if (!repository || !tag) {
return { valid: false, repository, tag, reason: "image repository and tag must be non-empty" };
}
return { valid: true, repository, tag, reason: null };
}
function replaceImageTag(image, targetTag) {
const parsed = parseImageRef(image);
return parsed.valid ? `${parsed.repository}:${targetTag}` : null;
}
function shortCommit(value) {
return typeof value === "string" && value.length >= 7 ? value.slice(0, 7) : value;
}
function commitEquivalent(actual, expected) {
if (typeof actual !== "string" || typeof expected !== "string") return false;
return actual === expected || actual === expected.slice(0, 7) || expected === actual.slice(0, 7);
}
function listItems(document) {
if (!document) return [];
return document.kind === "List" ? document.items ?? [] : [document];
}
function serviceIdForWorkload(item, container) {
return (
item?.metadata?.labels?.["hwlab.pikastech.local/service-id"] ||
item?.spec?.template?.metadata?.labels?.["hwlab.pikastech.local/service-id"] ||
container?.name ||
item?.metadata?.name
);
}
function workloadContainers(item) {
return item?.spec?.template?.spec?.containers ?? [];
}
function envListToObject(envList) {
const env = {};
if (!Array.isArray(envList)) return env;
for (const entry of envList) {
if (!entry?.name) continue;
if (Object.hasOwn(entry, "value")) {
env[entry.name] = entry.value;
} else if (entry.valueFrom) {
env[entry.name] = { valueFrom: entry.valueFrom };
}
}
return env;
}
function addDiagnostic(ctx, diagnostic) {
ctx.diagnostics.push({
severity: "blocker",
...diagnostic
});
}
function addMismatch(ctx, code, sourcePath, message, expected, actual, extra = {}) {
addDiagnostic(ctx, {
code,
path: sourcePath,
message,
expected,
actual,
...extra
});
}
function recordObservation(ctx, observation) {
ctx.observations.push(observation);
}
function expectedEnvValue(name, service, desiredCommitId) {
if (name === "HWLAB_COMMIT_ID" || name === "HWLAB_SKILLS_COMMIT_ID") return desiredCommitId;
if (name === "HWLAB_IMAGE") return service?.image ?? null;
if (name === "HWLAB_IMAGE_TAG") return service?.imageTag ?? null;
if (name === "HWLAB_IMAGE_DIGEST") return service?.digest ?? "not_published";
return null;
}
function observationKindForEnv(name) {
if (commitMirrorEnvNames.has(name)) return "commit";
if (imageMirrorEnvNames.has(name)) return "image";
if (tagMirrorEnvNames.has(name)) return "tag";
if (digestMirrorEnvNames.has(name)) return "digest";
return "unknown";
}
function valueMatchesExpected(kind, actual, expected) {
if (expected === null || expected === undefined) return true;
if (kind === "commit") return commitEquivalent(actual, expected);
return Object.is(actual, expected);
}
function inspectEnvMirrors(ctx, { source, basePath, serviceId, env, service, desiredCommitId }) {
const present = {};
const missing = [];
const diagnostics = [];
for (const name of mirrorEnvNames) {
if (!Object.hasOwn(env ?? {}, name)) {
missing.push(name);
continue;
}
const actual = env[name];
const kind = observationKindForEnv(name);
const expected = expectedEnvValue(name, service, desiredCommitId);
const record = {
name,
value: actual,
expected,
status: valueMatchesExpected(kind, actual, expected) ? "match" : "drift"
};
present[name] = record;
recordObservation(ctx, {
source,
serviceId,
path: `${basePath}.${name}`,
field: name,
kind,
value: actual,
expected
});
if (record.status === "drift") {
const message = `${source} ${serviceId} ${name} mirror does not match desired ${kind}`;
const diagnostic = {
code: "mirror_mismatch",
path: `${basePath}.${name}`,
message,
expected,
actual
};
diagnostics.push(diagnostic);
addDiagnostic(ctx, diagnostic);
}
}
return { present, missing, diagnostics };
}
function indexWorkloads(workloads) {
const byService = new Map();
for (const item of listItems(workloads)) {
for (const [containerIndex, container] of workloadContainers(item).entries()) {
const serviceId = serviceIdForWorkload(item, container);
if (!serviceId) continue;
const record = {
kind: item?.kind ?? "unknown",
name: item?.metadata?.name ?? "unknown",
namespace: item?.metadata?.namespace ?? null,
containerIndex,
containerName: container?.name ?? "unknown",
image: container?.image ?? null,
env: envListToObject(container?.env),
path: `${workloadsPath}.${item?.kind ?? "Workload"}.${item?.metadata?.name ?? serviceId}.containers[${containerIndex}]`
};
const list = byService.get(serviceId) ?? [];
list.push(record);
byService.set(serviceId, list);
}
}
return byService;
}
function serviceIdsFrom(...sources) {
const ids = [];
for (const source of sources) {
for (const id of source) {
if (typeof id === "string" && id.length > 0 && !ids.includes(id)) ids.push(id);
}
}
return ids;
}
function assertTargetTag(ctx, tag, pathName) {
if (!tagPattern.test(tag)) {
addMismatch(ctx, "invalid_target_tag", pathName, "target tag is not a valid Docker tag", "Docker tag syntax", tag);
}
if (mutableTags.has(tag)) {
addMismatch(ctx, "mutable_target_tag", pathName, `target tag ${tag} is mutable and forbidden for desired-state pinning`, "immutable tag", tag);
}
}
async function resolveTarget(repoRoot, { targetRef, targetTag }, ctx) {
if (!targetRef && !targetTag) return null;
let commitId = null;
let shortCommitId = null;
if (targetRef) {
try {
const result = await execFileAsync("git", ["rev-parse", "--verify", `${targetRef}^{commit}`], { cwd: repoRoot });
commitId = result.stdout.trim();
shortCommitId = commitId.slice(0, 7);
} catch (error) {
addDiagnostic(ctx, {
code: "target_ref_unresolved",
path: "--target-ref",
message: `target ref ${targetRef} could not be resolved with git`,
expected: "local git ref",
actual: error instanceof Error ? error.message : String(error)
});
}
}
const tag = targetTag ?? shortCommitId;
if (tag) assertTargetTag(ctx, tag, "--target-tag");
return {
targetRef,
commitId,
shortCommitId,
tag,
tagSource: targetTag ? "target-tag" : "target-ref-short-commit"
};
}
function observationMatchesTarget(observation, target) {
if (!target) return null;
if (observation.kind === "commit") {
return target.commitId ? commitEquivalent(observation.value, target.commitId) : null;
}
if (observation.kind === "tag") {
return target.tag ? observation.value === target.tag : null;
}
if (observation.kind === "image") {
return target.tag ? parseImageRef(observation.value).tag === target.tag : null;
}
return null;
}
function buildTargetConvergence(ctx, target) {
if (!target) return null;
if (target.targetRef && !target.commitId) {
return {
state: "target_unresolved",
comparableFields: 0,
matchingTargetFields: 0,
pendingTargetFields: 0,
matchingPaths: [],
pendingPaths: []
};
}
const comparable = ctx.observations
.map((observation) => ({
...observation,
matchesTarget: observationMatchesTarget(observation, target)
}))
.filter((observation) => observation.matchesTarget !== null);
const matching = comparable.filter((observation) => observation.matchesTarget === true);
const pending = comparable.filter((observation) => observation.matchesTarget === false);
const partial = matching.length > 0 && pending.length > 0;
if (partial) {
addDiagnostic(ctx, {
code: "partial_target_drift",
path: "desired-state",
message: "desired-state fields partially match the target while other authoritative fields still point elsewhere",
expected: target.tag,
actual: {
matching: matching.length,
pending: pending.length
}
});
}
const state = partial
? "partial_drift"
: pending.length === 0
? "already_promoted"
: "promotion_pending";
return {
state,
comparableFields: comparable.length,
matchingTargetFields: matching.length,
pendingTargetFields: pending.length,
matchingPaths: matching.map((item) => item.path),
pendingPaths: pending.map((item) => item.path)
};
}
function reportHints(report) {
if (!report) {
return {
path: artifactReportPath,
present: false,
authoritative: false,
note: "optional report snapshot is absent; desired-state review still uses deploy files only"
};
}
const artifactPublish = report.artifactPublish ?? {};
return {
path: artifactReportPath,
present: true,
authoritative: false,
note: "report snapshots are contextual evidence only; deploy desired-state remains authoritative in deploy/, not reports/",
commitId: report.commitId ?? null,
artifactPublish: {
status: artifactPublish.status ?? null,
sourceCommitId: artifactPublish.sourceCommitId ?? null,
publishedCount: artifactPublish.publishedCount ?? null,
serviceCount: artifactPublish.serviceCount ?? null,
registryVerified: artifactPublish.registryVerified ?? null,
registryCapabilitiesClassification: artifactPublish.registryCapabilities?.classification ?? null
}
};
}
function servicePromotion(service, target) {
if (!target?.tag) return null;
return {
targetCommitId: target.commitId,
targetImageTag: target.tag,
deployImage: service.deploy?.image ? replaceImageTag(service.deploy.image, target.tag) : null,
catalogImage: service.catalog?.image ? replaceImageTag(service.catalog.image, target.tag) : null,
workloadImages: (service.workloads ?? []).map((workload) => ({
workload: workload.name,
container: workload.container,
image: workload.image ? replaceImageTag(workload.image, target.tag) : null
}))
};
}
function targetCommands(target) {
if (!target?.targetRef) return [];
return [
`node scripts/deploy-desired-state-plan.mjs --target-ref ${target.targetRef} --pretty`,
`node scripts/refresh-artifact-catalog.mjs --target-ref ${target.targetRef} --blocked`,
`node scripts/refresh-artifact-catalog.mjs --target-ref ${target.targetRef} --publish-report ${artifactReportPath}`
];
}
export async function buildDesiredStatePlan(options = {}) {
const repoRoot = options.repoRoot ?? defaultRepoRoot;
const ctx = { diagnostics: [], observations: [] };
const [deploy, catalog, workloads, artifactReport] = await Promise.all([
readJson(repoRoot, deployPath),
readJson(repoRoot, catalogPath),
readJson(repoRoot, workloadsPath),
readOptionalJson(repoRoot, artifactReportPath)
]);
const target = await resolveTarget(repoRoot, {
targetRef: options.targetRef ?? null,
targetTag: options.targetTag ?? null
}, ctx);
const deployServices = deploy.services ?? [];
const catalogServices = catalog.services ?? [];
const deployByService = new Map(deployServices.map((service) => [service.serviceId, service]));
const catalogByService = new Map(catalogServices.map((service) => [service.serviceId, service]));
const workloadByService = indexWorkloads(workloads);
const desiredCommitId = deploy.commitId;
const desiredImageTag = shortCommit(desiredCommitId);
recordObservation(ctx, {
source: "deploy",
path: `${deployPath}.commitId`,
field: "commitId",
kind: "commit",
value: deploy.commitId,
expected: desiredCommitId
});
recordObservation(ctx, {
source: "catalog",
path: `${catalogPath}.commitId`,
field: "commitId",
kind: "commit",
value: catalog.commitId,
expected: desiredCommitId
});
if (!commitPattern.test(String(deploy.commitId ?? ""))) {
addMismatch(ctx, "invalid_commit", `${deployPath}.commitId`, "deploy commitId must be a short or full lowercase Git SHA", "7-40 lowercase hex", deploy.commitId);
}
if (!commitEquivalent(catalog.commitId, desiredCommitId)) {
addMismatch(ctx, "commit_mismatch", `${catalogPath}.commitId`, "artifact catalog commitId must match deploy commitId", desiredCommitId, catalog.commitId);
}
const serviceIds = serviceIdsFrom(
deployServices.map((service) => service.serviceId),
catalogServices.map((service) => service.serviceId),
[...workloadByService.keys()]
);
const services = [];
for (const serviceId of serviceIds) {
const deployService = deployByService.get(serviceId);
const catalogService = catalogByService.get(serviceId);
const workloadsForService = workloadByService.get(serviceId) ?? [];
const service = {
serviceId,
deploy: null,
catalog: null,
workloads: [],
promotion: null
};
if (!deployService) {
addMismatch(ctx, "missing_service", `${deployPath}.services.${serviceId}`, `${serviceId} missing from deploy manifest`, "service entry", null, { serviceId });
}
if (!catalogService) {
addMismatch(ctx, "missing_service", `${catalogPath}.services.${serviceId}`, `${serviceId} missing from artifact catalog`, "service entry", null, { serviceId });
}
if (!workloadsForService.length) {
addMismatch(ctx, "missing_workload", `${workloadsPath}.${serviceId}`, `${serviceId} missing from k8s workloads`, "workload container", null, { serviceId });
}
const deployImage = parseImageRef(deployService?.image);
const catalogImage = parseImageRef(catalogService?.image);
const serviceImageTag = catalogService?.imageTag ?? deployImage.tag;
const expectedService = {
image: deployService?.image ?? catalogService?.image ?? null,
imageTag: desiredImageTag,
digest: catalogService?.digest ?? "not_published"
};
if (deployService) {
const deployPathBase = `${deployPath}.services.${serviceId}`;
recordObservation(ctx, {
source: "deploy",
serviceId,
path: `${deployPathBase}.image`,
field: "image",
kind: "image",
value: deployService.image,
expected: catalogService?.image ?? deployService.image
});
if (!deployImage.valid) {
addMismatch(ctx, "invalid_image", `${deployPathBase}.image`, deployImage.reason, "tagged image ref", deployService.image, { serviceId });
} else if (deployImage.tag !== desiredImageTag) {
addMismatch(ctx, "image_tag_mismatch", `${deployPathBase}.image`, `${serviceId} deploy image tag must match deploy commit tag`, desiredImageTag, deployImage.tag, { serviceId });
}
service.deploy = {
image: deployService.image,
imageTag: deployImage.tag,
envMirrors: inspectEnvMirrors(ctx, {
source: "deploy",
basePath: `${deployPathBase}.env`,
serviceId,
env: deployService.env ?? {},
service: expectedService,
desiredCommitId
})
};
}
if (catalogService) {
const catalogPathBase = `${catalogPath}.services.${serviceId}`;
recordObservation(ctx, {
source: "catalog",
serviceId,
path: `${catalogPathBase}.commitId`,
field: "commitId",
kind: "commit",
value: catalogService.commitId,
expected: desiredCommitId
});
recordObservation(ctx, {
source: "catalog",
serviceId,
path: `${catalogPathBase}.image`,
field: "image",
kind: "image",
value: catalogService.image,
expected: deployService?.image ?? catalogService.image
});
recordObservation(ctx, {
source: "catalog",
serviceId,
path: `${catalogPathBase}.imageTag`,
field: "imageTag",
kind: "tag",
value: catalogService.imageTag,
expected: desiredImageTag
});
if (!commitEquivalent(catalogService.commitId, desiredCommitId)) {
addMismatch(ctx, "commit_mismatch", `${catalogPathBase}.commitId`, `${serviceId} catalog service commitId must match deploy commitId`, desiredCommitId, catalogService.commitId, { serviceId });
}
if (!catalogImage.valid) {
addMismatch(ctx, "invalid_image", `${catalogPathBase}.image`, catalogImage.reason, "tagged image ref", catalogService.image, { serviceId });
}
if (catalogService.imageTag !== desiredImageTag) {
addMismatch(ctx, "image_tag_mismatch", `${catalogPathBase}.imageTag`, `${serviceId} catalog imageTag must match deploy commit tag`, desiredImageTag, catalogService.imageTag, { serviceId });
}
if (catalogImage.valid && catalogImage.tag !== catalogService.imageTag) {
addMismatch(ctx, "image_tag_mismatch", `${catalogPathBase}.image`, `${serviceId} catalog image tag must match catalog imageTag`, catalogService.imageTag, catalogImage.tag, { serviceId });
}
if (deployService?.image && catalogService.image !== deployService.image) {
addMismatch(ctx, "image_mismatch", `${catalogPathBase}.image`, `${serviceId} catalog image must match deploy image`, deployService.image, catalogService.image, { serviceId });
}
service.catalog = {
commitId: catalogService.commitId,
image: catalogService.image,
imageTag: catalogService.imageTag,
digest: catalogService.digest,
publishState: catalogService.publishState,
artifactRequired: catalogService.artifactRequired
};
}
for (const workload of workloadsForService) {
const workloadImage = parseImageRef(workload.image);
recordObservation(ctx, {
source: "workload",
serviceId,
path: `${workload.path}.image`,
field: "image",
kind: "image",
value: workload.image,
expected: deployService?.image ?? catalogService?.image ?? null
});
if (!workloadImage.valid) {
addMismatch(ctx, "invalid_image", `${workload.path}.image`, workloadImage.reason, "tagged image ref", workload.image, { serviceId });
} else if (workloadImage.tag !== desiredImageTag) {
addMismatch(ctx, "image_tag_mismatch", `${workload.path}.image`, `${serviceId} workload image tag must match deploy commit tag`, desiredImageTag, workloadImage.tag, { serviceId });
}
if (deployService?.image && workload.image !== deployService.image) {
addMismatch(ctx, "image_mismatch", `${workload.path}.image`, `${serviceId} workload image must match deploy image`, deployService.image, workload.image, { serviceId });
}
service.workloads.push({
kind: workload.kind,
name: workload.name,
namespace: workload.namespace,
container: workload.containerName,
image: workload.image,
imageTag: workloadImage.tag,
envMirrors: inspectEnvMirrors(ctx, {
source: "workload",
basePath: `${workload.path}.env`,
serviceId,
env: workload.env,
service: expectedService,
desiredCommitId
})
});
}
service.promotion = servicePromotion(service, target);
services.push(service);
}
const targetConvergence = buildTargetConvergence(ctx, target);
const blockers = ctx.diagnostics.filter((diagnostic) => diagnostic.severity === "blocker");
const status = blockers.length > 0
? "blocked"
: targetConvergence?.state === "promotion_pending"
? "planned"
: "pass";
const presentMirrorCount = services.reduce((count, service) => {
const deployCount = service.deploy ? Object.keys(service.deploy.envMirrors.present).length : 0;
const workloadCount = service.workloads.reduce((inner, workload) => inner + Object.keys(workload.envMirrors.present).length, 0);
return count + deployCount + workloadCount;
}, 0);
return {
kind: "hwlab-deploy-desired-state-plan",
mode: "read-only-plan",
status,
source: {
deploy: deployPath,
artifactCatalog: catalogPath,
workloads: workloadsPath,
optionalReport: artifactReportPath
},
safety: {
readOnly: true,
sourceOrDryRunSupportOnly: true,
kubectlApply: false,
registryPull: false,
registryPush: false,
imageBuild: false,
serviceRestart: false,
prod: false,
devLiveClaim: false
},
checkSemantics: "--check exits non-zero only for missing/invalid desired-state sources, internal commit/image mirror drift, invalid target tags, or partial target drift. A uniform older desired-state under --target-ref is a read-only promotion plan, not a check failure.",
summary: {
desiredCommitId,
desiredImageTag,
artifactState: catalog.artifactState,
ciPublished: catalog.publish?.ciPublished === true,
registryVerified: catalog.publish?.registryVerified === true,
services: services.length,
workloadContainers: [...workloadByService.values()].reduce((sum, entries) => sum + entries.length, 0),
presentMirrorCount,
diagnostics: ctx.diagnostics.length,
blockers: blockers.length,
targetConvergence: targetConvergence?.state ?? "not_requested"
},
target: target ? {
...target,
convergence: targetConvergence,
commands: targetCommands(target)
} : null,
promotionBoundary: {
writes: [],
authoritativeDesiredState: [deployPath, catalogPath, workloadsPath],
nonAuthoritativeEvidence: [artifactReportPath],
note: "This planner reviews source desired-state only. It does not prove image existence, registry reachability, a real DEV apply, or M3 DEV-LIVE hardware-loop evidence."
},
reportHints: reportHints(artifactReport),
services,
diagnostics: ctx.diagnostics
};
}
export async function runDeployDesiredStatePlanCli(argv = process.argv.slice(2), options = {}) {
const args = parseArgs(argv);
if (args.help) {
process.stdout.write(`${usage()}\n`);
return;
}
const plan = await buildDesiredStatePlan({
repoRoot: options.repoRoot,
targetRef: args.targetRef,
targetTag: args.targetTag
});
process.stdout.write(`${JSON.stringify(plan, null, args.pretty ? 2 : 0)}\n`);
if (args.check && plan.status === "blocked") {
process.exitCode = 1;
}
}
export function writeDeployDesiredStatePlanError(error) {
process.stdout.write(`${JSON.stringify({
kind: "hwlab-deploy-desired-state-plan",
mode: "read-only-plan",
status: "error",
error: error instanceof Error ? error.message : String(error),
safety: {
readOnly: true,
kubectlApply: false,
registryPull: false,
registryPush: false,
imageBuild: false,
serviceRestart: false,
prod: false,
devLiveClaim: false
}
})}\n`);
process.exitCode = 1;
}