// SPEC: PJ2026-01060307 控制面模块化 draft-2026-06-25-p0. entry 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:3448-3700 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 { ArtifactRegistryAction, ArtifactRegistryCommandRuntime, ArtifactRegistryOptions } from "./types"; import { renderBundle } from "./bundle"; import { deployBackendCoreJob, deployBackendCoreNow, deployComposeArtifactNow, deployD601ComposeArtifactNow, dryRunArtifactConsumerPlan, legacyDeployBackendCoreResult } from "./compose-deploy"; import { artifactConsumerLiveBlock, artifactConsumerSpec, artifactConsumerTarget, supportedArtifactConsumers, unsupportedEnvironment, unsupportedService } from "./consumer"; import { install, installDryRun } from "./install"; import { deployD601K3sArtifactNow, rollbackInfo } from "./k3s-deploy"; import { isHelpArg, parseArtifactRegistryOptions } from "./options"; import { runReadonlyStatusWithRemoteFallback } from "./readonly"; import { plan } from "./status"; import { defaultOptions, legacyDeployBackendCoreDisabled, supportedArtifactConsumerServices } from "./types"; export async function deployServiceNow(options: ArtifactRegistryOptions): Promise> { if (options.serviceId === null) throw new Error("artifact-registry deploy-service requires --service "); if (options.commit === null) throw new Error("artifact-registry deploy-service requires --commit "); const spec = artifactConsumerSpec(options.serviceId, options.environment); if (spec === null) return unsupportedService(options.serviceId, options); const target = artifactConsumerTarget(spec, options.environment); if (target === null) return unsupportedEnvironment(spec, options); if (options.dryRun) return dryRunArtifactConsumerPlan(options, spec, target, options.commit); const liveBlock = artifactConsumerLiveBlock(spec, options); if (liveBlock !== null) return liveBlock; if (spec.kind === "compose") return deployComposeArtifactNow(options, spec, target); if (spec.kind === "d601-compose") return deployD601ComposeArtifactNow(options, spec, target); return deployD601K3sArtifactNow(options, spec, target); } export function deployServiceJob(args: string[], options: ArtifactRegistryOptions): Record { if (options.serviceId === null) throw new Error("artifact-registry deploy-service requires --service "); if (options.commit === null) throw new Error("artifact-registry deploy-service requires --commit "); const spec = artifactConsumerSpec(options.serviceId, options.environment); if (spec === null) return unsupportedService(options.serviceId, options); const target = artifactConsumerTarget(spec, options.environment); if (target === null) return unsupportedEnvironment(spec, options); if (options.dryRun) return dryRunArtifactConsumerPlan(options, spec, target, options.commit); const liveBlock = artifactConsumerLiveBlock(spec, options); if (liveBlock !== null) return liveBlock; const runArgs = args.includes("--run-now") ? args : [...args, "--run-now"]; const command = [process.execPath, rootPath("scripts", "cli.ts"), "artifact-registry", ...runArgs]; const job = startJob("artifact_registry_service_cd", command, `Pull and deploy ${options.environment ?? "prod"} ${options.serviceId} artifact ${options.commit} from D601 registry`); return { ok: true, mode: "async-job", serviceId: options.serviceId, environment: options.environment ?? "prod", job, statusCommand: `bun scripts/cli.ts job status ${job.id}`, tailCommand: `bun scripts/cli.ts job status ${job.id} --tail-bytes 30000`, note: "User-service CD continues in the background: D601 registry health, commit-pinned artifact check, pull/import, rollout, image-label and live health commit verification.", rollback: rollbackInfo(spec, target, options.environment ?? "prod", options.commit), }; } export function serviceIdFromDeployBackendCoreArgs(action: ArtifactRegistryAction, options: ArtifactRegistryOptions): string | null { return action === "deploy-backend-core" ? "backend-core" : options.serviceId; } export function localHelp(): Record { return { ok: true, command: "artifact-registry plan|render|status|health|install|deploy-backend-core|deploy-service", output: "json", usage: [ "bun scripts/cli.ts artifact-registry plan [--target D601]", "bun scripts/cli.ts artifact-registry render [--target D601]", "bun scripts/cli.ts artifact-registry status [--target D601]", "bun scripts/cli.ts artifact-registry health [--target D601]", "bun scripts/cli.ts artifact-registry install [--target D601]", "bun scripts/cli.ts artifact-registry deploy-service --service baidu-netdisk --commit [--dry-run] [--run-now] [--target D601]", "bun scripts/cli.ts artifact-registry deploy-service --service frontend --env prod --commit [--dry-run] [--run-now] [--target D601]", "bun scripts/cli.ts artifact-registry deploy-service --service frontend --env dev --commit [--dry-run] [--run-now] [--target D601]", "bun scripts/cli.ts artifact-registry deploy-service --env dev --service decision-center --commit [--dry-run] [--run-now] [--target D601]", "bun scripts/cli.ts artifact-registry deploy-service --env prod --service decision-center --commit [--dry-run] [--run-now] [--target D601]", "bun scripts/cli.ts artifact-registry deploy-service --env prod --service project-manager --commit [--dry-run] [--run-now] [--target D601]", "bun scripts/cli.ts artifact-registry deploy-service --env prod --service oa-event-flow --commit [--dry-run] [--run-now] [--target D601]", "bun scripts/cli.ts artifact-registry deploy-service --env prod --service code-queue-mgr --commit --dry-run [--target D601]", "bun scripts/cli.ts artifact-registry deploy-service --env prod --service todo-note --commit [--dry-run] [--run-now] [--target D601]", "bun scripts/cli.ts artifact-registry deploy-service --env dev --service todo-note --commit [--dry-run] [--run-now] [--target D601]", "bun scripts/cli.ts artifact-registry deploy-service --env dev --service findjob --commit [--dry-run] [--run-now] [--target D601]", "bun scripts/cli.ts artifact-registry deploy-service --env prod --service findjob --commit [--dry-run] [--run-now] [--target D601]", "bun scripts/cli.ts artifact-registry deploy-service --env dev --service pipeline --commit [--dry-run] [--run-now] [--target D601]", "bun scripts/cli.ts artifact-registry deploy-service --env prod --service pipeline --commit [--dry-run] [--run-now] [--target D601]", "bun scripts/cli.ts artifact-registry deploy-service --env dev --service met-nonlinear --commit --dry-run [--target D601]", "bun scripts/cli.ts artifact-registry deploy-service --env prod --service met-nonlinear --commit --dry-run [--target D601]", "bun scripts/cli.ts artifact-registry deploy-service --env prod --service k3sctl-adapter --commit --dry-run [--target D601]", "bun scripts/cli.ts artifact-registry deploy-service --env dev --service mdtodo --commit [--dry-run] [--run-now] [--target D601]", "bun scripts/cli.ts artifact-registry deploy-service --env prod --service mdtodo --commit [--dry-run] [--run-now] [--target D601]", "bun scripts/cli.ts artifact-registry deploy-service --env dev --service claudeqq --commit [--dry-run] [--run-now] [--target D601]", "bun scripts/cli.ts artifact-registry deploy-service --env prod --service claudeqq --commit [--dry-run] [--run-now] [--target D601]", "bun scripts/cli.ts artifact-registry deploy-service --env dev --service code-queue --commit --dry-run [--target D601]", ], firstStage: "install now writes the rendered systemd/Compose/config files and starts the registry", artifactConsumers: { supportedServices: supportedArtifactConsumerServices, supportedConsumers: supportedArtifactConsumers(), unsupportedPolicy: "return structured unsupported; never fall back to legacy maintenance-channel source builds", prodCommands: [ "bun scripts/cli.ts deploy apply --env prod --service backend-core", "bun scripts/cli.ts deploy apply --env prod --service baidu-netdisk", "bun scripts/cli.ts deploy apply --env prod --service frontend", "bun scripts/cli.ts deploy apply --env prod --service decision-center", "bun scripts/cli.ts deploy apply --env prod --service project-manager", "bun scripts/cli.ts deploy apply --env prod --service oa-event-flow", "bun scripts/cli.ts deploy apply --env prod --service code-queue-mgr --dry-run", "bun scripts/cli.ts deploy apply --env prod --service todo-note", "bun scripts/cli.ts deploy apply --env prod --service findjob", "bun scripts/cli.ts deploy apply --env prod --service pipeline", "bun scripts/cli.ts deploy apply --env prod --service met-nonlinear --dry-run", "bun scripts/cli.ts deploy apply --env prod --service k3sctl-adapter --dry-run", "bun scripts/cli.ts deploy apply --env prod --service mdtodo", "bun scripts/cli.ts deploy apply --env prod --service claudeqq", ], devCommands: [ "bun scripts/cli.ts deploy apply --env dev --service frontend", "bun scripts/cli.ts deploy apply --env dev --service decision-center", "bun scripts/cli.ts deploy apply --env dev --service project-manager --dry-run", "bun scripts/cli.ts deploy apply --env dev --service oa-event-flow --dry-run", "bun scripts/cli.ts deploy apply --env dev --service code-queue-mgr --dry-run", "bun scripts/cli.ts deploy apply --env dev --service todo-note", "bun scripts/cli.ts deploy apply --env dev --service findjob --dry-run", "bun scripts/cli.ts deploy apply --env dev --service pipeline --dry-run", "bun scripts/cli.ts deploy apply --env dev --service met-nonlinear --dry-run", "bun scripts/cli.ts deploy apply --env dev --service mdtodo", "bun scripts/cli.ts deploy apply --env dev --service claudeqq", "bun scripts/cli.ts deploy apply --env dev --service code-queue --dry-run", ], devOnlyConsumers: ["code-queue"], rollbackShape: "rerun the same artifact consumer with a previous commit-pinned image", }, legacyEntrypoints: { "deploy-backend-core": { deprecated: true, enabled: !legacyDeployBackendCoreDisabled, replacement: "bun scripts/cli.ts deploy apply --env prod --service backend-core --commit ", }, }, defaults: defaultOptions, }; } export async function runArtifactRegistryCommand(args: string[], runtime: ArtifactRegistryCommandRuntime = {}): Promise { const action = args[0]; if (isHelpArg(action)) return localHelp(); if (action !== "plan" && action !== "render" && action !== "status" && action !== "health" && action !== "install" && action !== "deploy-backend-core" && action !== "deploy-service") { throw new Error("artifact-registry usage: plan|render|status|health|install|deploy-backend-core|deploy-service"); } const typedAction = action as ArtifactRegistryAction; const options = parseArtifactRegistryOptions(args.slice(1)); if (action === "plan") return plan(options); if (action === "render") return { ok: true, providerId: options.providerId, render: renderBundle(options) }; if (action === "status") return runReadonlyStatusWithRemoteFallback(options, false, runtime); if (action === "health") return runReadonlyStatusWithRemoteFallback(options, true, runtime); if (action === "install") { return options.dryRun ? installDryRun(options) : install(options); } if (action === "deploy-backend-core") { if (options.commit === null) throw new Error("artifact-registry deploy-backend-core requires --commit "); if (legacyDeployBackendCoreDisabled) return legacyDeployBackendCoreResult(options); return options.runNow ? await deployBackendCoreNow(options) : deployBackendCoreJob(args, options); } if (action === "deploy-service") { const serviceId = serviceIdFromDeployBackendCoreArgs(typedAction, options); return options.runNow || options.dryRun ? await deployServiceNow({ ...options, serviceId }) : deployServiceJob(args, { ...options, serviceId }); } throw new Error("unreachable artifact-registry action"); }