feat: prepare 2.6.1-rc5 shared databases and build SSE

This commit is contained in:
2026-10-05 11:09:52 +00:00 Unverified
parent f76165603e
commit 828bf3a328
25 changed files with 2557 additions and 196 deletions
+1 -1
View File
@@ -37,7 +37,7 @@ function cloneCommand<T extends object>(command: T): T {
export const main = defineCommand({
meta: {
name: "kuber",
version: "2.6.0",
version: "2.6.1-rc5",
description: "Docker Compose -> K8s translation layer",
},
args: {
+54 -34
View File
@@ -1,5 +1,5 @@
import { defineCommand } from "citty";
import { Listr, ListrErrorTypes, type ListrTaskWrapper } from "listr2";
import { Listr, type ListrTaskWrapper } from "listr2";
import { randomUUID } from "node:crypto";
import { isDeepStrictEqual } from "node:util";
import type { ComposeSpecification } from "../schema/docker.d";
@@ -104,6 +104,8 @@ type UpContext = {
workspaceRoot?: string;
buildImages?: Record<string, string>;
serviceEnv?: Record<string, Record<string, string>>;
databaseEnv?: Record<string, Record<string, string>>;
storageEnv?: Record<string, Record<string, string>>;
resources?: KubernetesResource[];
plan?: ResourcePlan;
};
@@ -796,14 +798,20 @@ export async function runUp(
cwd,
{
progress: (message) => {
if (task.output !== message) task.output = message;
if (
!/^Build (?:queued|creating|starting|running|done)\b/.test(message) &&
task.output !== message
) task.output = message;
},
service: (name) => {
let stream: ReturnType<ListrTaskWrapper<UpContext, any, any>["stdout"]> | undefined;
return {
progress: (message) => {
const child = children.get(name);
if (child && child.output !== message) child.output = message;
if (!child) return;
const phase = /^Build (queued|creating|starting|running|done)\b/.exec(message)?.[1];
if (phase) child.title = `Build ${name}: ${phase}`;
// A failed status includes the full error; the parent bottom bar owns it.
},
get stream() {
const child = children.get(name);
@@ -833,7 +841,6 @@ export async function runUp(
error instanceof Error ? error : new Error(String(error));
operationFailure = failure;
task.output = failure.stack ?? failure.message;
task.report(failure, ListrErrorTypes.HAS_FAILED);
for (const resolve of pending.values()) resolve(failure);
pending.clear();
},
@@ -851,11 +858,14 @@ export async function runUp(
child: ListrTaskWrapper<UpContext, any, any>,
) => {
children.set(name, child);
child.output = "Build queued";
child.title = `Build ${name}: queued`;
if (children.size === services.length) start();
const error = await completed.get(name);
if (error) throw error;
child.output = "done";
if (error) {
child.title = `Build ${name}: failed`;
throw error;
}
child.title = `Build ${name}: done`;
},
})),
{
@@ -916,24 +926,29 @@ export async function runUp(
},
{
title: "Reconcile backing services",
task: async (taskCtx, task) => {
let databaseEnv: Record<string, Record<string, string>> = {};
let storageEnv: Record<string, Record<string, string>> = {};
await task.newListr(
rendererOptions: { bottomBar: 1, persistentOutput: true },
task: (taskCtx, task) =>
task.newListr(
[
{
title: "Reconcile databases",
enabled: () =>
getComposePostgresClaims(taskCtx.compose!).length > 0,
task: async (_ctx, child) => {
const response = await managementRequest<Record<string, unknown>>(
project,
request,
`${workspacePath}/databases`,
{ method: "POST", json: { compose: taskCtx.compose } },
);
databaseEnv = operationEnvironment(response);
child.output = `${Object.keys(databaseEnv).length} services`;
let response: Record<string, unknown>;
try {
response = await managementRequest<Record<string, unknown>>(
project,
request,
`${workspacePath}/databases`,
{ method: "POST", json: { compose: taskCtx.compose } },
);
} catch (error) {
task.output = error instanceof Error ? error.message : String(error);
throw error;
}
taskCtx.databaseEnv = operationEnvironment(response);
child.output = `${Object.keys(taskCtx.databaseEnv).length} services`;
},
},
{
@@ -941,28 +956,33 @@ export async function runUp(
enabled: () =>
getComposeS3Claims(taskCtx.compose!).length > 0,
task: async (_ctx, child) => {
const response = await managementRequest<Record<string, unknown>>(
project,
request,
`${workspacePath}/storage`,
{ method: "POST", json: { compose: taskCtx.compose } },
);
storageEnv = operationEnvironment(response);
child.output = `${Object.keys(storageEnv).length} services`;
let response: Record<string, unknown>;
try {
response = await managementRequest<Record<string, unknown>>(
project,
request,
`${workspacePath}/storage`,
{ method: "POST", json: { compose: taskCtx.compose } },
);
} catch (error) {
task.output = error instanceof Error ? error.message : String(error);
throw error;
}
taskCtx.storageEnv = operationEnvironment(response);
child.output = `${Object.keys(taskCtx.storageEnv).length} services`;
},
},
],
{ concurrent: true },
).run();
taskCtx.serviceEnv = mergeServiceEnv(
mergeServiceEnv(taskCtx.serviceEnv, databaseEnv),
storageEnv,
);
},
),
},
{
title: "Render manifests",
task: async (taskCtx, task) => {
taskCtx.serviceEnv = mergeServiceEnv(
mergeServiceEnv(taskCtx.serviceEnv, taskCtx.databaseEnv ?? {}),
taskCtx.storageEnv ?? {},
);
taskCtx.resources = await composeToKubernetes(
project,
taskCtx.compose!,
@@ -1085,7 +1105,7 @@ export async function runUp(
renderer: "default",
// Only redraw when a task changes; a queued build should not generate
// another frame on each spinner tick. Keep the renderer's task tree.
rendererOptions: { collapseErrors: false, collapseSubtasks: false, lazy: true },
rendererOptions: { collapseErrors: false, collapseSubtasks: false, showErrorMessage: false, lazy: true },
},
).run();