feat: extend main-server artifact consumers

This commit is contained in:
Codex
2026-05-20 01:07:52 +00:00
parent b28f693fda
commit 93d9488d36
17 changed files with 519 additions and 34 deletions
+56 -13
View File
@@ -136,9 +136,13 @@ const nativeK3sCtrAddress = "/run/k3s/containerd/containerd.sock";
const unideskRepoUrl = "https://github.com/pikasTech/unidesk";
const d601MaintenanceDeployAllowedServiceIds = new Set<string>(["backend-core", "k3sctl-adapter", "code-queue"]);
const devApplySupportedServiceIds = new Set<string>(["backend-core"]);
const devArtifactConsumerServiceIds = new Set<string>(["baidu-netdisk", "decision-center", "frontend"]);
const prodArtifactConsumerServiceIds = new Set<string>(["backend-core", "baidu-netdisk", "decision-center", "frontend"]);
const prodForbiddenTargetSideBuildServiceIds = prodArtifactConsumerServiceIds;
const devArtifactConsumerServiceIds = new Set<string>(["baidu-netdisk", "code-queue-mgr", "decision-center", "frontend", "oa-event-flow", "project-manager", "todo-note"]);
const devArtifactConsumerProdDesiredFallbackServiceIds = new Set<string>(["code-queue-mgr", "oa-event-flow", "project-manager", "todo-note"]);
const prodArtifactConsumerServiceIds = new Set<string>(["backend-core", "baidu-netdisk", "code-queue-mgr", "decision-center", "frontend", "oa-event-flow", "project-manager", "todo-note"]);
const prodArtifactLiveApplyBlockedServiceIds = new Map<string, string>([
["code-queue-mgr", "code-queue-mgr is the main-server Code Queue control-plane sidecar; live production apply requires explicit supervisor confirmation."],
["todo-note", "todo-note source is external to this repository and the current checked-in contract cannot prove /api/health deploy.commit/deploy.requestedCommit support."],
]);
const deployEnvironmentTargets: Record<DeployEnvironment, DeployEnvironmentTarget> = {
dev: {
environment: "dev",
@@ -201,7 +205,7 @@ export function deployHelp(action: string | undefined = undefined): Record<strin
},
options: [
{ name: "--file <path>", default: defaultDeployFile, description: "Desired-state manifest path relative to the repo root. JSON and ESM JS manifests are supported, for example deploy.json or develop.js. Local manifest apply allows k3sctl-adapter and explicit production code-queue controlled rollout on D601." },
{ name: "--env <dev|prod>", description: "Read the named environment from origin/master:deploy.json. Dev apply supports backend-core target-side rollout plus reviewed artifact consumers for frontend, baidu-netdisk, and decision-center; prod apply uses the D601 registry artifact consumer for backend-core, frontend, baidu-netdisk, and decision-center." },
{ name: "--env <dev|prod>", description: "Read the named environment from origin/master:deploy.json. Dev apply supports backend-core target-side rollout plus reviewed artifact consumers for frontend, baidu-netdisk, decision-center, project-manager, oa-event-flow, code-queue-mgr validation, while todo-note remains dry-run only. Prod apply uses the D601 registry artifact consumer for reviewed services; code-queue-mgr live apply is supervisor-gated and todo-note remains blocked." },
{ name: "--service <id>", description: "Limit reconcile to one service from the manifest." },
{ name: "--commit <full-sha>", description: "Prod artifact rollback/apply override for a selected service; the image must already exist in D601 registry." },
{ name: "--dry-run", description: "Prepare and validate without mutating the target service." },
@@ -569,6 +573,11 @@ function readEnvironmentDeployManifest(environment: DeployEnvironment): { manife
};
}
function readProdDeployManifestSnapshot(): DeployManifest {
const raw = runGitOrThrow(["show", `${deployEnvironmentTargets.prod.gitRef}:deploy.json`], repoRoot, `failed to read ${deployEnvironmentTargets.prod.gitRef}:deploy.json`).stdout;
return parseDeployManifest(JSON.parse(raw) as unknown, `${deployEnvironmentTargets.prod.gitRef}:deploy.json#environments.prod`, "prod");
}
async function readDeployManifest(file: string): Promise<DeployManifest> {
const path = resolve(repoRoot, file);
if (!existsSync(path)) throw new Error(`deploy manifest not found: ${path}`);
@@ -2607,6 +2616,15 @@ function environmentDryRunPlan(
reason: "No standardized dev D601 registry artifact consumer is implemented for this service; legacy maintenance-channel deployment is not allowed.",
}
: undefined,
liveApply: environment === "prod" && prodArtifactLiveApplyBlockedServiceIds.has(service.id)
? {
allowed: false,
reason: prodArtifactLiveApplyBlockedServiceIds.get(service.id),
dryRunOnly: true,
}
: environment === "prod"
? { allowed: prodArtifactConsumerServiceIds.has(service.id) }
: undefined,
})),
unsupported: environment === "prod" ? prodArtifactUnsupported : devUnsupported,
};
@@ -2615,6 +2633,7 @@ function environmentDryRunPlan(
function unsupportedDevApplyServices(manifest: DeployManifest, serviceId: string | null): string[] {
if (manifest.environment !== "dev") return [];
const services = serviceId === null ? manifest.services : manifest.services.filter((service) => service.id === serviceId);
if (serviceId !== null && services.length === 0 && devArtifactConsumerProdDesiredFallbackServiceIds.has(serviceId)) return [];
return services.map((service) => service.id).filter((id) => !devApplySupportedServiceIds.has(id) && !devArtifactConsumerServiceIds.has(id));
}
@@ -2636,6 +2655,7 @@ function selectedDevArtifactServices(manifest: DeployManifest, serviceId: string
function selectedDevTargetServices(manifest: DeployManifest, serviceId: string | null): DeployManifestService[] {
if (manifest.environment !== "dev") return [];
if (serviceId !== null && devArtifactConsumerProdDesiredFallbackServiceIds.has(serviceId) && !manifest.services.some((service) => service.id === serviceId)) return [];
return selectedEnvironmentServices(manifest, serviceId)
.filter((service) => devApplySupportedServiceIds.has(service.id) && !devArtifactConsumerServiceIds.has(service.id));
}
@@ -2646,6 +2666,14 @@ function selectedEnvironmentServices(manifest: DeployManifest, serviceId: string
return services;
}
function selectedDevArtifactServicesWithProdFallback(manifest: DeployManifest, serviceId: string | null): DeployManifestService[] {
if (manifest.environment !== "dev" || serviceId === null) return selectedDevArtifactServices(manifest, serviceId);
const selected = manifest.services.filter((service) => service.id === serviceId && devArtifactConsumerServiceIds.has(service.id));
if (selected.length > 0 || !devArtifactConsumerProdDesiredFallbackServiceIds.has(serviceId)) return selected;
const prodManifest = readProdDeployManifestSnapshot();
return prodManifest.services.filter((service) => service.id === serviceId);
}
function devUnsupportedServices(manifest: DeployManifest, serviceId: string | null): DeployManifestService[] {
if (manifest.environment !== "dev") return [];
return selectedEnvironmentServices(manifest, serviceId).filter((service) => !devApplySupportedServiceIds.has(service.id) && !devArtifactConsumerServiceIds.has(service.id));
@@ -2672,20 +2700,21 @@ function prodArtifactUnsupportedResult(services: DeployManifestService[]): Recor
};
}
function prodArtifactConsumerLocalManifestResult(services: DeployManifestService[]): Record<string, unknown> {
function prodArtifactLiveApplyBlockedResult(services: DeployManifestService[]): Record<string, unknown> {
return {
ok: false,
supported: false,
error: "prod-artifact-consumer-local-manifest-blocked",
supported: true,
error: "live-prod-apply-blocked",
services: services.map((service) => ({
id: service.id,
repo: service.repo,
commitId: service.commitId,
supported: true,
reason: "This service has a reviewed artifact consumer; production source-build deploy from a local manifest is blocked.",
liveApplyAllowed: false,
reason: prodArtifactLiveApplyBlockedServiceIds.get(service.id) ?? "live production artifact apply is blocked by policy",
})),
policy: "prod artifact consumers must enter through deploy apply --env prod so CD consumes an existing commit-pinned registry image and never falls back to source build or a dirty worktree",
commandShape: "bun scripts/cli.ts deploy apply --env prod --service <service-id> --commit <full-sha>",
policy: "prod dry-run/plan is available, but worker automation must not run live production apply for these services",
dryRunCommandShape: "bun scripts/cli.ts deploy apply --env prod --service <id> --dry-run",
};
}
@@ -2705,7 +2734,7 @@ async function runArtifactConsumerApplyNow(
"--commit", commit,
"--source-repo", service.repo,
"--deploy-ref", `${deployEnvironmentTargets[environment].gitRef}:deploy.json#environments.${environment}.services.${service.id}`,
...(environment === "prod" || service.id === "frontend" || service.id === "decision-center" ? ["--env", environment] : []),
"--env", environment,
"--timeout-ms", String(options.timeoutMs),
"--run-now",
...(options.dryRun ? ["--dry-run"] : []),
@@ -2731,6 +2760,10 @@ async function runProdArtifactApplyNow(manifest: DeployManifest, options: Deploy
const selected = selectedEnvironmentServices(manifest, options.serviceId);
const unsupported = selected.filter((service) => !prodArtifactConsumerServiceIds.has(service.id));
if (unsupported.length > 0) return prodArtifactUnsupportedResult(unsupported);
if (!options.dryRun) {
const blocked = selected.filter((service) => prodArtifactLiveApplyBlockedServiceIds.has(service.id));
if (blocked.length > 0) return prodArtifactLiveApplyBlockedResult(blocked);
}
return runArtifactConsumerApplyNow(manifest, options, "prod", selected);
}
@@ -2739,6 +2772,16 @@ function prodArtifactApplyJob(args: string[], options: DeployOptions): Record<st
throw new Error("deploy apply --env prod --dry-run should run in the foreground so the plan is visible immediately");
}
const runArgs = args.includes("--run-now") ? args : [...args, "--run-now"];
if (options.serviceId === null) {
throw new Error("deploy apply --env prod without --service is not allowed for mixed artifact consumers; use --service to avoid applying supervisor-gated services accidentally");
}
if (prodArtifactLiveApplyBlockedServiceIds.has(options.serviceId)) {
return prodArtifactLiveApplyBlockedResult([{
id: options.serviceId,
repo: "",
commitId: options.commitOverride ?? "",
}]);
}
const command = [process.execPath, rootPath("scripts", "cli.ts"), "deploy", ...runArgs];
const source = `${deployEnvironmentTargets.prod.gitRef}:deploy.json#environments.prod`;
const job = startJob("deploy_prod_artifact_apply", command, `Deploy prod artifact consumer from ${source}${options.serviceId === null ? "" : ` service=${options.serviceId}`}`);
@@ -2826,9 +2869,9 @@ export async function runDeployCommand(config: UniDeskConfig | null, args: strin
}
const unsupported = unsupportedDevApplyServices(manifest, options.serviceId);
if (unsupported.length > 0) {
throw new Error(`deploy apply --env dev currently supports backend-core target-side rollout plus frontend/baidu-netdisk/decision-center artifact consumers; unsupported selected services: ${unsupported.join(", ")}. Use ci run-dev-e2e for smoke verification.`);
throw new Error(`deploy apply --env dev currently supports backend-core target-side rollout plus frontend/baidu-netdisk/decision-center/project-manager/oa-event-flow/code-queue-mgr artifact consumers; unsupported selected services: ${unsupported.join(", ")}. todo-note is dry-run only until runtime verification is proven. Use ci run-dev-e2e for smoke verification.`);
}
const devArtifactServices = selectedDevArtifactServices(manifest, options.serviceId);
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");