fix: expose deploy artifact matrix plan fields
This commit is contained in:
@@ -112,6 +112,8 @@ Production `code-queue-mgr` is a separate main-server Compose sidecar artifact c
|
||||
|
||||
`bun scripts/cli.ts deploy plan --env dev [--service <id>]` reads `origin/master:deploy.json#environments.dev` and prints a dry-run environment plan without checking or mutating live runtime resources. `deploy check --env dev` uses the same dry-run environment plan. `--env prod` is available for parity as a dry-run planning path; it reads `origin/master:deploy.json#environments.prod` and must not use a dirty local `deploy.json`.
|
||||
|
||||
Environment plan output must be sufficient to review the artifact matrix without running a live apply. Each service item includes `deploymentPath`, `artifactConsumer.consumerKind`, `artifactConsumer.registryImage`, `artifactConsumer.noRuntimeSourceBuild`, `artifactConsumer.dryRunOnly`, `target`, `validation` and `liveApply` where relevant. `consumerKind=d601-direct-compose` means the reviewed consumer touches only the D601 Docker/Compose service and private health path; `consumerKind=d601-k3s-managed` means the reviewed consumer imports the artifact into native k3s/containerd and verifies through the Kubernetes API service proxy; `consumerKind=main-server-compose` means the reviewed consumer streams or loads the D601 artifact into the main-server Compose service; `consumerKind=d601-dev-target-side-build` is reserved for the controlled dev backend-core source-build exception. Artifact consumer plan items must explicitly report `noRuntimeSourceBuild=true` and list forbidden build/public exposure actions. Blocked or gated services must keep structured `dryRunOnly` / `blockedReason` output, for example `met-nonlinear` `runtime-verification-blocked` and `k3sctl-adapter` supervisor-only production apply.
|
||||
|
||||
`bun scripts/cli.ts deploy apply [--file deploy.json | --env dev|prod] [--service <id>] [--commit <full-sha>] [--dry-run] [--force]` starts an asynchronous job only for supported targets. Use `bun scripts/cli.ts job status <jobId> --tail-bytes 30000` to observe progress. `--dry-run` resolves the same plan but does not build or replace runtime objects. `--force` rebuilds even when the live commit matches. Environment apply is not the dev e2e trigger; use `bun scripts/cli.ts ci run-dev-e2e` for the Git-controlled temporary namespace smoke flow. `--env dev` apply is enabled for persistent D601 `backend-core` target-side rollout and for `frontend`/`baidu-netdisk`/`decision-center`/`mdtodo`/`claudeqq`/dev-only `code-queue`/`project-manager`/`oa-event-flow`/`code-queue-mgr`/`todo-note`/`findjob`/`pipeline`/`met-nonlinear` artifact consumers. `--env prod` apply exposes the D601 registry artifact consumer for `backend-core`, `frontend`, `baidu-netdisk`, `decision-center`, `mdtodo`, `claudeqq`, `project-manager`, `oa-event-flow`, `todo-note`, `findjob`, `pipeline` and `met-nonlinear`; `code-queue-mgr` prod live apply is supervisor-gated and `k3sctl-adapter` is plan/dry-run only. `--commit` may override one selected reviewed artifact consumer in either dev or prod, for example `deploy apply --env dev --service frontend --commit <release-v1-full-sha>`, and the image must already exist as `127.0.0.1:5000/unidesk/<service-id>:<commit>`. Unsupported prod services, especially `code-queue`, return a structured `unsupported` payload instead of silently falling back to a maintenance-channel source build.
|
||||
|
||||
All deploy commands output JSON. Long operations must use `.state/jobs/` and bounded log tails; no deploy path may succeed with missing progress output.
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
import { spawnSync } from "node:child_process";
|
||||
|
||||
function assertCondition(condition: unknown, message: string, detail: unknown = {}): void {
|
||||
if (!condition) throw new Error(`${message}: ${JSON.stringify(detail)}`);
|
||||
}
|
||||
|
||||
function asRecord(value: unknown, label: string): Record<string, unknown> {
|
||||
assertCondition(typeof value === "object" && value !== null && !Array.isArray(value), `${label} must be an object`, value);
|
||||
return value as Record<string, unknown>;
|
||||
}
|
||||
|
||||
function runDeployPlan(environment: "dev" | "prod", serviceId: string): Record<string, unknown> {
|
||||
const result = spawnSync("bun", ["scripts/cli.ts", "deploy", "plan", "--env", environment, "--service", serviceId], {
|
||||
cwd: process.cwd(),
|
||||
encoding: "utf8",
|
||||
maxBuffer: 8 * 1024 * 1024,
|
||||
});
|
||||
assertCondition(result.status === 0, `deploy plan should exit 0 for ${environment}/${serviceId}`, {
|
||||
status: result.status,
|
||||
stdout: result.stdout.slice(-2000),
|
||||
stderr: result.stderr.slice(-2000),
|
||||
});
|
||||
const envelope = asRecord(JSON.parse(result.stdout) as unknown, "cli envelope");
|
||||
assertCondition(envelope.ok === true, `deploy plan envelope should be ok for ${environment}/${serviceId}`, envelope);
|
||||
const data = asRecord(envelope.data, "deploy plan data");
|
||||
const services = Array.isArray(data.services) ? data.services : [];
|
||||
assertCondition(services.length === 1, `deploy plan should return one service for ${environment}/${serviceId}`, data);
|
||||
return asRecord(services[0], "service plan");
|
||||
}
|
||||
|
||||
function listIncludes(value: unknown, expected: string): boolean {
|
||||
return Array.isArray(value) && value.some((item) => item === expected);
|
||||
}
|
||||
|
||||
const findjob = runDeployPlan("dev", "findjob");
|
||||
const findjobArtifact = asRecord(findjob.artifactConsumer, "findjob artifactConsumer");
|
||||
const findjobTarget = asRecord(findjob.target, "findjob target");
|
||||
assertCondition(findjobArtifact.consumerKind === "d601-direct-compose", "findjob dev must be a D601 direct Compose artifact consumer", findjobArtifact);
|
||||
assertCondition(findjobArtifact.noRuntimeSourceBuild === true, "findjob dry-run must declare no runtime source build", findjobArtifact);
|
||||
assertCondition(findjobTarget.runtimeHost === "D601", "findjob target should be D601", findjobTarget);
|
||||
assertCondition(findjobTarget.composeService === "server", "findjob compose service should be server", findjobTarget);
|
||||
assertCondition(listIncludes(findjobTarget.forbiddenActions, "docker compose build"), "findjob plan should forbid Compose build", findjobTarget);
|
||||
|
||||
const mdtodo = runDeployPlan("prod", "mdtodo");
|
||||
const mdtodoArtifact = asRecord(mdtodo.artifactConsumer, "mdtodo artifactConsumer");
|
||||
const mdtodoTarget = asRecord(mdtodo.target, "mdtodo target");
|
||||
assertCondition(mdtodoArtifact.consumerKind === "d601-k3s-managed", "mdtodo prod must be a D601 k3s-managed artifact consumer", mdtodoArtifact);
|
||||
assertCondition(mdtodoArtifact.noRuntimeSourceBuild === true, "mdtodo dry-run must declare no runtime source build", mdtodoArtifact);
|
||||
assertCondition(mdtodoTarget.namespace === "unidesk", "mdtodo prod target namespace should be unidesk", mdtodoTarget);
|
||||
assertCondition(listIncludes(mdtodoTarget.forbiddenActions, "NodePort"), "mdtodo plan should forbid NodePort", mdtodoTarget);
|
||||
|
||||
const metNonlinear = runDeployPlan("prod", "met-nonlinear");
|
||||
const metArtifact = asRecord(metNonlinear.artifactConsumer, "met-nonlinear artifactConsumer");
|
||||
assertCondition(metArtifact.consumerKind === "d601-direct-compose", "met-nonlinear should remain D601 direct Compose", metArtifact);
|
||||
assertCondition(metArtifact.dryRunOnly === true, "met-nonlinear must remain dry-run only", metArtifact);
|
||||
assertCondition(String(metArtifact.blockedReason ?? "").includes("runtime-verification-blocked"), "met-nonlinear blocked reason should mention runtime verification", metArtifact);
|
||||
|
||||
const k3sctl = runDeployPlan("prod", "k3sctl-adapter");
|
||||
const k3sctlArtifact = asRecord(k3sctl.artifactConsumer, "k3sctl-adapter artifactConsumer");
|
||||
const k3sctlTarget = asRecord(k3sctl.target, "k3sctl-adapter target");
|
||||
assertCondition(k3sctlArtifact.consumerKind === "d601-direct-compose", "k3sctl-adapter should be D601 direct Compose", k3sctlArtifact);
|
||||
assertCondition(k3sctlArtifact.dryRunOnly === true, "k3sctl-adapter must be dry-run only for worker automation", k3sctlArtifact);
|
||||
assertCondition(String(k3sctlArtifact.blockedReason ?? "").includes("supervisor"), "k3sctl-adapter blocked reason should mention supervisor confirmation", k3sctlArtifact);
|
||||
assertCondition(k3sctlTarget.composeService === "k3sctl-adapter", "k3sctl target service should be k3sctl-adapter", k3sctlTarget);
|
||||
|
||||
process.stdout.write(`${JSON.stringify({
|
||||
ok: true,
|
||||
checks: [
|
||||
"deploy plan distinguishes D601 direct Compose from D601 k3s-managed artifact consumers",
|
||||
"deploy plan exposes no-runtime-source-build and forbidden build/public-port actions",
|
||||
"met-nonlinear remains runtime-verification-blocked",
|
||||
"k3sctl-adapter remains supervisor-only dry-run",
|
||||
],
|
||||
}, null, 2)}\n`);
|
||||
@@ -284,6 +284,7 @@ export function runChecks(config: UniDeskConfig, options: CheckOptions = default
|
||||
fileItem("scripts/code-queue-pr-preflight-contract-test.ts"),
|
||||
fileItem("scripts/src/ci.ts"),
|
||||
fileItem("scripts/src/e2e.ts"),
|
||||
fileItem("scripts/deploy-artifact-matrix-contract-test.ts"),
|
||||
fileItem("scripts/code-queue-prompt-observation-test.ts"),
|
||||
fileItem("scripts/gh-cli-issue-guard-contract-test.ts"),
|
||||
fileItem("scripts/gh-cli-pr-contract-test.ts"),
|
||||
@@ -302,6 +303,7 @@ export function runChecks(config: UniDeskConfig, options: CheckOptions = default
|
||||
items.push(commandItem("code-queue:issue3-diagnostics-and-image-preflight", ["bun", "scripts/code-queue-issue3-regression-test.ts"], 30_000));
|
||||
items.push(commandItem("code-queue:trace-summary-contract", ["bun", "scripts/code-queue-trace-summary-contract-test.ts"], 30_000));
|
||||
items.push(commandItem("code-queue:pr-preflight-contract", ["bun", "scripts/code-queue-pr-preflight-contract-test.ts"], 30_000));
|
||||
items.push(commandItem("deploy:artifact-matrix-contract", ["bun", "scripts/deploy-artifact-matrix-contract-test.ts"], 30_000));
|
||||
items.push(commandItem("code-queue:active-run-heartbeat-visible", ["bun", "scripts/code-queue-liveness-diagnostics-test.ts", "--only", "code-queue:active-run-heartbeat-visible"], 30_000));
|
||||
items.push(commandItem("code-queue:trace-gap-not-stale", ["bun", "scripts/code-queue-liveness-diagnostics-test.ts", "--only", "code-queue:trace-gap-not-stale"], 30_000));
|
||||
items.push(commandItem("code-queue:stale-active-owner-expired", ["bun", "scripts/code-queue-liveness-diagnostics-test.ts", "--only", "code-queue:stale-active-owner-expired"], 30_000));
|
||||
@@ -318,6 +320,7 @@ export function runChecks(config: UniDeskConfig, options: CheckOptions = default
|
||||
items.push(skippedItem("code-queue:issue3-diagnostics-and-image-preflight", "Code Queue issue #3 regression fixtures are opt-in with script checks", "--scripts-typecheck or --full"));
|
||||
items.push(skippedItem("code-queue:trace-summary-contract", "Code Queue trace summary contract is opt-in with script checks", "--scripts-typecheck or --full"));
|
||||
items.push(skippedItem("code-queue:pr-preflight-contract", "Code Queue PR preflight contract is opt-in with script checks", "--scripts-typecheck or --full"));
|
||||
items.push(skippedItem("deploy:artifact-matrix-contract", "deploy artifact matrix contract is opt-in with script checks", "--scripts-typecheck or --full"));
|
||||
items.push(skippedItem("code-queue:liveness-diagnostics-fixtures", "Code Queue liveness diagnostics fixtures are opt-in with script checks", "--scripts-typecheck or --full"));
|
||||
items.push(skippedItem("baidu-netdisk:artifact-guard-contract", "Baidu Netdisk artifact guard contract is opt-in with script checks", "--scripts-typecheck or --full"));
|
||||
items.push(skippedItem("artifact-registry:direct-compose-dry-run-matrix", "main-server direct artifact consumer dry-run matrix is opt-in with script checks", "--scripts-typecheck or --full"));
|
||||
|
||||
+186
-39
@@ -119,6 +119,8 @@ interface DeployEnvironmentManifestSource {
|
||||
fetchedAt: string;
|
||||
}
|
||||
|
||||
type ArtifactConsumerPlanKind = "main-server-compose" | "d601-direct-compose" | "d601-k3s-managed" | "d601-dev-target-side-build" | "unsupported";
|
||||
|
||||
const defaultDeployFile = "deploy.json";
|
||||
const defaultTimeoutMs = 900_000;
|
||||
const resolveFetchTimeout = "120s";
|
||||
@@ -148,6 +150,9 @@ const prodArtifactLiveApplyBlockedServiceIds = new Map<string, string>([
|
||||
["met-nonlinear", "met-nonlinear is blocked for live artifact deploy because config.json points at docker/unidesk/Dockerfile.ml while the compose service is met-nonlinear-ts."],
|
||||
["k3sctl-adapter", "k3sctl-adapter is an infrastructure control bridge; this executor exposes artifact consumer plan/dry-run only. Real production deployment requires supervisor confirmation outside this task."],
|
||||
]);
|
||||
const artifactConsumerDryRunBlockedServiceIds = new Map<string, string>([
|
||||
["met-nonlinear", "runtime-verification-blocked: CI publishes the ML image Dockerfile contract, but the long-running Compose service is met-nonlinear-ts; CD cannot yet prove the running container image label matches the requested commit."],
|
||||
]);
|
||||
const deployEnvironmentTargets: Record<DeployEnvironment, DeployEnvironmentTarget> = {
|
||||
dev: {
|
||||
environment: "dev",
|
||||
@@ -881,6 +886,133 @@ function environmentTargetSummary(target: DeployEnvironmentTarget): Record<strin
|
||||
};
|
||||
}
|
||||
|
||||
function artifactConsumerPlanKind(service: UniDeskMicroserviceConfig, environment: DeployEnvironment): ArtifactConsumerPlanKind {
|
||||
if (environment === "dev" && devApplySupportedServiceIds.has(service.id) && !devArtifactConsumerServiceIds.has(service.id)) {
|
||||
return "d601-dev-target-side-build";
|
||||
}
|
||||
if (service.deployment.mode === "k3sctl-managed") return "d601-k3s-managed";
|
||||
if (service.providerId === "D601" && service.deployment.mode === "unidesk-direct") return "d601-direct-compose";
|
||||
if (targetIsMain(service) && isDirectComposeDeployMode(service)) return "main-server-compose";
|
||||
return "unsupported";
|
||||
}
|
||||
|
||||
function deploymentPathForEnvironmentService(service: UniDeskMicroserviceConfig, environment: DeployEnvironment): string {
|
||||
return deploymentPathForEnvironmentServiceId(service.id, environment);
|
||||
}
|
||||
|
||||
function deploymentPathForEnvironmentServiceId(serviceId: string, environment: DeployEnvironment): string {
|
||||
if (environment === "prod") return prodArtifactConsumerServiceIds.has(serviceId) ? "d601-registry-artifact-consumer" : "unsupported";
|
||||
if (devArtifactConsumerServiceIds.has(serviceId)) return "d601-registry-artifact-consumer";
|
||||
if (devApplySupportedServiceIds.has(serviceId)) return "d601-dev-target-side-build";
|
||||
return "unsupported";
|
||||
}
|
||||
|
||||
function artifactConsumerPlanTarget(service: UniDeskMicroserviceConfig, environment: DeployEnvironment): Record<string, unknown> {
|
||||
const kind = artifactConsumerPlanKind(service, environment);
|
||||
const liveBlockReason = environment === "prod" ? prodArtifactLiveApplyBlockedServiceIds.get(service.id) ?? null : null;
|
||||
const dryRunBlockedReason = artifactConsumerDryRunBlockedServiceIds.get(service.id) ?? null;
|
||||
const targetImage = artifactConsumerTargetImageHint(service, environment);
|
||||
const common = {
|
||||
consumerKind: kind,
|
||||
runtimeHost: kind === "main-server-compose" ? "main-server" : "D601",
|
||||
deploymentMode: service.deployment.mode,
|
||||
noRuntimeSourceBuild: kind !== "d601-dev-target-side-build",
|
||||
dryRunOnly: dryRunBlockedReason !== null || (environment === "prod" && prodArtifactLiveApplyBlockedServiceIds.has(service.id)),
|
||||
blockedReason: dryRunBlockedReason ?? liveBlockReason,
|
||||
};
|
||||
if (kind === "d601-k3s-managed") {
|
||||
return {
|
||||
...common,
|
||||
namespace: service.deployment.namespace ?? (environment === "dev" ? "unidesk-dev" : k8sNamespace),
|
||||
deployment: service.repository.composeService,
|
||||
service: service.deployment.k3sServiceId ?? service.repository.composeService,
|
||||
containerName: service.repository.containerName,
|
||||
manifest: service.repository.composeFile,
|
||||
targetImage,
|
||||
deployCommandShape: "registry manifest check + native k3s containerd import + Kubernetes Deployment image/env/annotation update + API service proxy health",
|
||||
forbiddenActions: ["docker build", "docker compose build", "NodePort", "hostPort", "provider-gateway direct business backend"],
|
||||
};
|
||||
}
|
||||
if (kind === "d601-direct-compose" || kind === "main-server-compose") {
|
||||
return {
|
||||
...common,
|
||||
workDir: targetWorkDir(service),
|
||||
composeFile: service.repository.composeFile,
|
||||
composeService: service.repository.composeService,
|
||||
containerName: service.repository.containerName,
|
||||
targetImage,
|
||||
deployCommandShape: `docker compose up -d --no-build --no-deps --force-recreate ${service.repository.composeService}`,
|
||||
forbiddenActions: ["docker build", "docker compose build", "docker compose up --build", "dirty worktree source deploy"],
|
||||
};
|
||||
}
|
||||
if (kind === "d601-dev-target-side-build") {
|
||||
return {
|
||||
...common,
|
||||
namespace: service.deployment.namespace ?? "unidesk-dev",
|
||||
deployment: service.repository.composeService,
|
||||
manifest: service.repository.composeFile,
|
||||
workDir: targetWorkDir(service),
|
||||
deployCommandShape: "D601 dev target-side source materialization/build/import/rollout",
|
||||
forbiddenActions: ["production namespace mutation", "production database mutation"],
|
||||
};
|
||||
}
|
||||
return common;
|
||||
}
|
||||
|
||||
function artifactConsumerTargetImageHint(service: UniDeskMicroserviceConfig, environment: DeployEnvironment): string {
|
||||
if (service.id === "frontend" && environment === "prod") return "unidesk-frontend";
|
||||
if (service.id === "frontend" && environment === "dev") return "unidesk-frontend:dev";
|
||||
if (service.id === "k3sctl-adapter") return "unidesk-k3sctl-adapter:d601";
|
||||
if (service.id === "met-nonlinear") return "met-nonlinear-ml:tf26";
|
||||
if (service.deployment.mode === "k3sctl-managed") return `unidesk-${service.id}:${environment === "dev" ? "dev" : "d601"}`;
|
||||
const container = service.repository.containerName;
|
||||
if (container.endsWith("-backend")) return container.slice(0, -"-backend".length);
|
||||
return container;
|
||||
}
|
||||
|
||||
function artifactConsumerPlanValidation(service: UniDeskMicroserviceConfig, environment: DeployEnvironment): string[] {
|
||||
const kind = artifactConsumerPlanKind(service, environment);
|
||||
if (kind === "d601-dev-target-side-build") {
|
||||
return [
|
||||
"reads origin/master:deploy.json for dev desired state",
|
||||
"materializes and builds only the selected dev service on D601",
|
||||
"rolls out only unidesk-dev resources",
|
||||
];
|
||||
}
|
||||
const base = [
|
||||
"reads origin/master:deploy.json for commit intent",
|
||||
"requires a commit-pinned artifact in 127.0.0.1:5000",
|
||||
"verifies image labels for service id, source commit, and Dockerfile",
|
||||
"does not build source on the runtime target",
|
||||
];
|
||||
if (kind === "d601-k3s-managed") {
|
||||
return [
|
||||
...base,
|
||||
"imports the artifact into native k3s containerd",
|
||||
"verifies Deployment metadata and health through the Kubernetes API service proxy",
|
||||
];
|
||||
}
|
||||
if (kind === "d601-direct-compose" || kind === "main-server-compose") {
|
||||
return [
|
||||
...base,
|
||||
"recreates only the selected Compose service with --no-build --no-deps --force-recreate",
|
||||
"verifies running image labels and private service health",
|
||||
];
|
||||
}
|
||||
return ["unsupported service remains blocked before source materialization or runtime mutation"];
|
||||
}
|
||||
|
||||
function environmentPlanServiceConfig(config: UniDeskConfig | null, service: DeployManifestService, environment: DeployEnvironment): UniDeskMicroserviceConfig | null {
|
||||
if (environment === "dev") {
|
||||
const devService = devK3sDeployService(service.id);
|
||||
if (devService !== undefined) return devService;
|
||||
}
|
||||
const configured = config?.microservices.find((candidate) => candidate.id === service.id);
|
||||
if (configured !== undefined) return configured;
|
||||
if (config !== null) return coreDeployService(config, service.id, environment) ?? null;
|
||||
return null;
|
||||
}
|
||||
|
||||
function targetIsMain(service: UniDeskMicroserviceConfig): boolean {
|
||||
return service.providerId === "main-server";
|
||||
}
|
||||
@@ -2657,6 +2789,7 @@ async function checkOrPlan(config: UniDeskConfig, manifest: DeployManifest, opti
|
||||
}
|
||||
|
||||
function environmentDryRunPlan(
|
||||
config: UniDeskConfig | null,
|
||||
manifest: DeployManifest,
|
||||
source: DeployEnvironmentManifestSource,
|
||||
options: DeployOptions,
|
||||
@@ -2707,49 +2840,63 @@ function environmentDryRunPlan(
|
||||
deployJsonFromWorktree: false,
|
||||
dirtyWorktreeUsed: false,
|
||||
},
|
||||
services: services.map((service) => ({
|
||||
id: service.id,
|
||||
repo: service.repo,
|
||||
commitId: service.commitId,
|
||||
environment,
|
||||
targetNamespace: target.namespace,
|
||||
providerId: target.provider.providerId,
|
||||
databaseFingerprint: fingerprint,
|
||||
deploymentPath: environment === "prod"
|
||||
? prodArtifactConsumerServiceIds.has(service.id)
|
||||
? "d601-registry-artifact-consumer"
|
||||
: "unsupported"
|
||||
: devArtifactConsumerServiceIds.has(service.id)
|
||||
? "d601-registry-artifact-consumer"
|
||||
: devApplySupportedServiceIds.has(service.id)
|
||||
? "d601-dev-target-side-build"
|
||||
: "unsupported",
|
||||
unsupported: (environment === "prod" && !prodArtifactConsumerServiceIds.has(service.id))
|
||||
|| (environment === "dev" && !devApplySupportedServiceIds.has(service.id) && !devArtifactConsumerServiceIds.has(service.id))
|
||||
? {
|
||||
ok: false,
|
||||
supported: false,
|
||||
reason: environment === "prod"
|
||||
? "No standardized prod D601 registry artifact consumer is implemented for this service; legacy maintenance-channel deployment is not allowed."
|
||||
: "No standardized dev deployment or artifact validation path is implemented for this service.",
|
||||
}
|
||||
: environment === "dev" && !devApplySupportedServiceIds.has(service.id) && !devArtifactConsumerServiceIds.has(service.id)
|
||||
services: services.map((service) => {
|
||||
const serviceConfig = environmentPlanServiceConfig(config, service, environment);
|
||||
const unsupported = (environment === "prod" && !prodArtifactConsumerServiceIds.has(service.id))
|
||||
|| (environment === "dev" && !devApplySupportedServiceIds.has(service.id) && !devArtifactConsumerServiceIds.has(service.id));
|
||||
const dryRunBlockedReason = artifactConsumerDryRunBlockedServiceIds.get(service.id) ?? null;
|
||||
const planTarget = serviceConfig === null ? null : artifactConsumerPlanTarget(serviceConfig, environment);
|
||||
return {
|
||||
id: service.id,
|
||||
repo: service.repo,
|
||||
commitId: service.commitId,
|
||||
environment,
|
||||
targetNamespace: target.namespace,
|
||||
providerId: target.provider.providerId,
|
||||
databaseFingerprint: fingerprint,
|
||||
deploymentPath: serviceConfig === null
|
||||
? deploymentPathForEnvironmentServiceId(service.id, environment)
|
||||
: deploymentPathForEnvironmentService(serviceConfig, environment),
|
||||
artifactConsumer: serviceConfig === null ? {
|
||||
consumerKind: "unsupported",
|
||||
noRuntimeSourceBuild: true,
|
||||
dryRunOnly: true,
|
||||
blockedReason: "service is missing from config.json and no synthetic deploy service is available for this environment",
|
||||
} : {
|
||||
consumerKind: artifactConsumerPlanKind(serviceConfig, environment),
|
||||
registryImage: `127.0.0.1:5000/unidesk/${service.id}:${service.commitId}`,
|
||||
noRuntimeSourceBuild: artifactConsumerPlanKind(serviceConfig, environment) !== "d601-dev-target-side-build",
|
||||
dryRunOnly: (environment === "prod" && prodArtifactLiveApplyBlockedServiceIds.has(service.id)) || dryRunBlockedReason !== null,
|
||||
blockedReason: dryRunBlockedReason ?? (environment === "prod" ? prodArtifactLiveApplyBlockedServiceIds.get(service.id) ?? null : null),
|
||||
},
|
||||
target: planTarget,
|
||||
validation: serviceConfig === null ? undefined : artifactConsumerPlanValidation(serviceConfig, environment),
|
||||
unsupported: unsupported
|
||||
? {
|
||||
ok: false,
|
||||
supported: false,
|
||||
reason: "No standardized dev D601 registry artifact consumer is implemented for this service; legacy maintenance-channel deployment is not allowed.",
|
||||
reason: environment === "prod"
|
||||
? "No standardized prod D601 registry artifact consumer is implemented for this service; legacy maintenance-channel deployment is not allowed."
|
||||
: "No standardized dev deployment or artifact validation path is implemented for this service.",
|
||||
}
|
||||
: 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,
|
||||
})),
|
||||
liveApply: dryRunBlockedReason !== null
|
||||
? {
|
||||
allowed: false,
|
||||
reason: dryRunBlockedReason,
|
||||
dryRunOnly: true,
|
||||
}
|
||||
: 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,
|
||||
};
|
||||
}
|
||||
@@ -3031,7 +3178,7 @@ export async function runDeployCommand(config: UniDeskConfig | null, args: strin
|
||||
const commitOverrideError = commitOverrideArtifactConsumerError(options);
|
||||
if (commitOverrideError !== null) throw new Error(commitOverrideError);
|
||||
const { manifest, source } = readEnvironmentDeployManifest(options.environment);
|
||||
if (action === "check" || action === "plan") return environmentDryRunPlan(manifest, source, options, action);
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user