edfddd2445
* docs: specify cicd yaml target governance * fix: resolve cicd targets from yaml --------- Co-authored-by: Codex <codex@noreply.local>
156 lines
6.3 KiB
TypeScript
156 lines
6.3 KiB
TypeScript
// SPEC: PJ2026-01060307 控制面模块化 draft-2026-06-25-p0. options module for scripts/src/artifact-registry.ts.
|
|
// SPEC: PJ2026-01060308 cicd-yaml-targets draft-2026-06-25-cicd-yaml-targets.
|
|
|
|
// Moved mechanically from scripts/src/artifact-registry.ts:881-978 for #903.
|
|
|
|
import { createHash } from "node:crypto";
|
|
import { existsSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
import { join } from "node:path";
|
|
import { runCommand, type CommandResult } from "../command";
|
|
import { readConfig, type UniDeskConfig, repoRoot, rootPath } from "../config";
|
|
import { startJob } from "../jobs";
|
|
import {
|
|
compareDeployJsonExecutorMirrors,
|
|
deployJsonCommitImage,
|
|
deployJsonDriftResult,
|
|
deployJsonSourceOfTruth,
|
|
hasDeployJsonExecutorContract,
|
|
k3sManifestExecutorMirror,
|
|
parseDeployJsonServiceContractBase64,
|
|
readDeployJsonServiceContractFromFile,
|
|
type DeployJsonExecutorMirror,
|
|
type DeployJsonServiceContract,
|
|
} from "../deploy-json-contract";
|
|
import { d601K3sGuardShellLines } from "../d601-k3s-guard";
|
|
import { composeRuntimeEnvValue } from "../runtime-env";
|
|
|
|
import type { ArtifactDeployEnvironment, ArtifactRegistryOptions } from "./types";
|
|
import { artifactRegistryOptionsFromTarget } from "./types";
|
|
import { resolveArtifactRegistryTarget } from "../ops/targets";
|
|
|
|
export function isHelpArg(value: string | undefined): boolean {
|
|
return value === undefined || value === "help" || value === "--help" || value === "-h";
|
|
}
|
|
|
|
export function requireValue(args: string[], index: number, option: string): string {
|
|
const value = args[index + 1];
|
|
if (value === undefined || value.length === 0) throw new Error(`${option} requires a non-empty value`);
|
|
return value;
|
|
}
|
|
|
|
export function positiveInt(value: string, option: string): number {
|
|
const parsed = Number(value);
|
|
if (!Number.isInteger(parsed) || parsed <= 0) throw new Error(`${option} must be a positive integer`);
|
|
return parsed;
|
|
}
|
|
|
|
export function absolutePath(value: string, option: string): string {
|
|
if (!value.startsWith("/")) throw new Error(`${option} must be an absolute path`);
|
|
if (value.includes("\n") || value.includes("\0")) throw new Error(`${option} must not contain control characters`);
|
|
return value.replace(/\/+$/u, "");
|
|
}
|
|
|
|
export function commitValue(value: string, option: string): string {
|
|
const normalized = value.toLowerCase();
|
|
if (!/^[0-9a-f]{40}$/u.test(normalized)) throw new Error(`${option} must be a full 40-character commit SHA`);
|
|
return normalized;
|
|
}
|
|
|
|
export function environmentValue(value: string, option: string): ArtifactDeployEnvironment {
|
|
if (value === "prod" || value === "dev") return value;
|
|
throw new Error(`${option} must be one of: prod, dev`);
|
|
}
|
|
|
|
export function parseArtifactRegistryOptions(args: string[]): ArtifactRegistryOptions {
|
|
let targetSelection: string | null = null;
|
|
let environment: ArtifactDeployEnvironment | null = null;
|
|
let host: string | null = null;
|
|
let port: number | null = null;
|
|
let image: string | null = null;
|
|
let baseDir: string | null = null;
|
|
let storageDir: string | null = null;
|
|
let timeoutMs: number | null = null;
|
|
let dryRun = false;
|
|
let runNow = false;
|
|
let commit: string | null = null;
|
|
let targetImage: string | null = null;
|
|
let serviceId: string | null = null;
|
|
let sourceRepo: string | null = null;
|
|
let sourceRepoExplicit = false;
|
|
let deployRef: string | null = null;
|
|
let deployJsonService: ArtifactRegistryOptions["deployJsonService"] = null;
|
|
for (let index = 0; index < args.length; index += 1) {
|
|
const arg = args[index];
|
|
if (arg === "--dry-run") {
|
|
dryRun = true;
|
|
} else if (arg === "--run-now") {
|
|
runNow = true;
|
|
} else if (arg === "--env" || arg === "--environment") {
|
|
environment = environmentValue(requireValue(args, index, arg), arg);
|
|
index += 1;
|
|
} else if (arg === "--target" || arg === "--provider-id") {
|
|
targetSelection = requireValue(args, index, arg);
|
|
index += 1;
|
|
} else if (arg === "--host") {
|
|
host = requireValue(args, index, arg);
|
|
index += 1;
|
|
} else if (arg === "--port") {
|
|
port = positiveInt(requireValue(args, index, arg), arg);
|
|
index += 1;
|
|
} else if (arg === "--image") {
|
|
image = requireValue(args, index, arg);
|
|
index += 1;
|
|
} else if (arg === "--base-dir") {
|
|
baseDir = absolutePath(requireValue(args, index, arg), arg);
|
|
index += 1;
|
|
} else if (arg === "--storage-dir") {
|
|
storageDir = absolutePath(requireValue(args, index, arg), arg);
|
|
index += 1;
|
|
} else if (arg === "--timeout-ms") {
|
|
timeoutMs = positiveInt(requireValue(args, index, arg), arg);
|
|
index += 1;
|
|
} else if (arg === "--commit") {
|
|
commit = commitValue(requireValue(args, index, arg), arg);
|
|
index += 1;
|
|
} else if (arg === "--target-image") {
|
|
targetImage = requireValue(args, index, arg);
|
|
index += 1;
|
|
} else if (arg === "--service" || arg === "--service-id") {
|
|
serviceId = requireValue(args, index, arg);
|
|
index += 1;
|
|
} else if (arg === "--source-repo") {
|
|
sourceRepo = requireValue(args, index, arg);
|
|
sourceRepoExplicit = true;
|
|
index += 1;
|
|
} else if (arg === "--deploy-ref") {
|
|
deployRef = requireValue(args, index, arg);
|
|
index += 1;
|
|
} else if (arg === "--deploy-json-service") {
|
|
deployJsonService = parseDeployJsonServiceContractBase64(requireValue(args, index, arg));
|
|
index += 1;
|
|
} else {
|
|
throw new Error(`unknown artifact-registry option: ${arg}`);
|
|
}
|
|
}
|
|
const options = artifactRegistryOptionsFromTarget(resolveArtifactRegistryTarget(targetSelection));
|
|
options.environment = environment;
|
|
options.host = host ?? options.host;
|
|
options.port = port ?? options.port;
|
|
options.image = image ?? options.image;
|
|
options.baseDir = baseDir ?? options.baseDir;
|
|
options.storageDir = storageDir ?? options.storageDir;
|
|
options.timeoutMs = timeoutMs ?? options.timeoutMs;
|
|
options.dryRun = dryRun;
|
|
options.runNow = runNow;
|
|
options.commit = commit;
|
|
options.targetImage = targetImage;
|
|
options.serviceId = serviceId;
|
|
options.sourceRepo = sourceRepo ?? options.sourceRepo;
|
|
options.sourceRepoExplicit = sourceRepoExplicit;
|
|
options.deployRef = deployRef;
|
|
options.deployJsonService = deployJsonService;
|
|
if (options.host !== "127.0.0.1") throw new Error("--host is first-stage restricted to 127.0.0.1");
|
|
if (options.port !== 5000) throw new Error("--port is first-stage restricted to 5000");
|
|
return options;
|
|
}
|