diff --git a/deploy.json b/deploy.json index 71bd6b76..89d3c030 100644 --- a/deploy.json +++ b/deploy.json @@ -120,7 +120,43 @@ { "id": "decision-center", "repo": "https://github.com/pikasTech/unidesk", - "commitId": "b5486a61ab0aa6c227366a95d1afa68281584359" + "commitId": "b5486a61ab0aa6c227366a95d1afa68281584359", + "artifact": { + "kind": "source-build", + "repository": "unidesk/decision-center", + "tag": "commitId" + }, + "consumer": { + "kind": "d601-k3s-managed", + "dev": { + "enabled": true + }, + "prod": { + "enabled": false + }, + "supportLevel": "reviewed", + "targetRef": "origin/master:deploy.json#environments.dev.services.decision-center", + "noRuntimeSourceBuild": true, + "target": { + "namespace": "unidesk-dev", + "deployment": "decision-center-dev", + "service": "decision-center-dev", + "containerName": "decision-center", + "stableImage": "unidesk-decision-center:dev", + "manifestRepoPath": "src/components/microservices/k3sctl-adapter/k3s/dev/unidesk-dev-decision-center.k8s.yaml" + } + }, + "runtime": { + "containerPort": 4277, + "healthPath": "/health", + "memory": { + "request": "96Mi", + "limit": "512Mi" + }, + "health": { + "deployMetadataRequired": true + } + } }, { "id": "mdtodo", diff --git a/docs/reference/cicd-standardization.md b/docs/reference/cicd-standardization.md index 53574512..3e4bfb07 100644 --- a/docs/reference/cicd-standardization.md +++ b/docs/reference/cicd-standardization.md @@ -36,14 +36,14 @@ Phase one extends the desired-state model in this order: 4. Add deployment-time metadata that must be common across CI, dev CD and prod CD: deploy env prefix, health deploy-metadata requirement, required runtime secret key names, rollback policy, service port, health path, and resource profile identifiers. The planned first keys are `runtime.containerPort`, `runtime.healthPath`, `runtime.memory.request`, `runtime.memory.limit`, `health.deployMetadataRequired` and `runtime.requiredSecretKeys`. Secret values, credentials, volumes and host paths do not move into `deploy.json`. 5. Teach CI producer dry-run, deploy plan and artifact-registry dry-run to render from `deploy.json` first and compare mirrored `CI.json`/`config.json`/manifest values as derived copies. A mismatch is drift, not an alternate source of truth. -Phase two starts with the low-risk `dev/mdtodo` artifact consumer. Its `deploy.json` entry now owns the source-build artifact repository/tag policy, D601 k3s consumer target, stable image, manifest reference, service port, health path and memory request/limit. `deploy plan --env dev --service mdtodo` and `deploy apply --env dev --service mdtodo --dry-run` must read those fields from `deploy.json`; the k8s manifest and artifact-registry executor constants are derived mirrors checked by a structured `deploy-json-drift` preflight. +Phase two starts with low-risk D601 k3s user-service artifact consumers: `dev/mdtodo` and `dev/decision-center`. Their `deploy.json` entries now own the source-build artifact repository/tag policy, D601 k3s consumer target, stable image, manifest reference, service port, health path, memory request/limit and health deploy-metadata requirement. `deploy plan --env dev --service ` and dry-run artifact consumer plans must read those fields from `deploy.json`; the k8s manifest and artifact-registry executor constants are derived mirrors checked by a structured `deploy-json-drift` preflight. Fields that stay outside `deploy.json` during phase one: - `CI.json`: producer pipeline name, source root, Dockerfile path and success summary shape. Once artifact identity is in `deploy.json`, `CI.json.artifacts[].image.repository` becomes a compatibility mirror checked for drift. -- `config.json`: provider id, proxy route policy, Compose file/service/container names for services not yet migrated, development SSH/worktree details, and secret source locations. For `dev/mdtodo`, port/health target values in dry-run are deploy-owned and config is no longer an alternate source. -- Compose and Kubernetes manifests: volumes, PVCs, security context and rollout strategy remain concrete manifests. For `dev/mdtodo`, container port and memory request/limit are deploy-owned values and manifest copies are drift-checked mirrors until a renderer owns the file. -- Artifact-registry executor code: low-level pull/import/retag/recreate commands, registry probes and platform-specific verification scripts. For `dev/mdtodo`, executor dry-run consumes the deploy-owned artifact/consumer/runtime contract; any hardcoded mismatch returns `deploy-json-drift`. +- `config.json`: provider id, proxy route policy, Compose file/service/container names for services not yet migrated, development SSH/worktree details, and secret source locations. For `dev/mdtodo` and `dev/decision-center`, port/health target values in dry-run are deploy-owned and config is no longer an alternate source. +- Compose and Kubernetes manifests: volumes, PVCs, security context and rollout strategy remain concrete manifests. For `dev/mdtodo` and `dev/decision-center`, container port, memory request/limit and deploy metadata env presence are deploy-owned values and manifest copies are drift-checked mirrors until a renderer owns the file. +- Artifact-registry executor code: low-level pull/import/retag/recreate commands, registry probes and platform-specific verification scripts. For `dev/mdtodo` and `dev/decision-center`, executor dry-run consumes the deploy-owned artifact/consumer/runtime contract; any hardcoded mismatch returns `deploy-json-drift`. The drift contract is: @@ -55,7 +55,7 @@ The drift contract is: Lightweight evidence for this contract is `bun scripts/issue-60-cicd-drift-contract-test.ts`. The test parses local JSON/docs only; it does not publish images, deploy services, restart containers, run Playwright or run full e2e. -Focused second-stage evidence is `bun scripts/issue-60-deploy-json-executor-preflight-contract-test.ts`. It runs only dry-run/contract paths, verifies the `dev/mdtodo` executor reads image, target, port and memory from `deploy.json`, and simulates drift to assert field-level expected/actual error output. +Focused second-stage evidence is `bun scripts/issue-60-deploy-json-executor-preflight-contract-test.ts`. It runs only dry-run/contract paths, verifies the `dev/mdtodo` and `dev/decision-center` executors read image, target, port, memory and deploy metadata requirements from `deploy.json`, and simulates drift to assert field-level expected/actual error output. ## Artifact Catalog diff --git a/scripts/issue-60-cicd-drift-contract-test.ts b/scripts/issue-60-cicd-drift-contract-test.ts index 85f021b7..58b84a2b 100644 --- a/scripts/issue-60-cicd-drift-contract-test.ts +++ b/scripts/issue-60-cicd-drift-contract-test.ts @@ -59,7 +59,7 @@ const plannedDeployJsonFields = [ "runtime.requiredSecretKeys", ]; -const phaseTwoExecutorContractServices = new Set(["dev/mdtodo"]); +const phaseTwoExecutorContractServices = new Set(["dev/decision-center", "dev/mdtodo"]); function assertCondition(condition: unknown, message: string, detail: unknown = {}): void { if (!condition) throw new Error(`${message}: ${JSON.stringify(detail)}`); diff --git a/scripts/issue-60-deploy-json-executor-preflight-contract-test.ts b/scripts/issue-60-deploy-json-executor-preflight-contract-test.ts index 24f08cdf..93cd43ce 100644 --- a/scripts/issue-60-deploy-json-executor-preflight-contract-test.ts +++ b/scripts/issue-60-deploy-json-executor-preflight-contract-test.ts @@ -28,6 +28,12 @@ function asArray(value: unknown, label: string): unknown[] { return value; } +interface ContractCase { + serviceId: string; + expectedRuntimeImage: string; + driftPort: number; +} + function deployService(environment: "dev" | "prod", serviceId: string): DeployJsonServiceContract { const deploy = asRecord(JSON.parse(readFileSync(rootPath("deploy.json"), "utf8")) as unknown, "deploy.json"); const environments = asRecord(deploy.environments, "deploy.json.environments"); @@ -38,90 +44,127 @@ function deployService(environment: "dev" | "prod", serviceId: string): DeployJs return parseDeployJsonServiceContract(raw, `deploy.json.environments.${environment}.services.${serviceId}`); } -const service = deployService("dev", "mdtodo"); -const artifact = asRecord(service.artifact, "mdtodo artifact"); -const consumer = asRecord(service.consumer, "mdtodo consumer"); -const targetContract = asRecord(consumer.target, "mdtodo consumer target"); -const runtime = asRecord(service.runtime, "mdtodo runtime"); -const memory = asRecord(runtime.memory, "mdtodo runtime memory"); -const commit = String(service.commitId); -const sourceOfTruth = deployJsonSourceOfTruth(service, "dev"); const deploySource = readFileSync(rootPath("scripts/src/deploy.ts"), "utf8"); assertCondition(deploySource.includes('"--deploy-json-service", encodeDeployJsonServiceContract(service)'), "deploy executor must pass deploy.json service contract into artifact-registry", {}); -const applyPlan = asRecord(await runArtifactRegistryCommand([ - "deploy-service", - "--env", - "dev", - "--service", - "mdtodo", - "--commit", - commit, - "--source-repo", - service.repo, - "--deploy-ref", - "origin/master:deploy.json#environments.dev.services.mdtodo", - "--deploy-json-service", - encodeDeployJsonServiceContract(service), - "--dry-run", -]), "artifact-registry dry-run result"); -const applyTarget = asRecord(applyPlan.target, "artifact-registry target"); -const applyRegistry = asRecord(applyPlan.registry, "artifact-registry registry"); -const applyRuntime = asRecord(applyPlan.runtime, "artifact-registry runtime"); -const applyRuntimeMemory = asRecord(applyRuntime.memory, "artifact-registry runtime memory"); -const applyDriftCheck = asRecord(applyPlan.driftCheck, "artifact-registry driftCheck"); -assertCondition(applyPlan.sourceOfTruth !== undefined, "artifact-registry dry-run should expose deploy.json sourceOfTruth", applyPlan); -assertCondition(JSON.stringify(applyPlan.sourceOfTruth) === JSON.stringify(sourceOfTruth), "artifact-registry sourceOfTruth must enumerate deploy.json fields", applyPlan.sourceOfTruth); -assertCondition(applyDriftCheck.ok === true, "artifact-registry dry-run must report a passing drift preflight", applyDriftCheck); -assertCondition(applyRegistry.repository === artifact.repository, "artifact-registry dry-run repository must come from deploy.json", applyRegistry); -assertCondition(applyRegistry.tag === commit, "artifact-registry dry-run tag must be deploy.json commitId", applyRegistry); -assertCondition(applyRegistry.imageRef === `127.0.0.1:5000/${artifact.repository}:${commit}`, "artifact-registry imageRef must be deploy.json artifact repository + commit", applyRegistry); -assertCondition(applyTarget.namespace === targetContract.namespace, "artifact-registry dry-run namespace must come from deploy.json", applyTarget); -assertCondition(applyTarget.deployment === targetContract.deployment, "artifact-registry dry-run deployment must come from deploy.json", applyTarget); -assertCondition(applyTarget.service === targetContract.service, "artifact-registry dry-run service must come from deploy.json", applyTarget); -assertCondition(asArray(applyTarget.deployments, "artifact-registry deployments").some((item) => asRecord(item, "deployment").name === targetContract.deployment), "artifact-registry deployments must come from deploy.json target", applyTarget); -assertCondition(applyTarget.stableImage === targetContract.stableImage, "artifact-registry stableImage must come from deploy.json", applyTarget); -assertCondition(applyTarget.runtimeImage === `unidesk-mdtodo:${commit}`, "artifact-registry runtimeImage must derive from deploy.json stableImage + commit", applyTarget); -assertCondition(applyTarget.manifestRepoPath === targetContract.manifestRepoPath, "artifact-registry manifest path must come from deploy.json", applyTarget); -assertCondition(applyRuntime.sourceOfTruth === "deploy.json", "artifact-registry runtime source must be deploy.json", applyRuntime); -assertCondition(applyRuntime.containerPort === runtime.containerPort, "artifact-registry runtime port must come from deploy.json", applyRuntime); -assertCondition(applyRuntime.healthPath === runtime.healthPath, "artifact-registry runtime healthPath must come from deploy.json", applyRuntime); -assertCondition(applyRuntimeMemory.request === memory.request && applyRuntimeMemory.limit === memory.limit, "artifact-registry memory must come from deploy.json", applyRuntimeMemory); - -const manifestMirror = k3sManifestExecutorMirror(service); -assertCondition(manifestMirror !== null, "mdtodo deploy.json contract should locate a k8s manifest mirror"); -const cleanDrifts = compareDeployJsonExecutorMirrors(service, "dev", [manifestMirror!]); -assertCondition(cleanDrifts.length === 0, "current k8s manifest mirror should match deploy.json contract", cleanDrifts); - -const driftMirror: DeployJsonExecutorMirror = { - ...manifestMirror!, - runtime: { - ...manifestMirror!.runtime, - containerPort: 4268, - memory: { - ...manifestMirror!.runtime?.memory, - limit: "384Mi", - }, +const cases: ContractCase[] = [ + { + serviceId: "mdtodo", + expectedRuntimeImage: "unidesk-mdtodo", + driftPort: 4268, }, -}; -const drift = compareDeployJsonExecutorMirrors(service, "dev", [driftMirror]); -const driftResult = deployJsonDriftResult(service, "dev", drift); -const driftPayload = asRecord(driftResult.drift, "drift result payload"); -const driftItems = asArray(driftPayload.items, "drift result items").map((item) => asRecord(item, "drift item")); -assertCondition(driftResult.ok === false, "drift result must be non-ok", driftResult); -assertCondition(driftResult.error === "deploy-json-drift", "drift result should use structured deploy-json-drift error", driftResult); -assertCondition(driftItems.some((item) => item.field === "runtime.containerPort" && item.expected === 4267 && item.actual === 4268), "drift result should report port mismatch", driftItems); -assertCondition(driftItems.some((item) => item.field === "runtime.memory.limit" && item.expected === "512Mi" && item.actual === "384Mi"), "drift result should report memory mismatch", driftItems); + { + serviceId: "decision-center", + expectedRuntimeImage: "unidesk-decision-center", + driftPort: 4278, + }, +]; + +const verifiedServices: string[] = []; +for (const item of cases) { + const service = deployService("dev", item.serviceId); + const artifact = asRecord(service.artifact, `${item.serviceId} artifact`); + const consumer = asRecord(service.consumer, `${item.serviceId} consumer`); + const targetContract = asRecord(consumer.target, `${item.serviceId} consumer target`); + const runtime = asRecord(service.runtime, `${item.serviceId} runtime`); + const memory = asRecord(runtime.memory, `${item.serviceId} runtime memory`); + const health = asRecord(runtime.health, `${item.serviceId} runtime health`); + const commit = String(service.commitId); + const sourceOfTruth = deployJsonSourceOfTruth(service, "dev"); + + const applyPlan = asRecord(await runArtifactRegistryCommand([ + "deploy-service", + "--env", + "dev", + "--service", + item.serviceId, + "--commit", + commit, + "--source-repo", + service.repo, + "--deploy-ref", + `origin/master:deploy.json#environments.dev.services.${item.serviceId}`, + "--deploy-json-service", + encodeDeployJsonServiceContract(service), + "--dry-run", + ]), `${item.serviceId} artifact-registry dry-run result`); + const applyTarget = asRecord(applyPlan.target, `${item.serviceId} artifact-registry target`); + const applyRegistry = asRecord(applyPlan.registry, `${item.serviceId} artifact-registry registry`); + const applyRuntime = asRecord(applyPlan.runtime, `${item.serviceId} artifact-registry runtime`); + const applyRuntimeMemory = asRecord(applyRuntime.memory, `${item.serviceId} artifact-registry runtime memory`); + const applyRuntimeHealth = asRecord(applyRuntime.health, `${item.serviceId} artifact-registry runtime health`); + const applyDriftCheck = asRecord(applyPlan.driftCheck, `${item.serviceId} artifact-registry driftCheck`); + assertCondition(applyPlan.sourceOfTruth !== undefined, `${item.serviceId} artifact-registry dry-run should expose deploy.json sourceOfTruth`, applyPlan); + assertCondition(JSON.stringify(applyPlan.sourceOfTruth) === JSON.stringify(sourceOfTruth), `${item.serviceId} artifact-registry sourceOfTruth must enumerate deploy.json fields`, applyPlan.sourceOfTruth); + assertCondition(applyDriftCheck.ok === true, `${item.serviceId} artifact-registry dry-run must report a passing drift preflight`, applyDriftCheck); + assertCondition(applyRegistry.repository === artifact.repository, `${item.serviceId} artifact-registry dry-run repository must come from deploy.json`, applyRegistry); + assertCondition(applyRegistry.tag === commit, `${item.serviceId} artifact-registry dry-run tag must be deploy.json commitId`, applyRegistry); + assertCondition(applyRegistry.imageRef === `127.0.0.1:5000/${artifact.repository}:${commit}`, `${item.serviceId} artifact-registry imageRef must be deploy.json artifact repository + commit`, applyRegistry); + assertCondition(applyTarget.namespace === targetContract.namespace, `${item.serviceId} artifact-registry dry-run namespace must come from deploy.json`, applyTarget); + assertCondition(applyTarget.deployment === targetContract.deployment, `${item.serviceId} artifact-registry dry-run deployment must come from deploy.json`, applyTarget); + assertCondition(applyTarget.service === targetContract.service, `${item.serviceId} artifact-registry dry-run service must come from deploy.json`, applyTarget); + assertCondition(asArray(applyTarget.deployments, `${item.serviceId} artifact-registry deployments`).some((deployment) => asRecord(deployment, "deployment").name === targetContract.deployment), `${item.serviceId} artifact-registry deployments must come from deploy.json target`, applyTarget); + assertCondition(applyTarget.stableImage === targetContract.stableImage, `${item.serviceId} artifact-registry stableImage must come from deploy.json`, applyTarget); + assertCondition(applyTarget.runtimeImage === `${item.expectedRuntimeImage}:${commit}`, `${item.serviceId} artifact-registry runtimeImage must derive from deploy.json stableImage + commit`, applyTarget); + assertCondition(applyTarget.manifestRepoPath === targetContract.manifestRepoPath, `${item.serviceId} artifact-registry manifest path must come from deploy.json`, applyTarget); + assertCondition(applyRuntime.sourceOfTruth === "deploy.json", `${item.serviceId} artifact-registry runtime source must be deploy.json`, applyRuntime); + assertCondition(applyRuntime.containerPort === runtime.containerPort, `${item.serviceId} artifact-registry runtime port must come from deploy.json`, applyRuntime); + assertCondition(applyRuntime.healthPath === runtime.healthPath, `${item.serviceId} artifact-registry runtime healthPath must come from deploy.json`, applyRuntime); + assertCondition(applyRuntimeMemory.request === memory.request && applyRuntimeMemory.limit === memory.limit, `${item.serviceId} artifact-registry memory must come from deploy.json`, applyRuntimeMemory); + assertCondition(applyRuntimeHealth.deployMetadataRequired === health.deployMetadataRequired, `${item.serviceId} artifact-registry deploy metadata requirement must come from deploy.json`, applyRuntimeHealth); + + const implicitPlan = asRecord(await runArtifactRegistryCommand([ + "deploy-service", + "--env", + "dev", + "--service", + item.serviceId, + "--commit", + commit, + "--dry-run", + ]), `${item.serviceId} implicit deploy.json dry-run result`); + assertCondition(asRecord(implicitPlan.registry, `${item.serviceId} implicit registry`).repository === artifact.repository, `${item.serviceId} implicit artifact-registry dry-run must read deploy.json from file`, implicitPlan); + assertCondition(asRecord(implicitPlan.driftCheck, `${item.serviceId} implicit driftCheck`).ok === true, `${item.serviceId} implicit artifact-registry dry-run must drift-check deploy.json`, implicitPlan); + + const manifestMirror = k3sManifestExecutorMirror(service); + assertCondition(manifestMirror !== null, `${item.serviceId} deploy.json contract should locate a k8s manifest mirror`); + const cleanDrifts = compareDeployJsonExecutorMirrors(service, "dev", [manifestMirror!]); + assertCondition(cleanDrifts.length === 0, `${item.serviceId} current k8s manifest mirror should match deploy.json contract`, cleanDrifts); + + const driftMirror: DeployJsonExecutorMirror = { + ...manifestMirror!, + runtime: { + ...manifestMirror!.runtime, + containerPort: item.driftPort, + memory: { + ...manifestMirror!.runtime?.memory, + limit: "384Mi", + }, + health: { + ...manifestMirror!.runtime?.health, + deployMetadataRequired: false, + }, + }, + }; + const drift = compareDeployJsonExecutorMirrors(service, "dev", [driftMirror]); + const driftResult = deployJsonDriftResult(service, "dev", drift); + const driftPayload = asRecord(driftResult.drift, `${item.serviceId} drift result payload`); + const driftItems = asArray(driftPayload.items, `${item.serviceId} drift result items`).map((entry) => asRecord(entry, "drift item")); + assertCondition(driftResult.ok === false, `${item.serviceId} drift result must be non-ok`, driftResult); + assertCondition(driftResult.error === "deploy-json-drift", `${item.serviceId} drift result should use structured deploy-json-drift error`, driftResult); + assertCondition(driftItems.some((entry) => entry.field === "runtime.containerPort" && entry.expected === runtime.containerPort && entry.actual === item.driftPort), `${item.serviceId} drift result should report port mismatch`, driftItems); + assertCondition(driftItems.some((entry) => entry.field === "runtime.memory.limit" && entry.expected === "512Mi" && entry.actual === "384Mi"), `${item.serviceId} drift result should report memory mismatch`, driftItems); + assertCondition(driftItems.some((entry) => entry.field === "runtime.health.deployMetadataRequired" && entry.expected === true && entry.actual === false), `${item.serviceId} drift result should report deploy metadata requirement mismatch`, driftItems); + verifiedServices.push(item.serviceId); +} process.stdout.write(`${JSON.stringify({ ok: true, checks: [ "deploy executor passes the deploy.json service contract to artifact-registry dry-run", - "artifact-registry dry-run reads mdtodo image, target, port and memory fields from deploy.json", - "k8s manifest target/port/memory are treated as derived mirrors and checked for drift", + "artifact-registry dry-run reads k3s user-service image, target, port, memory and deploy metadata fields from deploy.json", + "k8s manifest target/port/memory/deploy metadata are treated as derived mirrors and checked for drift", "drift preflight returns structured deploy-json-drift with field-level expected/actual values", ], - serviceId: service.id, - commit, - sourceOfTruth, + services: verifiedServices, }, null, 2)}\n`); diff --git a/scripts/src/artifact-registry.ts b/scripts/src/artifact-registry.ts index f36c28e4..d5693f25 100644 --- a/scripts/src/artifact-registry.ts +++ b/scripts/src/artifact-registry.ts @@ -1396,7 +1396,7 @@ function deployRefFor(options: ArtifactRegistryOptions, spec: ArtifactConsumerSp function deployJsonServiceForOptions(options: ArtifactRegistryOptions, spec: ArtifactConsumerSpec, environment: ArtifactDeployEnvironment): DeployJsonServiceContract | null { if (!options.dryRun) return null; if (options.deployJsonService !== null) return options.deployJsonService; - if (environment === "dev" && spec.serviceId === "mdtodo") { + if (environment === "dev" && (spec.serviceId === "decision-center" || spec.serviceId === "mdtodo")) { return readDeployJsonServiceContractFromFile(environment, spec.serviceId); } return null; @@ -1444,6 +1444,9 @@ function artifactRegistryDeployJsonMirrors( containerPort: target.k3s.servicePort, servicePort: target.k3s.servicePort, healthPath: target.k3s.healthPath, + health: options.deployJsonService?.runtime?.health === undefined ? undefined : { + deployMetadataRequired: true, + }, }, }, ]; diff --git a/scripts/src/deploy-json-contract.ts b/scripts/src/deploy-json-contract.ts index 48479845..4d6be157 100644 --- a/scripts/src/deploy-json-contract.ts +++ b/scripts/src/deploy-json-contract.ts @@ -78,6 +78,9 @@ export interface DeployJsonExecutorMirror { request?: string; limit?: string; }; + health?: { + deployMetadataRequired?: boolean; + }; }; } @@ -295,6 +298,9 @@ export function compareDeployJsonExecutorMirrors( pushDrift(items, mirror, "runtime.healthPath", service.runtime.healthPath, mirror.runtime.healthPath); pushDrift(items, mirror, "runtime.memory.request", service.runtime.memory.request, mirror.runtime.memory?.request); pushDrift(items, mirror, "runtime.memory.limit", service.runtime.memory.limit, mirror.runtime.memory?.limit); + if (service.runtime.health !== undefined) { + pushDrift(items, mirror, "runtime.health.deployMetadataRequired", service.runtime.health.deployMetadataRequired, mirror.runtime.health?.deployMetadataRequired); + } } } return items; @@ -366,6 +372,14 @@ export function k3sManifestExecutorMirror(service: DeployJsonServiceContract): D request: firstRegex(container, /^\s+requests:\s*$[\s\S]*?^\s+memory:\s*([^\s#"]+)\s*$/mu), limit: firstRegex(container, /^\s+limits:\s*$[\s\S]*?^\s+memory:\s*([^\s#"]+)\s*$/mu), }, + health: { + deployMetadataRequired: [ + "UNIDESK_DEPLOY_SERVICE_ID", + "UNIDESK_DEPLOY_REPO", + "UNIDESK_DEPLOY_COMMIT", + "UNIDESK_DEPLOY_REQUESTED_COMMIT", + ].every((name) => container.includes(`name: ${name}`)), + }, }, }; } diff --git a/scripts/src/deploy.ts b/scripts/src/deploy.ts index 209b867c..db66a5f6 100644 --- a/scripts/src/deploy.ts +++ b/scripts/src/deploy.ts @@ -1042,6 +1042,9 @@ function deployJsonExecutorMirrors( containerPort: serviceConfig.backend.nodePort, servicePort: serviceConfig.backend.nodePort, healthPath: serviceConfig.backend.healthPath, + health: service.runtime?.health === undefined ? undefined : { + deployMetadataRequired: true, + }, }, }); const manifestMirror = k3sManifestExecutorMirror(service);