// SPEC: PJ2026-01060307 控制面模块化 draft-2026-06-25-p0. entry module for scripts/src/deploy.ts. // SPEC: PJ2026-01060308 cicd-yaml-targets draft-2026-06-25-cicd-yaml-targets. // Moved mechanically from scripts/src/deploy.ts:3528-3700 for #903. import { createHash } from "node:crypto"; import { existsSync, mkdirSync, readFileSync } from "node:fs"; import { join, resolve } from "node:path"; import { pathToFileURL } from "node:url"; import { runCommand } from "../command"; import { type UniDeskConfig, type UniDeskMicroserviceConfig, repoRoot, rootPath } from "../config"; import { ensureGithubSshIdentityForProvider } from "../deploy-ssh-identity"; import { baiduNetdiskRuntimeSecretRequirements, runtimeSecretContractFromEnvText, type RuntimeSecretContract, runArtifactRegistryCommand } from "../artifact-registry"; import { startJob } from "../jobs"; import { coreInternalFetch } from "../microservices"; import { codeQueueSourceImportPreflight, codeQueueSourceSubdir } from "../code-queue-source-guard"; import { d601K3sGuardShellLines, d601NativeKubeconfig } from "../d601-k3s-guard"; import { composeRuntimeEnvValue } from "../runtime-env"; import { compareDeployJsonExecutorMirrors, deployJsonCommitImage, deployJsonDriftResult, deployJsonSourceOfTruth, encodeDeployJsonServiceContract, hasDeployJsonExecutorContract, k3sManifestExecutorMirror, parseDeployJsonServiceContract, type DeployJsonExecutorMirror, type DeployJsonServiceContract, } from "../deploy-json-contract"; import { resolveArtifactRegistryTarget } from "../ops/targets"; import type { DeployAction } from "./types"; import { applyJob, devArtifactApplyJob, prodArtifactApplyJob, runApplyNow, runArtifactConsumerApplyNow, runProdArtifactApplyNow } from "./artifact-jobs"; import { blockedD601MaintenanceDeployServices, checkOrPlan, commitOverrideArtifactConsumerError, d601MaintenanceDeployBlockMessage, devArtifactLiveApplyBlockedResult, environmentDryRunPlan, prodArtifactConsumerLocalManifestResult, prodArtifactUnsupportedResult, prodArtifactUnsupportedServices, selectedDevArtifactServicesWithProdFallback, selectedDevTargetServices, selectedEnvironmentServices, unsupportedDevApplyServices } from "./check-plan"; import { readDeployManifest, readEnvironmentDeployManifest } from "./manifest"; import { deployHelp, isHelpArg, optionValue, parseOptions, resolveManifestCommits } from "./options"; import { devArtifactLiveApplyBlockedServiceIds, prodForbiddenTargetSideBuildServiceIds } from "./types"; export function runDeployGuardCommand(args: string[]): unknown { const [guardName] = args; if (guardName !== "code-queue-source") { return { ok: false, supported: false, error: "unsupported-deploy-guard", guard: guardName ?? null, supportedGuards: ["code-queue-source"], }; } const root = optionValue(args.slice(1), ["--root"]) ?? repoRoot; return codeQueueSourceImportPreflight(root); } export async function runDeployCommand(config: UniDeskConfig | null, args: string[]): Promise { const [actionRaw = "check"] = args; if (isHelpArg(actionRaw) || args.slice(1).some(isHelpArg)) return deployHelp(isHelpArg(actionRaw) ? undefined : actionRaw); if (actionRaw === "guard") return runDeployGuardCommand(args.slice(1)); if (!["check", "plan", "apply"].includes(actionRaw)) throw new Error("deploy command must be one of: check, plan, apply, guard"); const action = actionRaw as DeployAction; const options = parseOptions(args.slice(1)); if (options.environment !== null) { const commitOverrideError = commitOverrideArtifactConsumerError(options); if (commitOverrideError !== null) throw new Error(commitOverrideError); const { manifest, source } = readEnvironmentDeployManifest(options.environment); if (action === "check" || action === "plan") return environmentDryRunPlan(config, manifest, source, options, action); if (options.environment === "prod") { const unsupported = prodArtifactUnsupportedServices(manifest, options.serviceId); if (unsupported.length > 0) return prodArtifactUnsupportedResult(unsupported); if (options.dryRun || options.runNow) return await runProdArtifactApplyNow(manifest, options); return prodArtifactApplyJob(args, options); } const unsupported = unsupportedDevApplyServices(manifest, options.serviceId); if (unsupported.length > 0) { throw new Error(`deploy apply --env dev currently supports auth-broker/backend-core/frontend/baidu-netdisk/decision-center/mdtodo/claudeqq/code-queue/project-manager/oa-event-flow/code-queue-mgr/todo-note/findjob/pipeline/met-nonlinear artifact consumers; unsupported selected services: ${unsupported.join(", ")}. Use ci run-dev-e2e for smoke verification.`); } const devArtifactServices = selectedDevArtifactServicesWithProdFallback(manifest, options.serviceId); const devTargetServices = selectedDevTargetServices(manifest, options.serviceId); if (devArtifactServices.length > 0 && devTargetServices.length > 0) { throw new Error("deploy apply --env dev cannot mix artifact consumer services with target-side rollout services in one invocation; pass --service"); } if (devArtifactServices.length > 0) { const blocked = devArtifactServices.filter((service) => devArtifactLiveApplyBlockedServiceIds.has(service.id)); if (!options.dryRun && blocked.length > 0) return devArtifactLiveApplyBlockedResult(blocked); if (options.dryRun || options.runNow) return await runArtifactConsumerApplyNow(manifest, options, "dev", devArtifactServices); return devArtifactApplyJob(args, options); } if (config === null) throw new Error("deploy apply --env dev requires config.json"); if (!options.dryRun) { const blocked = blockedD601MaintenanceDeployServices(config, manifest, options.serviceId).filter((serviceId) => serviceId !== "decision-center"); if (blocked.length > 0) throw new Error(d601MaintenanceDeployBlockMessage(blocked)); } if (options.dryRun || options.runNow) return await runApplyNow(config, manifest, options); return applyJob(config, args, options); } if (config === null) throw new Error("deploy local manifest mode requires config.json"); const rawManifest = await readDeployManifest(options.file); if (action === "apply") { const blocked = blockedD601MaintenanceDeployServices(config, rawManifest, options.serviceId); if (blocked.length > 0) throw new Error(d601MaintenanceDeployBlockMessage(blocked)); } const manifest = resolveManifestCommits(rawManifest, options.serviceId); if (action === "check" || action === "plan") return await checkOrPlan(config, manifest, options, action); const prodArtifactConsumers = selectedEnvironmentServices(manifest, options.serviceId) .filter((service) => manifest.environment !== "dev" && prodForbiddenTargetSideBuildServiceIds.has(service.id)); if (prodArtifactConsumers.length > 0) return prodArtifactConsumerLocalManifestResult(prodArtifactConsumers); if (options.dryRun || options.runNow) return await runApplyNow(config, manifest, options); return applyJob(config, args, options); } export async function runCodeQueueDeployCompatCommand(_config: UniDeskConfig, args: string[]): Promise { if (args.includes("--skip-build")) throw new Error("codex deploy is disabled; --skip-build is not supported"); const selectedTarget = optionValue(args, ["--target", "--provider-id", "--provider"]); if (selectedTarget === undefined) throw new Error("codex deploy compatibility path is a legacy adapter and requires explicit --target D601; it no longer falls back to a hidden provider default"); const target = resolveArtifactRegistryTarget(selectedTarget); if (target.providerId !== "D601") throw new Error(`codex deploy compatibility path only supports D601; got ${target.providerId}`); throw new Error("codex deploy is disabled because D601 maintenance-channel direct deployment must not deploy Code Queue. Use the dev-only artifact consumer with deploy apply --env dev --service code-queue or artifact-registry deploy-service --env dev --service code-queue; production Code Queue artifact deployment is unsupported."); }