test: guard code queue artifact dry-run readiness
This commit is contained in:
@@ -140,6 +140,8 @@ interface ArtifactConsumerSpec {
|
||||
targets: Partial<Record<ArtifactDeployEnvironment, ArtifactConsumerTarget>>;
|
||||
prodLiveApply: "enabled" | "supervisor-only" | "unsupported";
|
||||
prodLiveBlockReason?: string;
|
||||
devLiveApply?: "enabled" | "supervisor-only";
|
||||
devLiveBlockReason?: string;
|
||||
runtimeVerification?: "strict" | "blocked";
|
||||
runtimeVerificationBlockReason?: string;
|
||||
}
|
||||
@@ -503,7 +505,9 @@ const artifactConsumerSpecs: Record<string, ArtifactConsumerSpec> = {
|
||||
registryRepository: "unidesk/code-queue",
|
||||
dockerfile: "src/components/microservices/code-queue/Dockerfile",
|
||||
prodLiveApply: "unsupported",
|
||||
prodLiveBlockReason: "code-queue is dev-only for artifact consumer validation and has no prod artifact deploy, rollout, or manifest mutation target.",
|
||||
prodLiveBlockReason: "code-queue is dev-only for artifact consumer validation and has no production artifact deploy, production rollout, or production manifest mutation target.",
|
||||
devLiveApply: "supervisor-only",
|
||||
devLiveBlockReason: "Code Queue DEV live apply is self-bootstrap sensitive: a running Code Queue task may plan only dry-run evidence. A human operator or supervisor must explicitly authorize DEV apply outside Code Queue after reviewing the CI artifact digest and dry-run target list.",
|
||||
targets: {
|
||||
dev: {
|
||||
targetImage: "unidesk-code-queue:dev",
|
||||
@@ -1315,6 +1319,10 @@ function unsupportedService(serviceId: string, options: ArtifactRegistryOptions)
|
||||
|
||||
function unsupportedEnvironment(spec: ArtifactConsumerSpec, options: ArtifactRegistryOptions): Record<string, unknown> {
|
||||
const environment = options.environment ?? "prod";
|
||||
const codeQueueGuard = spec.serviceId === "code-queue" ? codeQueueSelfBootstrapGuard(environment) : undefined;
|
||||
const reason = spec.serviceId === "code-queue" && environment === "prod"
|
||||
? spec.prodLiveBlockReason ?? "Code Queue production artifact deploy, rollout and manifest mutation are intentionally unsupported in this phase."
|
||||
: `No standardized ${environment} registry artifact consumer is implemented for ${spec.serviceId}.`;
|
||||
return {
|
||||
ok: false,
|
||||
supported: false,
|
||||
@@ -1322,14 +1330,125 @@ function unsupportedEnvironment(spec: ArtifactConsumerSpec, options: ArtifactReg
|
||||
serviceId: spec.serviceId,
|
||||
environment,
|
||||
providerId: options.providerId,
|
||||
reason: `No standardized ${environment} registry artifact consumer is implemented for ${spec.serviceId}.`,
|
||||
reason,
|
||||
supportedEnvironments: Object.keys(spec.targets),
|
||||
requiresSupervisorApproval: spec.serviceId === "code-queue",
|
||||
selfBootstrapGuard: codeQueueGuard,
|
||||
affectedRuntime: spec.serviceId === "code-queue" ? codeQueueAffectedRuntime(environment, false) : undefined,
|
||||
excludedTargets: spec.serviceId === "code-queue" ? codeQueueExcludedTargets(environment) : undefined,
|
||||
policy: "artifact CD must not silently fall back to maintenance-channel source builds or legacy direct deployment",
|
||||
};
|
||||
}
|
||||
|
||||
function artifactConsumerLivePolicy(spec: ArtifactConsumerSpec, environment: ArtifactDeployEnvironment): "enabled" | "supervisor-only" | "unsupported" {
|
||||
return environment === "prod" ? spec.prodLiveApply : spec.devLiveApply ?? "enabled";
|
||||
}
|
||||
|
||||
function artifactConsumerLiveBlockReason(spec: ArtifactConsumerSpec, environment: ArtifactDeployEnvironment): string | null {
|
||||
if (spec.runtimeVerificationBlockReason !== undefined) return spec.runtimeVerificationBlockReason;
|
||||
return environment === "prod" ? spec.prodLiveBlockReason ?? null : spec.devLiveBlockReason ?? null;
|
||||
}
|
||||
|
||||
function codeQueueExcludedTargets(environment: ArtifactDeployEnvironment): Record<string, unknown>[] {
|
||||
const excluded: Record<string, unknown>[] = [
|
||||
{
|
||||
namespace: "unidesk",
|
||||
deployments: ["code-queue", "code-queue-read", "code-queue-write", "d601-provider-egress-proxy", "d601-tcp-egress-gateway"],
|
||||
reason: "production Code Queue execution-plane Deployments are outside this artifact consumer and must not be mutated by dry-run or self-bootstrap automation.",
|
||||
},
|
||||
{
|
||||
operations: ["restart scheduler", "restart runner", "interrupt active tasks", "cancel active tasks"],
|
||||
reason: "artifact CD planning must not alter scheduler/runner lifecycle or active task control state.",
|
||||
},
|
||||
];
|
||||
if (environment === "dev") {
|
||||
excluded.push({
|
||||
namespace: "unidesk-dev",
|
||||
nonDryRunMutation: "requires explicit supervisor or human authorization outside the running Code Queue task",
|
||||
reason: "DEV is the only Code Queue artifact consumer target, but self-bootstrap automation may produce dry-run evidence only.",
|
||||
});
|
||||
}
|
||||
return excluded;
|
||||
}
|
||||
|
||||
function codeQueueAffectedRuntime(environment: ArtifactDeployEnvironment, targetPlanned: boolean): Record<string, unknown> {
|
||||
return {
|
||||
dryRunMutation: false,
|
||||
plannedTarget: targetPlanned && environment === "dev"
|
||||
? {
|
||||
namespace: "unidesk-dev",
|
||||
deployments: ["code-queue-scheduler-dev", "code-queue-read-dev", "code-queue-write-dev", "d601-dev-provider-egress-proxy"],
|
||||
}
|
||||
: null,
|
||||
productionNamespaceAffected: false,
|
||||
productionSchedulerRunnerAffected: false,
|
||||
activeTaskControlAffected: false,
|
||||
activeTaskInterruptCancelAffected: false,
|
||||
};
|
||||
}
|
||||
|
||||
function codeQueueSelfBootstrapGuard(environment: ArtifactDeployEnvironment): Record<string, unknown> {
|
||||
return {
|
||||
check: "code-queue-self-bootstrap-guard",
|
||||
serviceId: "code-queue",
|
||||
selfBootstrapBlocked: true,
|
||||
requiresSupervisorApproval: true,
|
||||
actorBoundary: "a running Code Queue task may publish contract evidence and dry-run plans only; it must not deploy Code Queue itself",
|
||||
devApply: "requires explicit supervisor or human authorization outside the running Code Queue task",
|
||||
prodApply: "unsupported until a separate supervisor-approved production Code Queue CD design exists",
|
||||
allowedWithoutApproval: [
|
||||
"CI artifact publication",
|
||||
"deploy plan",
|
||||
"deploy apply --dry-run",
|
||||
"artifact-registry deploy-service --dry-run",
|
||||
],
|
||||
forbiddenActions: [
|
||||
"self-deploy Code Queue from Code Queue",
|
||||
"run a non-dry-run DEV apply from Code Queue",
|
||||
"production namespace mutation",
|
||||
"production manifest mutation",
|
||||
"scheduler or runner restart",
|
||||
"active task interrupt",
|
||||
"active task cancel",
|
||||
],
|
||||
environment,
|
||||
};
|
||||
}
|
||||
|
||||
function codeQueueMgrSelfBootstrapGuard(environment: ArtifactDeployEnvironment, requiresSupervisorApproval: boolean): Record<string, unknown> {
|
||||
return {
|
||||
check: "code-queue-mgr-control-plane-guard",
|
||||
serviceId: "code-queue-mgr",
|
||||
selfBootstrapBlocked: true,
|
||||
requiresSupervisorApproval,
|
||||
actorBoundary: "dry-run is allowed, but a running Code Queue task must not replace the Code Queue control-plane sidecar as part of delivering Code Queue itself",
|
||||
targetScope: "main-server Compose service code-queue-mgr / container code-queue-mgr-backend only",
|
||||
prodApply: "requires explicit supervisor confirmation",
|
||||
environment,
|
||||
forbiddenActions: [
|
||||
"mutate D601 Code Queue scheduler",
|
||||
"mutate D601 Code Queue runner",
|
||||
"restart active tasks",
|
||||
"interrupt active tasks",
|
||||
"cancel active tasks",
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
function artifactConsumerSelfBootstrapGuard(
|
||||
spec: ArtifactConsumerSpec,
|
||||
environment: ArtifactDeployEnvironment,
|
||||
requiresSupervisorApproval: boolean,
|
||||
): Record<string, unknown> | undefined {
|
||||
if (spec.serviceId === "code-queue") return codeQueueSelfBootstrapGuard(environment);
|
||||
if (spec.serviceId === "code-queue-mgr") return codeQueueMgrSelfBootstrapGuard(environment, requiresSupervisorApproval);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function artifactConsumerLiveBlock(spec: ArtifactConsumerSpec, options: ArtifactRegistryOptions): Record<string, unknown> | null {
|
||||
const environment = options.environment ?? "prod";
|
||||
const livePolicy = artifactConsumerLivePolicy(spec, environment);
|
||||
const liveReason = artifactConsumerLiveBlockReason(spec, environment);
|
||||
if (spec.runtimeVerification === "blocked") {
|
||||
return {
|
||||
ok: false,
|
||||
@@ -1347,8 +1466,8 @@ function artifactConsumerLiveBlock(spec: ArtifactConsumerSpec, options: Artifact
|
||||
policy: "artifact CD must not accept a healthy old service or silently fall back to legacy rebuild paths",
|
||||
};
|
||||
}
|
||||
if (environment !== "prod" || spec.prodLiveApply === "enabled") return null;
|
||||
if (spec.prodLiveApply === "supervisor-only") {
|
||||
if (livePolicy === "enabled") return null;
|
||||
if (livePolicy === "supervisor-only") {
|
||||
return {
|
||||
ok: false,
|
||||
supported: true,
|
||||
@@ -1357,9 +1476,13 @@ function artifactConsumerLiveBlock(spec: ArtifactConsumerSpec, options: Artifact
|
||||
serviceId: spec.serviceId,
|
||||
environment,
|
||||
providerId: options.providerId,
|
||||
reason: spec.prodLiveBlockReason ?? `${spec.serviceId} production artifact apply requires supervisor confirmation.`,
|
||||
dryRunCommandShape: `bun scripts/cli.ts artifact-registry deploy-service --env prod --service ${spec.serviceId} --commit <full-sha> --dry-run`,
|
||||
policy: "worker automation must not perform live production apply for this infrastructure control-plane service",
|
||||
reason: liveReason ?? `${spec.serviceId} ${environment} artifact apply requires supervisor confirmation.`,
|
||||
requiresSupervisorApproval: true,
|
||||
selfBootstrapGuard: artifactConsumerSelfBootstrapGuard(spec, environment, true),
|
||||
affectedRuntime: spec.serviceId === "code-queue" ? codeQueueAffectedRuntime(environment, true) : undefined,
|
||||
excludedTargets: spec.serviceId === "code-queue" ? codeQueueExcludedTargets(environment) : undefined,
|
||||
dryRunCommandShape: `bun scripts/cli.ts artifact-registry deploy-service --env ${environment} --service ${spec.serviceId} --commit <full-sha> --dry-run`,
|
||||
policy: "worker automation must not perform live apply for this supervisor-gated artifact consumer",
|
||||
};
|
||||
}
|
||||
return {
|
||||
@@ -1370,7 +1493,11 @@ function artifactConsumerLiveBlock(spec: ArtifactConsumerSpec, options: Artifact
|
||||
serviceId: spec.serviceId,
|
||||
environment,
|
||||
providerId: options.providerId,
|
||||
reason: spec.prodLiveBlockReason ?? `${spec.serviceId} does not yet satisfy the artifact consumer runtime verification contract.`,
|
||||
reason: liveReason ?? `${spec.serviceId} does not yet satisfy the artifact consumer runtime verification contract.`,
|
||||
requiresSupervisorApproval: spec.serviceId === "code-queue",
|
||||
selfBootstrapGuard: spec.serviceId === "code-queue" ? codeQueueSelfBootstrapGuard(environment) : undefined,
|
||||
affectedRuntime: spec.serviceId === "code-queue" ? codeQueueAffectedRuntime(environment, false) : undefined,
|
||||
excludedTargets: spec.serviceId === "code-queue" ? codeQueueExcludedTargets(environment) : undefined,
|
||||
requiredBeforeLiveApply: [
|
||||
"CI can publish a commit-pinned image with matching service id, source repo, source commit, and Dockerfile labels",
|
||||
"runtime Compose env injects deploy commit/requestedCommit metadata",
|
||||
@@ -1380,6 +1507,7 @@ function artifactConsumerLiveBlock(spec: ArtifactConsumerSpec, options: Artifact
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
function artifactImageRef(options: ArtifactRegistryOptions, spec: ArtifactConsumerSpec, commit: string): string {
|
||||
return `127.0.0.1:${options.port}/${(options.dryRun ? options.deployJsonService?.artifact?.repository : undefined) ?? spec.registryRepository}:${commit}`;
|
||||
}
|
||||
@@ -2627,7 +2755,9 @@ function dryRunArtifactConsumerPlan(options: ArtifactRegistryOptions, spec: Arti
|
||||
: [];
|
||||
if (drifts.length > 0) return deployJsonDriftResult(deployJsonService, environment, drifts);
|
||||
const verificationBlocked = spec.runtimeVerification === "blocked";
|
||||
const livePolicy = environment === "prod" ? spec.prodLiveApply : "enabled";
|
||||
const livePolicy = artifactConsumerLivePolicy(spec, environment);
|
||||
const liveReason = artifactConsumerLiveBlockReason(spec, environment);
|
||||
const requiresSupervisorApproval = livePolicy === "supervisor-only" || spec.serviceId === "code-queue";
|
||||
const sourceImage = artifactImageRef(effectiveOptions, spec, commit);
|
||||
const registryEndpoint = `http://127.0.0.1:${effectiveOptions.port}`;
|
||||
const config = readConfig();
|
||||
@@ -2689,9 +2819,13 @@ function dryRunArtifactConsumerPlan(options: ArtifactRegistryOptions, spec: Arti
|
||||
boundary: `${environment} CD is artifact-consumer only: verify commit-pinned registry image, pull/import, deploy, then verify live commit/image/health; it never builds source on the runtime target`,
|
||||
liveApply: {
|
||||
policy: livePolicy,
|
||||
allowed: !verificationBlocked && (environment !== "prod" || spec.prodLiveApply === "enabled"),
|
||||
reason: spec.runtimeVerificationBlockReason ?? (environment === "prod" ? spec.prodLiveBlockReason ?? null : null),
|
||||
allowed: !verificationBlocked && livePolicy === "enabled",
|
||||
requiresSupervisorApproval,
|
||||
reason: liveReason,
|
||||
},
|
||||
requiresSupervisorApproval,
|
||||
selfBootstrapGuard: artifactConsumerSelfBootstrapGuard(spec, environment, requiresSupervisorApproval),
|
||||
affectedRuntime: spec.serviceId === "code-queue" ? codeQueueAffectedRuntime(environment, true) : undefined,
|
||||
sourceOfTruth: deployJsonService !== null && hasDeployJsonExecutorContract(deployJsonService) ? deployJsonSourceOfTruth(deployJsonService, environment) : undefined,
|
||||
driftCheck: deployJsonService !== null && hasDeployJsonExecutorContract(deployJsonService) ? {
|
||||
ok: true,
|
||||
@@ -2727,6 +2861,8 @@ function dryRunArtifactConsumerPlan(options: ArtifactRegistryOptions, spec: Arti
|
||||
reason: "dry-run does not mutate D601 Code Queue scheduler, runner, tasks, interrupts, or cancellations.",
|
||||
},
|
||||
]
|
||||
: spec.serviceId === "code-queue"
|
||||
? codeQueueExcludedTargets(environment)
|
||||
: undefined,
|
||||
validation: [
|
||||
"D601 registry /v2 manifest exists for the commit tag",
|
||||
@@ -2800,6 +2936,7 @@ function dryRunArtifactConsumerPlan(options: ArtifactRegistryOptions, spec: Arti
|
||||
memory: contractRuntime.memory,
|
||||
health: contractRuntime.health,
|
||||
},
|
||||
excludedTargets: spec.serviceId === "code-queue" ? codeQueueExcludedTargets(environment) : undefined,
|
||||
validation: [
|
||||
"D601 registry /v2 manifest exists for the commit tag before mutation",
|
||||
"D601 Docker-pulled image labels match service id, source repo, source commit, and Dockerfile",
|
||||
@@ -3183,7 +3320,7 @@ function localHelp(): Record<string, unknown> {
|
||||
"bun scripts/cli.ts artifact-registry deploy-service --env prod --service mdtodo --commit <full-sha> [--dry-run] [--run-now] [--provider-id D601]",
|
||||
"bun scripts/cli.ts artifact-registry deploy-service --env dev --service claudeqq --commit <full-sha> [--dry-run] [--run-now] [--provider-id D601]",
|
||||
"bun scripts/cli.ts artifact-registry deploy-service --env prod --service claudeqq --commit <full-sha> [--dry-run] [--run-now] [--provider-id D601]",
|
||||
"bun scripts/cli.ts artifact-registry deploy-service --env dev --service code-queue --commit <full-sha> [--dry-run] [--run-now] [--provider-id D601]",
|
||||
"bun scripts/cli.ts artifact-registry deploy-service --env dev --service code-queue --commit <full-sha> --dry-run [--provider-id D601]",
|
||||
],
|
||||
firstStage: "install now writes the rendered systemd/Compose/config files and starts the registry",
|
||||
artifactConsumers: {
|
||||
@@ -3218,7 +3355,7 @@ function localHelp(): Record<string, unknown> {
|
||||
"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",
|
||||
"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",
|
||||
|
||||
+82
-6
@@ -158,6 +158,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 devArtifactLiveApplyBlockedServiceIds = new Map<string, string>([
|
||||
["code-queue", "Code Queue DEV live apply is self-bootstrap sensitive: a running Code Queue task may produce dry-run evidence only. A human operator or supervisor must explicitly authorize DEV apply outside Code Queue after reviewing the CI artifact digest and dry-run target list."],
|
||||
]);
|
||||
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."],
|
||||
]);
|
||||
@@ -1141,11 +1144,41 @@ function unsupportedPlanTarget(serviceId: string, environment: DeployEnvironment
|
||||
};
|
||||
}
|
||||
|
||||
function codeQueueSelfBootstrapGuard(environment: DeployEnvironment): Record<string, unknown> | undefined {
|
||||
if (environment !== "dev" && environment !== "prod") return undefined;
|
||||
return {
|
||||
check: "code-queue-self-bootstrap-guard",
|
||||
selfBootstrapBlocked: true,
|
||||
requiresSupervisorApproval: true,
|
||||
actorBoundary: "a running Code Queue task may publish contract evidence and dry-run plans only; it must not deploy Code Queue itself",
|
||||
devApply: "requires explicit supervisor or human authorization outside the running Code Queue task",
|
||||
prodApply: "unsupported until a separate supervisor-approved production Code Queue CD design exists",
|
||||
allowedWithoutApproval: [
|
||||
"CI artifact publication",
|
||||
"deploy plan",
|
||||
"deploy apply --dry-run",
|
||||
"artifact-registry deploy-service --dry-run",
|
||||
],
|
||||
forbiddenActions: [
|
||||
"self-deploy Code Queue from Code Queue",
|
||||
"run a non-dry-run DEV apply from Code Queue",
|
||||
"production namespace mutation",
|
||||
"production manifest mutation",
|
||||
"scheduler or runner restart",
|
||||
"active task interrupt",
|
||||
"active task cancel",
|
||||
],
|
||||
environment,
|
||||
};
|
||||
}
|
||||
|
||||
function codeQueueCiCdBoundary(serviceId: string, environment: DeployEnvironment): Record<string, unknown> | undefined {
|
||||
if (serviceId !== "code-queue") return undefined;
|
||||
return {
|
||||
serviceId,
|
||||
selfDeployBlocked: true,
|
||||
requiresSupervisorApproval: true,
|
||||
selfBootstrapGuard: codeQueueSelfBootstrapGuard(environment),
|
||||
ciProducer: {
|
||||
command: "bun scripts/cli.ts ci publish-user-service --service code-queue --commit <full-sha>",
|
||||
allowed: true,
|
||||
@@ -1157,7 +1190,9 @@ function codeQueueCiCdBoundary(serviceId: string, environment: DeployEnvironment
|
||||
? {
|
||||
environment: "dev",
|
||||
dryRunCommand: "bun scripts/cli.ts deploy apply --env dev --service code-queue --commit <full-sha> --dry-run",
|
||||
liveApplyCommandShape: "bun scripts/cli.ts deploy apply --env dev --service code-queue --commit <full-sha> --run-now",
|
||||
liveApplyCommandShape: null,
|
||||
liveApplyAllowed: false,
|
||||
requiresSupervisorApproval: true,
|
||||
allowedTarget: "unidesk-dev Code Queue scheduler/read/write/provider-egress-proxy only",
|
||||
manualAuthorizationPoint: "operator reviews CI artifact summary and dev dry-run plan, then explicitly authorizes DEV apply outside this Code Queue task",
|
||||
prodMutationAllowed: false,
|
||||
@@ -1166,6 +1201,8 @@ function codeQueueCiCdBoundary(serviceId: string, environment: DeployEnvironment
|
||||
environment: "prod",
|
||||
dryRunCommand: "bun scripts/cli.ts deploy plan --env prod --service code-queue",
|
||||
liveApplyCommandShape: null,
|
||||
liveApplyAllowed: false,
|
||||
requiresSupervisorApproval: true,
|
||||
allowedTarget: null,
|
||||
manualAuthorizationPoint: "PROD requires a future supervisor-approved Code Queue CD design; current runner output is plan/unsupported only",
|
||||
prodMutationAllowed: false,
|
||||
@@ -3072,6 +3109,10 @@ function environmentDryRunPlan(
|
||||
: planTarget;
|
||||
const registryRepository = service.artifact?.repository ?? `unidesk/${service.id}`;
|
||||
const stableImage = service.consumer?.target.stableImage;
|
||||
const liveApplyBlockedReason = environment === "prod"
|
||||
? prodArtifactLiveApplyBlockedServiceIds.get(service.id) ?? null
|
||||
: devArtifactLiveApplyBlockedServiceIds.get(service.id) ?? null;
|
||||
const dryRunOnly = unsupported || liveApplyBlockedReason !== null || dryRunBlockedReason !== null;
|
||||
return {
|
||||
id: service.id,
|
||||
repo: service.repo,
|
||||
@@ -3119,8 +3160,10 @@ function environmentDryRunPlan(
|
||||
"unidesk.ai/dockerfile": serviceConfig.repository.dockerfile,
|
||||
},
|
||||
noRuntimeSourceBuild: unsupported || (service.consumer?.noRuntimeSourceBuild ?? effectivePlanKind !== "d601-dev-target-side-build"),
|
||||
dryRunOnly: unsupported || (environment === "prod" && prodArtifactLiveApplyBlockedServiceIds.has(service.id)) || dryRunBlockedReason !== null,
|
||||
blockedReason: unsupportedReason ?? dryRunBlockedReason ?? (environment === "prod" ? prodArtifactLiveApplyBlockedServiceIds.get(service.id) ?? null : null),
|
||||
dryRunOnly,
|
||||
blockedReason: unsupportedReason ?? dryRunBlockedReason ?? liveApplyBlockedReason,
|
||||
requiresSupervisorApproval: service.id === "code-queue" || liveApplyBlockedReason !== null,
|
||||
selfBootstrapGuard: service.id === "code-queue" ? codeQueueSelfBootstrapGuard(environment) : undefined,
|
||||
runtimeSecrets,
|
||||
sourceOfTruth: deployJsonContract ? deployJsonSourceOfTruth(service, environment) : undefined,
|
||||
driftCheck: deployJsonContract ? {
|
||||
@@ -3162,11 +3205,12 @@ function environmentDryRunPlan(
|
||||
reason: dryRunBlockedReason,
|
||||
dryRunOnly: true,
|
||||
}
|
||||
: environment === "prod" && prodArtifactLiveApplyBlockedServiceIds.has(service.id)
|
||||
: liveApplyBlockedReason !== null
|
||||
? {
|
||||
allowed: false,
|
||||
reason: prodArtifactLiveApplyBlockedServiceIds.get(service.id),
|
||||
reason: liveApplyBlockedReason,
|
||||
dryRunOnly: true,
|
||||
requiresSupervisorApproval: true,
|
||||
}
|
||||
: environment === "prod"
|
||||
? { allowed: prodArtifactConsumerServiceIds.has(service.id) }
|
||||
@@ -3240,7 +3284,17 @@ function prodArtifactUnsupportedResult(services: DeployManifestService[]): Recor
|
||||
repo: service.repo,
|
||||
commitId: service.commitId,
|
||||
supported: false,
|
||||
reason: "No standardized prod D601 registry artifact consumer is implemented for this service.",
|
||||
reason: unsupportedEnvironmentPlanReason(service.id, "prod"),
|
||||
requiresSupervisorApproval: service.id === "code-queue",
|
||||
selfBootstrapGuard: service.id === "code-queue" ? codeQueueSelfBootstrapGuard("prod") : undefined,
|
||||
affectedRuntime: service.id === "code-queue" ? {
|
||||
dryRunMutation: false,
|
||||
plannedTarget: null,
|
||||
productionNamespaceAffected: false,
|
||||
productionSchedulerRunnerAffected: false,
|
||||
activeTaskControlAffected: false,
|
||||
activeTaskInterruptCancelAffected: false,
|
||||
} : undefined,
|
||||
})),
|
||||
policy: "prod deploy must not silently fall back to legacy D601 maintenance-channel source builds",
|
||||
supportedServices: Array.from(prodArtifactConsumerServiceIds),
|
||||
@@ -3294,6 +3348,26 @@ function prodArtifactLiveApplyBlockedResult(services: DeployManifestService[]):
|
||||
};
|
||||
}
|
||||
|
||||
function devArtifactLiveApplyBlockedResult(services: DeployManifestService[]): Record<string, unknown> {
|
||||
return {
|
||||
ok: false,
|
||||
supported: true,
|
||||
error: "live-dev-apply-blocked",
|
||||
services: services.map((service) => ({
|
||||
id: service.id,
|
||||
repo: service.repo,
|
||||
commitId: service.commitId,
|
||||
supported: true,
|
||||
liveApplyAllowed: false,
|
||||
requiresSupervisorApproval: true,
|
||||
reason: devArtifactLiveApplyBlockedServiceIds.get(service.id) ?? "live DEV artifact apply is blocked by policy",
|
||||
selfBootstrapGuard: service.id === "code-queue" ? codeQueueSelfBootstrapGuard("dev") : undefined,
|
||||
})),
|
||||
policy: "dev dry-run/plan is available, but a running Code Queue task must not perform live DEV Code Queue apply without explicit supervisor or human authorization outside Code Queue",
|
||||
dryRunCommandShape: "bun scripts/cli.ts deploy apply --env dev --service <id> --dry-run",
|
||||
};
|
||||
}
|
||||
|
||||
async function runArtifactConsumerApplyNow(
|
||||
manifest: DeployManifest,
|
||||
options: DeployOptions,
|
||||
@@ -3472,6 +3546,8 @@ export async function runDeployCommand(config: UniDeskConfig | null, args: strin
|
||||
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);
|
||||
}
|
||||
|
||||
+2
-2
@@ -349,7 +349,7 @@ function artifactRegistryHelp(): unknown {
|
||||
"bun scripts/cli.ts artifact-registry deploy-service --env prod --service mdtodo --commit <full-sha> [--dry-run] [--run-now] [--provider-id D601]",
|
||||
"bun scripts/cli.ts artifact-registry deploy-service --env dev --service claudeqq --commit <full-sha> [--dry-run] [--run-now] [--provider-id D601]",
|
||||
"bun scripts/cli.ts artifact-registry deploy-service --env prod --service claudeqq --commit <full-sha> [--dry-run] [--run-now] [--provider-id D601]",
|
||||
"bun scripts/cli.ts artifact-registry deploy-service --env dev --service code-queue --commit <full-sha> [--dry-run] [--run-now] [--provider-id D601]",
|
||||
"bun scripts/cli.ts artifact-registry deploy-service --env dev --service code-queue --commit <full-sha> --dry-run [--provider-id D601]",
|
||||
],
|
||||
description: "Manage the declaration, rendered files and readonly checks for the D601 host-managed CNCF Distribution artifact registry.",
|
||||
boundary: [
|
||||
@@ -359,7 +359,7 @@ function artifactRegistryHelp(): unknown {
|
||||
"deploy-backend-core only pulls commit-pinned backend-core artifacts and does not build backend-core on the master server",
|
||||
"deploy-service currently supports backend-core, baidu-netdisk, prod/dev frontend, decision-center, mdtodo, claudeqq, project-manager, oa-event-flow, code-queue-mgr, todo-note, findjob, pipeline, met-nonlinear, k3sctl-adapter, and dev-only code-queue as standardized consumers",
|
||||
"findjob and pipeline have D601 direct dev/prod Compose artifact consumers; met-nonlinear is runtime-verification blocked; k3sctl-adapter is supervisor-only",
|
||||
"code-queue has no prod artifact deploy target and prod requests return structured unsupported",
|
||||
"code-queue has no prod artifact deploy target; dev requests are dry-run evidence only unless a human operator or supervisor authorizes DEV apply outside Code Queue",
|
||||
"status and health use provider-gateway Host SSH readonly checks",
|
||||
],
|
||||
legacyEntrypoints: {
|
||||
|
||||
Reference in New Issue
Block a user