fix: route publish dry-run through remote control plane
This commit is contained in:
+66
-1
@@ -87,6 +87,7 @@ interface DispatchResult {
|
||||
raw: unknown;
|
||||
}
|
||||
|
||||
type PublishPreflightFailureClassification = "auth-missing" | "remote-proxy-missing" | "provider-unreachable" | "local-docker-required";
|
||||
type PublishPreflightControlChannel = "backend-core" | "database" | "provider" | "registry";
|
||||
type PublishPreflightDetailedChannel = "backend-core-api" | "provider-dispatch" | "provider-host-ssh" | "database" | "artifact-registry";
|
||||
|
||||
@@ -108,6 +109,7 @@ interface PublishPreflightControlChannelProbe {
|
||||
interface PublishPreflight {
|
||||
ok: boolean;
|
||||
runnerDisposition: "ready" | "infra-blocked";
|
||||
failureClassification: PublishPreflightFailureClassification | null;
|
||||
serviceId: string;
|
||||
commit: string;
|
||||
providerId: string;
|
||||
@@ -117,11 +119,14 @@ interface PublishPreflight {
|
||||
controlChannels: PublishPreflightControlChannelProbe[];
|
||||
channels: PublishPreflightChannelProbe[];
|
||||
registry: unknown;
|
||||
controlPlane: Record<string, unknown>;
|
||||
next: string[];
|
||||
boundary: string;
|
||||
}
|
||||
|
||||
export interface PublishPreflightTransport {
|
||||
kind?: "local-docker" | "remote-frontend" | "provider-tunnel";
|
||||
remoteHost?: string | null;
|
||||
coreFetch: (path: string, init?: { method?: string; body?: unknown; maxResponseBytes?: number }) => unknown | Promise<unknown>;
|
||||
dispatchHostSsh: (command: string, waitMs: number, remoteTimeoutMs: number) => Promise<DispatchResult>;
|
||||
commandCwd: string;
|
||||
@@ -400,13 +405,63 @@ function summarizePublishControlChannels(channels: PublishPreflightChannelProbe[
|
||||
|
||||
function backendCoreUnavailable(value: unknown): boolean {
|
||||
const record = asRecord(value);
|
||||
const body = coreBody(value);
|
||||
if (record?.runnerDisposition === "infra-blocked") return true;
|
||||
if (record?.failureKind === "target-stack-not-running") return true;
|
||||
if (body?.runnerDisposition === "infra-blocked") return true;
|
||||
if (body?.failureKind === "target-stack-not-running") return true;
|
||||
if (body?.degradedReason === "backend-core-container-missing") return true;
|
||||
const text = JSON.stringify(value) ?? "";
|
||||
return text.includes("No such container: unidesk-backend-core")
|
||||
|| text.includes("No such container: unidesk-database");
|
||||
}
|
||||
|
||||
function transportKind(transport: PublishPreflightTransport): "local-docker" | "remote-frontend" | "provider-tunnel" {
|
||||
return transport.kind ?? "local-docker";
|
||||
}
|
||||
|
||||
function classifyPublishPreflightFailure(
|
||||
transport: PublishPreflightTransport,
|
||||
overview: unknown,
|
||||
sshProbe: DispatchResult,
|
||||
registry: unknown,
|
||||
): PublishPreflightFailureClassification | null {
|
||||
const kind = transportKind(transport);
|
||||
const overviewRecord = asRecord(overview);
|
||||
if (overviewRecord?.failureClassification === "auth-missing") return "auth-missing";
|
||||
if (overviewRecord?.failureClassification === "remote-proxy-missing") return "remote-proxy-missing";
|
||||
if (overviewRecord?.failureClassification === "provider-unreachable") return "provider-unreachable";
|
||||
if (kind === "local-docker" && backendCoreUnavailable(overview)) return "local-docker-required";
|
||||
if (kind !== "local-docker" && !responseOk(overview)) return "remote-proxy-missing";
|
||||
if (!sshProbe.ok) return "provider-unreachable";
|
||||
const registryRecord = asRecord(registry);
|
||||
if (registryRecord?.ok === false) return "provider-unreachable";
|
||||
return null;
|
||||
}
|
||||
|
||||
function publishPreflightControlPlane(
|
||||
transport: PublishPreflightTransport,
|
||||
failureClassification: PublishPreflightFailureClassification | null,
|
||||
): Record<string, unknown> {
|
||||
const kind = transportKind(transport);
|
||||
const remoteCapable = kind !== "local-docker";
|
||||
return {
|
||||
transport: kind,
|
||||
remoteCapable,
|
||||
remoteHost: transport.remoteHost ?? null,
|
||||
localDockerRequired: kind === "local-docker",
|
||||
failureClassification,
|
||||
preferredRunnerPath: "frontend-private-proxy",
|
||||
fallbackRunnerPath: "main-server-local-docker",
|
||||
remoteCommandShape: "bun scripts/cli.ts --main-server-ip <host> ci publish-user-service --service <id> --commit <full-sha> --dry-run",
|
||||
providerTunnel: {
|
||||
providerId: d601ProviderId,
|
||||
command: "host.ssh",
|
||||
requiredFor: ["provider-host-ssh", "artifact-registry"],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function dispatchPreflightFailure(command: string, result: DispatchResult): DispatchResult {
|
||||
return {
|
||||
ok: false,
|
||||
@@ -468,6 +523,7 @@ function artifactRegistryProbeCommand(probe: ArtifactRegistryReadonlyProbe): str
|
||||
}
|
||||
|
||||
const localPublishPreflightTransport: PublishPreflightTransport = {
|
||||
kind: "local-docker",
|
||||
coreFetch: (path, init) => coreInternalFetch(path, init),
|
||||
dispatchHostSsh: dispatchReadonlySsh,
|
||||
commandCwd: repoRoot,
|
||||
@@ -1373,9 +1429,11 @@ async function publishUserServicePreflight(
|
||||
const controlChannels = summarizePublishControlChannels(channels);
|
||||
const missingControlChannels = controlChannels.filter((item) => !item.ok).map((item) => item.channel);
|
||||
const ready = missingChannels.length === 0;
|
||||
const failureClassification = ready ? null : classifyPublishPreflightFailure(transport, overview, sshProbe, registry);
|
||||
return {
|
||||
ok: ready,
|
||||
runnerDisposition: ready ? "ready" : "infra-blocked",
|
||||
failureClassification,
|
||||
serviceId: options.serviceId,
|
||||
commit: options.commit,
|
||||
providerId,
|
||||
@@ -1385,6 +1443,7 @@ async function publishUserServicePreflight(
|
||||
controlChannels,
|
||||
channels,
|
||||
registry,
|
||||
controlPlane: publishPreflightControlPlane(transport, failureClassification),
|
||||
next: ready
|
||||
? [
|
||||
`bun scripts/cli.ts ci publish-user-service --service ${options.serviceId} --commit ${options.commit} --wait-ms 1200000`,
|
||||
@@ -1392,7 +1451,9 @@ async function publishUserServicePreflight(
|
||||
]
|
||||
: [
|
||||
`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.",
|
||||
failureClassification === "local-docker-required"
|
||||
? "From a Code Queue runner, rerun this read-only preflight through the existing remote frontend transport: bun scripts/cli.ts --main-server-ip <host> ci publish-user-service --service <id> --commit <full-sha> --dry-run."
|
||||
: "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.",
|
||||
],
|
||||
@@ -1572,6 +1633,8 @@ async function publishUserServiceArtifact(config: UniDeskConfig, options: CiPubl
|
||||
missingControlChannels: preflight.missingControlChannels,
|
||||
controlChannels: preflight.controlChannels,
|
||||
channels: preflight.channels,
|
||||
failureClassification: preflight.failureClassification,
|
||||
controlPlane: preflight.controlPlane,
|
||||
registry: preflight.registry,
|
||||
sourceHostPath: options.sourceHostPath,
|
||||
source: {
|
||||
@@ -1697,6 +1760,8 @@ export async function runCiPublishUserServiceDryRunPreflight(
|
||||
missingControlChannels: preflight.missingControlChannels,
|
||||
controlChannels: preflight.controlChannels,
|
||||
channels: preflight.channels,
|
||||
failureClassification: preflight.failureClassification,
|
||||
controlPlane: preflight.controlPlane,
|
||||
registry: preflight.registry,
|
||||
sourceHostPath: options.sourceHostPath,
|
||||
source: {
|
||||
|
||||
Reference in New Issue
Block a user