test: lock backend-core artifact readiness contract

This commit is contained in:
Codex
2026-05-21 12:55:01 +00:00
parent b334430c00
commit 569cf7a74c
4 changed files with 237 additions and 7 deletions
+15 -6
View File
@@ -1333,7 +1333,7 @@ function artifactConsumerLiveBlock(spec: ArtifactConsumerSpec, options: Artifact
providerId: options.providerId,
reason: spec.prodLiveBlockReason ?? `${spec.serviceId} does not yet satisfy the artifact consumer runtime verification contract.`,
requiredBeforeLiveApply: [
"CI can publish a commit-pinned image with matching service id, source commit, and Dockerfile labels",
"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",
"service health reports deploy.commit and deploy.requestedCommit for strict verification",
],
@@ -1823,25 +1823,29 @@ function verifyLocalArtifactLabels(
localLoadedImage: string,
spec: ArtifactConsumerSpec,
commit: string,
sourceRepo: string,
): Record<string, unknown> | null {
const inspectPulled = runCommand(["docker", "image", "inspect", localLoadedImage, "--format", "{{json .Config.Labels}}"], repoRoot);
const labelCommit = runCommand(["docker", "image", "inspect", localLoadedImage, "--format", "{{ index .Config.Labels \"unidesk.ai/source-commit\" }}"], repoRoot);
const labelService = runCommand(["docker", "image", "inspect", localLoadedImage, "--format", "{{ index .Config.Labels \"unidesk.ai/service-id\" }}"], repoRoot);
const labelRepo = runCommand(["docker", "image", "inspect", localLoadedImage, "--format", "{{ index .Config.Labels \"unidesk.ai/source-repo\" }}"], repoRoot);
const labelDockerfile = runCommand(["docker", "image", "inspect", localLoadedImage, "--format", "{{ index .Config.Labels \"unidesk.ai/dockerfile\" }}"], repoRoot);
const observed = {
commit: labelCommit.stdout.trim(),
serviceId: labelService.stdout.trim(),
sourceRepo: labelRepo.stdout.trim(),
dockerfile: labelDockerfile.stdout.trim(),
labels: inspectPulled.stdout.trim(),
};
const ok = observed.commit === commit
&& observed.serviceId === spec.serviceId
&& observed.sourceRepo === sourceRepo
&& observed.dockerfile === spec.dockerfile;
if (ok) return null;
return {
ok: false,
step: "image-label-verify",
expected: { commit, serviceId: spec.serviceId, dockerfile: spec.dockerfile },
expected: { commit, serviceId: spec.serviceId, sourceRepo, dockerfile: spec.dockerfile },
observed,
};
}
@@ -1937,7 +1941,7 @@ async function deployComposeArtifactNow(options: ArtifactRegistryOptions, spec:
};
}
const localLoadedImage = sourceImage;
const labelFailure = verifyLocalArtifactLabels(localLoadedImage, spec, commit);
const labelFailure = verifyLocalArtifactLabels(localLoadedImage, spec, commit, sourceRepoFor(options, spec));
if (labelFailure !== null) return { ...labelFailure, registryProbe: commandTail(registryProbe) };
const tag = runCommand(["docker", "tag", localLoadedImage, composeImage], repoRoot);
if (tag.exitCode !== 0 || tag.timedOut) {
@@ -2100,9 +2104,11 @@ function d601ComposeArtifactDeployScript(options: ArtifactRegistryOptions, spec:
"docker pull -q \"$registry_image\" >/dev/null",
"label_commit=$(docker image inspect \"$registry_image\" --format '{{ index .Config.Labels \"unidesk.ai/source-commit\" }}')",
"label_service=$(docker image inspect \"$registry_image\" --format '{{ index .Config.Labels \"unidesk.ai/service-id\" }}')",
"label_repo=$(docker image inspect \"$registry_image\" --format '{{ index .Config.Labels \"unidesk.ai/source-repo\" }}')",
"label_dockerfile=$(docker image inspect \"$registry_image\" --format '{{ index .Config.Labels \"unidesk.ai/dockerfile\" }}')",
"test \"$label_commit\" = \"$commit\"",
"test \"$label_service\" = \"$service_id\"",
"test \"$label_repo\" = \"$source_repo\"",
"test \"$label_dockerfile\" = \"$dockerfile\"",
"test -d \"$work_dir\"",
"test -f \"$work_dir/$compose_file\"",
@@ -2290,6 +2296,7 @@ function dryRunArtifactConsumerPlan(options: ArtifactRegistryOptions, spec: Arti
},
requiredLabels: {
"unidesk.ai/service-id": spec.serviceId,
"unidesk.ai/source-repo": sourceRepoFor(options, spec),
"unidesk.ai/source-commit": commit,
"unidesk.ai/dockerfile": spec.dockerfile,
},
@@ -2338,8 +2345,8 @@ function dryRunArtifactConsumerPlan(options: ArtifactRegistryOptions, spec: Arti
validation: [
"D601 registry /v2 manifest exists for the commit tag",
spec.kind === "d601-compose"
? "D601-pulled image labels match service id, source commit, and Dockerfile"
: "loaded image labels match service id, source commit, and Dockerfile",
? "D601-pulled image labels match service id, source repo, source commit, and Dockerfile"
: "loaded image labels match service id, source repo, source commit, and Dockerfile",
spec.kind === "d601-compose"
? "running D601 Compose container is recreated with a no-build override that points the service image at the artifact"
: "running Compose container image label matches the requested commit",
@@ -2380,7 +2387,7 @@ function dryRunArtifactConsumerPlan(options: ArtifactRegistryOptions, spec: Arti
},
validation: [
"D601 registry /v2 manifest exists for the commit tag before mutation",
"D601 Docker-pulled image labels match service id, source commit, and Dockerfile",
"D601 Docker-pulled image labels match service id, source repo, source commit, and Dockerfile",
"native k3s containerd has the commit image and stable runtime image tag",
"Deployment annotation and pod image id label match the requested commit",
"service health via Kubernetes API service proxy returns the same deploy.commit and deploy.requestedCommit",
@@ -2485,9 +2492,11 @@ function d601K3sArtifactDeployScript(options: ArtifactRegistryOptions, spec: Art
"docker pull -q \"$registry_image\" >/dev/null",
"label_commit=$(docker image inspect \"$registry_image\" --format '{{ index .Config.Labels \"unidesk.ai/source-commit\" }}')",
"label_service=$(docker image inspect \"$registry_image\" --format '{{ index .Config.Labels \"unidesk.ai/service-id\" }}')",
"label_repo=$(docker image inspect \"$registry_image\" --format '{{ index .Config.Labels \"unidesk.ai/source-repo\" }}')",
"label_dockerfile=$(docker image inspect \"$registry_image\" --format '{{ index .Config.Labels \"unidesk.ai/dockerfile\" }}')",
"test \"$label_commit\" = \"$commit\"",
"test \"$label_service\" = \"$service_id\"",
"test \"$label_repo\" = \"$source_repo\"",
"test \"$label_dockerfile\" = \"$dockerfile\"",
"docker tag \"$registry_image\" \"$stable_image\"",
"docker tag \"$registry_image\" \"$commit_image\"",
+1
View File
@@ -567,6 +567,7 @@ function backendCoreDevApplyPath(options: CiPublishBackendCoreOptions, plannedAr
environment: "dev",
pullOnly: true,
apply: `bun scripts/cli.ts deploy apply --env dev --service backend-core --commit ${options.commit}`,
dryRun: `bun scripts/cli.ts deploy apply --env dev --service backend-core --commit ${options.commit} --dry-run`,
sourceImage: plannedArtifact.imageRef,
forbidden: ["--env prod", "cargo build", "docker build", "docker compose build", "server rebuild backend-core"],
note: "PROD is intentionally not part of backend-core publish preflight; dev apply still needs explicit authorization after artifact publication.",
+8 -1
View File
@@ -982,7 +982,7 @@ function artifactConsumerPlanValidation(service: UniDeskMicroserviceConfig, envi
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",
"verifies image labels for service id, source repo, source commit, and Dockerfile",
"does not build source on the runtime target",
];
if (kind === "d601-k3s-managed") {
@@ -2997,6 +2997,7 @@ function environmentDryRunPlan(
tag: service.commitId,
imageRef: `127.0.0.1:5000/unidesk/${service.id}:${service.commitId}`,
digest: null,
digestHeader: "Docker-Content-Digest",
digestSource: "planned registry manifest HEAD; live apply records Docker-Content-Digest before mutation",
},
source: unsupported ? null : {
@@ -3011,6 +3012,12 @@ function environmentDryRunPlan(
willRunDockerComposeBuild: false,
producerBoundary: service.id === "backend-core" ? "ci publish-backend-core" : "ci publish-user-service",
},
requiredLabels: unsupported ? null : {
"unidesk.ai/service-id": service.id,
"unidesk.ai/source-repo": service.repo,
"unidesk.ai/source-commit": service.commitId,
"unidesk.ai/dockerfile": serviceConfig.repository.dockerfile,
},
noRuntimeSourceBuild: unsupported || planKind !== "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),