feat: extend main-server artifact consumers
This commit is contained in:
+56
-13
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user