test: add deploy json executor drift preflight

This commit is contained in:
Codex
2026-05-21 13:25:16 +00:00
parent 70263014b5
commit fc87e680e4
7 changed files with 825 additions and 46 deletions
+127 -19
View File
@@ -9,15 +9,23 @@ import { baiduNetdiskRuntimeSecretRequirements, runtimeSecretContractFromEnvText
import { startJob } from "./jobs";
import { coreInternalFetch } from "./microservices";
import { codeQueueSourceImportPreflight, codeQueueSourceSubdir } from "./code-queue-source-guard";
import {
compareDeployJsonExecutorMirrors,
deployJsonCommitImage,
deployJsonDriftResult,
deployJsonSourceOfTruth,
encodeDeployJsonServiceContract,
hasDeployJsonExecutorContract,
k3sManifestExecutorMirror,
parseDeployJsonServiceContract,
type DeployJsonExecutorMirror,
type DeployJsonServiceContract,
} from "./deploy-json-contract";
type DeployAction = "check" | "plan" | "apply";
type DeployEnvironment = "dev" | "prod";
interface DeployManifestService {
id: string;
repo: string;
commitId: string;
}
type DeployManifestService = DeployJsonServiceContract;
interface DeployManifest {
schemaVersion: 1 | 2;
@@ -512,17 +520,16 @@ function parseOptions(args: string[]): DeployOptions {
}
function parseDeployManifestService(item: unknown, index: number, seen: Set<string>, path: string): DeployManifestService {
const service = asRecord(item);
if (service === null) throw new Error(`deploy manifest ${path}[${index}] must be an object`);
const id = asString(service.id);
const repo = asString(service.repo);
const commitId = asString(service.commitId).toLowerCase();
const service = parseDeployJsonServiceContract(item, `deploy manifest ${path}[${index}]`);
const id = service.id;
const repo = service.repo;
const commitId = service.commitId;
if (id.length === 0) throw new Error(`deploy manifest ${path}[${index}].id must be a non-empty string`);
if (repo.length === 0) throw new Error(`deploy manifest ${path}[${index}].repo must be a non-empty string`);
if (!/^[0-9a-f]{7,40}$/iu.test(commitId)) throw new Error(`deploy manifest ${path}[${index}].commitId must be a 7-40 char git SHA`);
if (seen.has(id)) throw new Error(`duplicate deploy manifest service id: ${id}`);
seen.add(id);
return { id, repo, commitId };
return service;
}
function parseDeployManifestServices(value: unknown, path: string): DeployManifestService[] {
@@ -896,6 +903,11 @@ function artifactConsumerPlanKind(service: UniDeskMicroserviceConfig, environmen
return "unsupported";
}
function artifactConsumerPlanKindFromDeployJson(service: DeployManifestService, fallback: ArtifactConsumerPlanKind): ArtifactConsumerPlanKind {
if (service.consumer?.kind === "d601-k3s-managed") return "d601-k3s-managed";
return fallback;
}
function deploymentPathForEnvironmentService(service: UniDeskMicroserviceConfig, environment: DeployEnvironment): string {
return deploymentPathForEnvironmentServiceId(service.id, environment);
}
@@ -959,6 +971,35 @@ function artifactConsumerPlanTarget(service: UniDeskMicroserviceConfig, environm
return common;
}
function artifactConsumerPlanTargetFromDeployJson(
service: DeployManifestService,
fallbackService: UniDeskMicroserviceConfig,
environment: DeployEnvironment,
fallbackTarget: Record<string, unknown> | null,
): Record<string, unknown> | null {
if (service.consumer?.kind !== "d601-k3s-managed") return fallbackTarget;
const target = service.consumer.target;
const liveBlockReason = environment === "prod" ? prodArtifactLiveApplyBlockedServiceIds.get(service.id) ?? null : null;
const dryRunBlockedReason = artifactConsumerDryRunBlockedServiceIds.get(service.id) ?? null;
return {
consumerKind: service.consumer.kind,
runtimeHost: "D601",
deploymentMode: fallbackService.deployment.mode,
noRuntimeSourceBuild: service.consumer.noRuntimeSourceBuild,
dryRunOnly: dryRunBlockedReason !== null || (environment === "prod" && prodArtifactLiveApplyBlockedServiceIds.has(service.id)),
blockedReason: dryRunBlockedReason ?? liveBlockReason,
namespace: target.namespace,
deployment: target.deployment,
service: target.service,
containerName: target.containerName,
manifest: target.manifestRepoPath,
targetImage: target.stableImage,
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"],
sourceOfTruth: deployJsonSourceOfTruth(service, environment),
};
}
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";
@@ -970,6 +1011,44 @@ function artifactConsumerTargetImageHint(service: UniDeskMicroserviceConfig, env
return container;
}
function deployJsonExecutorMirrors(
service: DeployManifestService,
serviceConfig: UniDeskMicroserviceConfig,
environment: DeployEnvironment,
planKind: ArtifactConsumerPlanKind,
planTarget: Record<string, unknown> | null,
): DeployJsonExecutorMirror[] {
const mirrors: DeployJsonExecutorMirror[] = [];
mirrors.push({
surface: "deploy-executor-plan",
artifact: {
kind: "source-build",
repository: `unidesk/${service.id}`,
tag: "commitId",
},
consumer: {
kind: planKind === "d601-k3s-managed" ? "d601-k3s-managed" : planKind,
noRuntimeSourceBuild: planKind !== "d601-dev-target-side-build",
target: planTarget === null ? undefined : {
namespace: typeof planTarget.namespace === "string" ? planTarget.namespace : undefined,
deployment: typeof planTarget.deployment === "string" ? planTarget.deployment : undefined,
service: typeof planTarget.service === "string" ? planTarget.service : undefined,
containerName: typeof planTarget.containerName === "string" ? planTarget.containerName : undefined,
stableImage: typeof planTarget.targetImage === "string" ? planTarget.targetImage : undefined,
manifestRepoPath: typeof planTarget.manifest === "string" ? planTarget.manifest : undefined,
},
},
runtime: {
containerPort: serviceConfig.backend.nodePort,
servicePort: serviceConfig.backend.nodePort,
healthPath: serviceConfig.backend.healthPath,
},
});
const manifestMirror = k3sManifestExecutorMirror(service);
if (manifestMirror !== null) mirrors.push(manifestMirror);
return mirrors;
}
function artifactConsumerPlanValidation(service: UniDeskMicroserviceConfig, environment: DeployEnvironment): string[] {
const kind = artifactConsumerPlanKind(service, environment);
if (kind === "d601-dev-target-side-build") {
@@ -2969,9 +3048,24 @@ function environmentDryRunPlan(
const planTarget = serviceConfig === null ? null : artifactConsumerPlanTarget(serviceConfig, environment);
const runtimeSecrets = serviceConfig === null ? undefined : redactedSecretContractForService(config, serviceConfig, environment);
const unsupportedReason = unsupported ? unsupportedEnvironmentPlanReason(service.id, environment) : null;
const deployJsonContract = hasDeployJsonExecutorContract(service);
const effectivePlanKind = deployJsonContract
? artifactConsumerPlanKindFromDeployJson(service, planKind)
: planKind;
const deployJsonPlanTarget = serviceConfig === null
? null
: artifactConsumerPlanTargetFromDeployJson(service, serviceConfig, environment, planTarget);
const drifts = deployJsonContract && serviceConfig !== null && !unsupported
? compareDeployJsonExecutorMirrors(service, environment, deployJsonExecutorMirrors(service, serviceConfig, environment, effectivePlanKind, deployJsonPlanTarget))
: [];
if (drifts.length > 0) return deployJsonDriftResult(service, environment, drifts);
const effectiveTarget = unsupported
? unsupportedPlanTarget(service.id, environment, unsupportedReason ?? "unsupported")
: planTarget;
: deployJsonContract
? deployJsonPlanTarget
: planTarget;
const registryRepository = service.artifact?.repository ?? `unidesk/${service.id}`;
const stableImage = service.consumer?.target.stableImage;
return {
id: service.id,
repo: service.repo,
@@ -2989,13 +3083,13 @@ function environmentDryRunPlan(
dryRunOnly: true,
blockedReason: "service is missing from config.json and no synthetic deploy service is available for this environment",
} : {
consumerKind: unsupported ? "unsupported" : planKind,
registryImage: unsupported ? null : `127.0.0.1:5000/unidesk/${service.id}:${service.commitId}`,
consumerKind: unsupported ? "unsupported" : effectivePlanKind,
registryImage: unsupported ? null : `127.0.0.1:5000/${registryRepository}:${service.commitId}`,
registry: unsupported ? null : {
endpoint: "http://127.0.0.1:5000",
repository: `unidesk/${service.id}`,
repository: registryRepository,
tag: service.commitId,
imageRef: `127.0.0.1:5000/unidesk/${service.id}:${service.commitId}`,
imageRef: `127.0.0.1:5000/${registryRepository}:${service.commitId}`,
digest: null,
digestHeader: "Docker-Content-Digest",
digestSource: "planned registry manifest HEAD; live apply records Docker-Content-Digest before mutation",
@@ -3006,9 +3100,9 @@ function environmentDryRunPlan(
deployRef: `${source.ref}:deploy.json#environments.${environment}.services.${service.id}`,
},
build: unsupported ? null : {
willCompile: planKind === "d601-dev-target-side-build",
willCompile: effectivePlanKind === "d601-dev-target-side-build",
willRunCargoBuild: false,
willRunDockerBuild: planKind === "d601-dev-target-side-build",
willRunDockerBuild: effectivePlanKind === "d601-dev-target-side-build",
willRunDockerComposeBuild: false,
producerBoundary: service.id === "backend-core" ? "ci publish-backend-core" : "ci publish-user-service",
},
@@ -3018,12 +3112,25 @@ function environmentDryRunPlan(
"unidesk.ai/source-commit": service.commitId,
"unidesk.ai/dockerfile": serviceConfig.repository.dockerfile,
},
noRuntimeSourceBuild: unsupported || planKind !== "d601-dev-target-side-build",
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),
runtimeSecrets,
sourceOfTruth: deployJsonContract ? deployJsonSourceOfTruth(service, environment) : undefined,
driftCheck: deployJsonContract ? {
ok: true,
mirrors: ["deploy-executor-plan", ...(service.consumer?.kind === "d601-k3s-managed" ? ["k8s-manifest"] : [])],
} : undefined,
},
target: effectiveTarget,
runtime: service.runtime === undefined ? undefined : {
sourceOfTruth: "deploy.json",
containerPort: service.runtime.containerPort,
healthPath: service.runtime.healthPath,
memory: service.runtime.memory,
health: service.runtime.health,
},
runtimeImage: stableImage === undefined ? undefined : deployJsonCommitImage(stableImage, service.commitId),
validation: unsupported
? ["unsupported service remains blocked before source materialization, registry pull, manifest update, rollout, or runtime mutation"]
: serviceConfig === null
@@ -3197,6 +3304,7 @@ async function runArtifactConsumerApplyNow(
"--commit", commit,
"--source-repo", service.repo,
"--deploy-ref", `${deployEnvironmentTargets[environment].gitRef}:deploy.json#environments.${environment}.services.${service.id}`,
...(options.dryRun && hasDeployJsonExecutorContract(service) ? ["--deploy-json-service", encodeDeployJsonServiceContract(service)] : []),
"--env", environment,
"--timeout-ms", String(options.timeoutMs),
"--run-now",