fix: expose publish preflight control channels

This commit is contained in:
Codex
2026-05-21 05:09:10 +00:00
parent 44e1fb2613
commit 71918843ae
5 changed files with 81 additions and 10 deletions
+59 -8
View File
@@ -87,13 +87,24 @@ interface DispatchResult {
raw: unknown;
}
type PublishPreflightControlChannel = "backend-core" | "database" | "provider" | "registry";
type PublishPreflightDetailedChannel = "backend-core-api" | "provider-dispatch" | "provider-host-ssh" | "database" | "artifact-registry";
interface PublishPreflightChannelProbe {
channel: "backend-core-api" | "provider-dispatch" | "provider-host-ssh" | "database" | "artifact-registry";
channel: PublishPreflightDetailedChannel;
controlChannel: PublishPreflightControlChannel;
ok: boolean;
requiredFor: string;
detail: unknown;
}
interface PublishPreflightControlChannelProbe {
channel: PublishPreflightControlChannel;
ok: boolean;
requiredFor: string[];
probes: PublishPreflightDetailedChannel[];
}
interface PublishPreflight {
ok: boolean;
runnerDisposition: "ready" | "infra-blocked";
@@ -102,6 +113,8 @@ interface PublishPreflight {
providerId: string;
supportedArtifactPublish: boolean;
missingChannels: string[];
missingControlChannels: PublishPreflightControlChannel[];
controlChannels: PublishPreflightControlChannelProbe[];
channels: PublishPreflightChannelProbe[];
registry: unknown;
next: string[];
@@ -362,12 +375,27 @@ function responseOk(response: unknown): boolean {
}
function channelProbe(
channel: PublishPreflightChannelProbe["channel"],
channel: PublishPreflightDetailedChannel,
controlChannel: PublishPreflightControlChannel,
ok: boolean,
requiredFor: string,
detail: unknown,
): PublishPreflightChannelProbe {
return { channel, ok, requiredFor, detail };
return { channel, controlChannel, ok, requiredFor, detail };
}
const publishPreflightControlChannelOrder: PublishPreflightControlChannel[] = ["backend-core", "database", "provider", "registry"];
function summarizePublishControlChannels(channels: PublishPreflightChannelProbe[]): PublishPreflightControlChannelProbe[] {
return publishPreflightControlChannelOrder.map((channel) => {
const probes = channels.filter((item) => item.controlChannel === channel);
return {
channel,
ok: probes.length > 0 && probes.every((item) => item.ok),
requiredFor: Array.from(new Set(probes.map((item) => item.requiredFor))),
probes: probes.map((item) => item.channel),
};
});
}
function backendCoreUnavailable(value: unknown): boolean {
@@ -1292,7 +1320,7 @@ async function publishUserServicePreflight(
const overview = await transport.coreFetch("/api/overview", { maxResponseBytes: 500_000 });
const overviewBody = coreBody(overview);
const backendCoreOk = responseOk(overview) && overviewBody?.dbReady === true;
channels.push(channelProbe("backend-core-api", backendCoreOk, "dispatch API, provider catalog, task polling, and database-backed CI state", {
channels.push(channelProbe("backend-core-api", "backend-core", backendCoreOk, "dispatch API, provider catalog, task polling, and database-backed CI state", {
ok: responseOk(overview),
dbReady: overviewBody?.dbReady ?? null,
runnerDisposition: asRecord(overview)?.runnerDisposition ?? null,
@@ -1302,7 +1330,7 @@ async function publishUserServicePreflight(
body: overviewBody,
},
}));
channels.push(channelProbe("database", backendCoreOk, "backend-core task dispatch, provider state, Tekton task polling, and source identity lookup", {
channels.push(channelProbe("database", "database", backendCoreOk, "backend-core task dispatch, provider state, Tekton task polling, and source identity lookup", {
dbReady: overviewBody?.dbReady ?? false,
observedThrough: "backend-core /api/overview",
}));
@@ -1316,14 +1344,14 @@ async function publishUserServicePreflight(
"test -S /var/run/docker.sock || test -S /run/docker.sock || true",
].join("\n");
const sshProbe = await transport.dispatchHostSsh(probeScript, 30_000, 15_000);
channels.push(channelProbe("provider-dispatch", sshProbe.taskId !== null || sshProbe.ok, "backend-core /api/dispatch can create D601 host.ssh tasks", {
channels.push(channelProbe("provider-dispatch", "provider", sshProbe.taskId !== null || sshProbe.ok, "backend-core /api/dispatch can create D601 host.ssh tasks", {
taskId: sshProbe.taskId,
status: sshProbe.status,
ok: sshProbe.taskId !== null || sshProbe.ok,
exitCode: sshProbe.exitCode,
stderrTail: sshProbe.stderr.slice(-1200),
}));
channels.push(channelProbe("provider-host-ssh", sshProbe.ok, "D601 source export, registry checks, kubectl/Tekton submission, and artifact summary reads", {
channels.push(channelProbe("provider-host-ssh", "provider", sshProbe.ok, "D601 source export, registry checks, kubectl/Tekton submission, and artifact summary reads", {
taskId: sshProbe.taskId,
status: sshProbe.status,
exitCode: sshProbe.exitCode,
@@ -1339,9 +1367,11 @@ async function publishUserServicePreflight(
const registry = artifactRegistryReadonlyResultFromCommand(registryProbe, registryCommand);
const registryRecord = asRecord(registry);
const registryOk = registryRecord?.ok === true || registryRecord?.runtimeApiHealthy === true;
channels.push(channelProbe("artifact-registry", registryOk, "commit-pinned image push and later CD manifest checks", registry));
channels.push(channelProbe("artifact-registry", "registry", registryOk, "commit-pinned image push and later CD manifest checks", registry));
const missingChannels = channels.filter((item) => !item.ok).map((item) => item.channel);
const controlChannels = summarizePublishControlChannels(channels);
const missingControlChannels = controlChannels.filter((item) => !item.ok).map((item) => item.channel);
const ready = missingChannels.length === 0;
return {
ok: ready,
@@ -1351,6 +1381,8 @@ async function publishUserServicePreflight(
providerId,
supportedArtifactPublish: true,
missingChannels,
missingControlChannels,
controlChannels,
channels,
registry,
next: ready
@@ -1359,6 +1391,7 @@ async function publishUserServicePreflight(
`later CD must consume ${plannedArtifact.imageRef}; CI itself must not deploy production`,
]
: [
`Restore missing control channel(s): ${missingControlChannels.join(", ") || "unknown"}.`,
"Run from the main-server CLI or use remote frontend transport against a healthy frontend/backend-core path.",
"Restore backend-core/database/provider-gateway/Host SSH connectivity before retrying artifact publication.",
"Use bun scripts/cli.ts artifact-registry health --provider-id D601 to recheck registry reachability after the control bridge is restored.",
@@ -1536,6 +1569,8 @@ async function publishUserServiceArtifact(config: UniDeskConfig, options: CiPubl
serviceId: options.serviceId,
supportedArtifactPublish: preflight.supportedArtifactPublish,
missingChannels: preflight.missingChannels,
missingControlChannels: preflight.missingControlChannels,
controlChannels: preflight.controlChannels,
channels: preflight.channels,
registry: preflight.registry,
sourceHostPath: options.sourceHostPath,
@@ -1555,6 +1590,13 @@ async function publishUserServiceArtifact(config: UniDeskConfig, options: CiPubl
},
artifact: plannedArtifact.imageRef,
artifactSummary: plannedArtifact,
controlledPublish: {
environment: "D601",
namespace: "unidesk-ci",
pipeline: "unidesk-user-service-artifact-publish",
command: `bun scripts/cli.ts ci publish-user-service --service ${options.serviceId} --commit ${options.commit} --wait-ms 1200000`,
requiresReadyControlChannels: publishPreflightControlChannelOrder,
},
boundary: preflight.boundary,
next: preflight.next,
};
@@ -1652,6 +1694,8 @@ export async function runCiPublishUserServiceDryRunPreflight(
serviceId: options.serviceId,
supportedArtifactPublish: preflight.supportedArtifactPublish,
missingChannels: preflight.missingChannels,
missingControlChannels: preflight.missingControlChannels,
controlChannels: preflight.controlChannels,
channels: preflight.channels,
registry: preflight.registry,
sourceHostPath: options.sourceHostPath,
@@ -1671,6 +1715,13 @@ export async function runCiPublishUserServiceDryRunPreflight(
},
artifact: plannedArtifact.imageRef,
artifactSummary: plannedArtifact,
controlledPublish: {
environment: "D601",
namespace: "unidesk-ci",
pipeline: "unidesk-user-service-artifact-publish",
command: `bun scripts/cli.ts ci publish-user-service --service ${options.serviceId} --commit ${options.commit} --wait-ms 1200000`,
requiresReadyControlChannels: publishPreflightControlChannelOrder,
},
boundary: preflight.boundary,
next: preflight.next,
};