feat: release 2.7.0-rc2

This commit is contained in:
2026-10-06 15:31:51 +00:00 Unverified
parent cd9fb512cd
commit c8de9ddcba
42 changed files with 6127 additions and 593 deletions
+71 -14
View File
@@ -625,7 +625,12 @@ export async function reconcileResources(
request: ApiRequester = apiRequest,
resumeOptions: OperationResumeOptions = {},
): Promise<ResourcePlan> {
const plan = await planResources(project, resources, request, resumeOptions.signal);
const plan = await planResources(
project,
resources,
request,
resumeOptions.signal,
);
await applyResourcePlan(
project,
plan,
@@ -792,6 +797,30 @@ export async function runUp(
new Promise((resolve) => pending.set(name, resolve)),
]),
);
let settleUpload!: (error?: unknown) => void;
const uploaded = new Promise<unknown>((resolve) => {
settleUpload = resolve;
});
let uploadChild: ListrTaskWrapper<UpContext, any, any> | undefined;
let uploadedBytes = 0;
let totalBytes = 0;
const formatBytes = (bytes: number): string => {
if (bytes < 1024) return `${bytes} B`;
const unit = bytes < 1024 ** 2 ? "KiB" : "MiB";
const divisor = unit === "KiB" ? 1024 : 1024 ** 2;
return `${Number((bytes / divisor).toFixed(1))} ${unit}`;
};
const uploadTitle = (
bytes: number,
total: number,
finished = false,
): string =>
`Uploading context: ${formatBytes(bytes)}/${formatBytes(total)} (${finished ? 100 : total === 0 ? 0 : Math.min(99, Math.floor((100 * bytes) / total))}%)`;
const finishUpload = (error?: unknown) => {
if (!error && uploadChild)
uploadChild.title = uploadTitle(uploadedBytes, totalBytes, true);
settleUpload(error);
};
let result: Awaited<ReturnType<typeof buildServices>> | undefined;
let operationFailure: Error | undefined;
let operation: Promise<void> | undefined;
@@ -801,19 +830,27 @@ export async function runUp(
taskCtx.compose!,
cwd,
{
progress: (message) => {
if (
!/^Build (?:queued|creating|starting|running|done)\b/.test(message) &&
task.output !== message
) task.output = message;
upload: (bytes, total) => {
uploadedBytes = bytes;
totalBytes = total;
if (uploadChild)
uploadChild.title = uploadTitle(bytes, total);
},
uploadSettled: finishUpload,
service: (name) => {
let stream: ReturnType<ListrTaskWrapper<UpContext, any, any>["stdout"]> | undefined;
let stream:
| ReturnType<
ListrTaskWrapper<UpContext, any, any>["stdout"]
>
| undefined;
return {
progress: (message) => {
const child = children.get(name);
if (!child) return;
const phase = /^Build (queued|creating|starting|running|done)\b/.exec(message)?.[1];
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.
},
@@ -845,6 +882,7 @@ export async function runUp(
error instanceof Error ? error : new Error(String(error));
operationFailure = failure;
task.output = failure.stack ?? failure.message;
finishUpload(failure);
for (const resolve of pending.values()) resolve(failure);
pending.clear();
},
@@ -853,6 +891,19 @@ export async function runUp(
};
return task.newListr<UpContext>(
[
{
title: uploadTitle(0, 0),
exitOnError: false,
task: async (_ctx, child) => {
uploadChild = child;
if (children.size === services.length) start();
const error = await uploaded;
if (error) {
child.title = "Uploading context: failed";
throw error;
}
},
},
...services.map((name) => ({
title: `Build ${name}`,
rendererOptions: { outputBar: 10, persistentOutput: true },
@@ -863,7 +914,7 @@ export async function runUp(
) => {
children.set(name, child);
child.title = `Build ${name}: queued`;
if (children.size === services.length) start();
if (children.size === services.length && uploadChild) start();
const error = await completed.get(name);
if (error) {
child.title = `Build ${name}: failed`;
@@ -948,7 +999,8 @@ export async function runUp(
{ method: "POST", json: { compose: taskCtx.compose } },
);
} catch (error) {
task.output = error instanceof Error ? error.message : String(error);
task.output =
error instanceof Error ? error.message : String(error);
throw error;
}
taskCtx.databaseEnv = operationEnvironment(response);
@@ -957,8 +1009,7 @@ export async function runUp(
},
{
title: "Reconcile S3 storage",
enabled: () =>
getComposeS3Claims(taskCtx.compose!).length > 0,
enabled: () => getComposeS3Claims(taskCtx.compose!).length > 0,
task: async (_ctx, child) => {
let response: Record<string, unknown>;
try {
@@ -969,7 +1020,8 @@ export async function runUp(
{ method: "POST", json: { compose: taskCtx.compose } },
);
} catch (error) {
task.output = error instanceof Error ? error.message : String(error);
task.output =
error instanceof Error ? error.message : String(error);
throw error;
}
taskCtx.storageEnv = operationEnvironment(response);
@@ -1110,7 +1162,12 @@ 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, showErrorMessage: false, lazy: true },
rendererOptions: {
collapseErrors: false,
collapseSubtasks: false,
showErrorMessage: false,
lazy: true,
},
},
).run();