Merge pull request #701 from bjesuiter/fix/update-progress-logs
feat(update): add progress spinner during update steps
This commit is contained in:
@@ -7,6 +7,7 @@
|
|||||||
- CLI/Status: improve Tailscale reporting in `status --all` and harden parsing of noisy `tailscale status --json` output.
|
- CLI/Status: improve Tailscale reporting in `status --all` and harden parsing of noisy `tailscale status --json` output.
|
||||||
- CLI/Status: make `status --all` scan progress determinate (OSC progress + spinner).
|
- CLI/Status: make `status --all` scan progress determinate (OSC progress + spinner).
|
||||||
- Terminal/Table: ANSI-safe wrapping to prevent table clipping/color loss; add regression coverage.
|
- Terminal/Table: ANSI-safe wrapping to prevent table clipping/color loss; add regression coverage.
|
||||||
|
- CLI/Update: gate progress spinner on stdout TTY and align clean-check step label. (#701) — thanks @bjesuiter.
|
||||||
|
|
||||||
## 2026.1.11-4
|
## 2026.1.11-4
|
||||||
|
|
||||||
|
|||||||
@@ -90,7 +90,11 @@ export function createCliProgress(options: ProgressOptions): ProgressReporter {
|
|||||||
applyState();
|
applyState();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (delayMs === 0) {
|
||||||
|
start();
|
||||||
|
} else {
|
||||||
timer = setTimeout(start, delayMs);
|
timer = setTimeout(start, delayMs);
|
||||||
|
}
|
||||||
|
|
||||||
const setLabel = (next: string) => {
|
const setLabel = (next: string) => {
|
||||||
label = next;
|
label = next;
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
|
import { spinner } from "@clack/prompts";
|
||||||
import type { Command } from "commander";
|
import type { Command } from "commander";
|
||||||
|
|
||||||
import { resolveClawdbotPackageRoot } from "../infra/clawdbot-root.js";
|
import { resolveClawdbotPackageRoot } from "../infra/clawdbot-root.js";
|
||||||
import {
|
import {
|
||||||
runGatewayUpdate,
|
runGatewayUpdate,
|
||||||
type UpdateRunResult,
|
type UpdateRunResult,
|
||||||
|
type UpdateStepInfo,
|
||||||
|
type UpdateStepProgress,
|
||||||
} from "../infra/update-runner.js";
|
} from "../infra/update-runner.js";
|
||||||
import { defaultRuntime } from "../runtime.js";
|
import { defaultRuntime } from "../runtime.js";
|
||||||
import { formatDocsLink } from "../terminal/links.js";
|
import { formatDocsLink } from "../terminal/links.js";
|
||||||
@@ -15,6 +18,75 @@ export type UpdateCommandOptions = {
|
|||||||
timeout?: string;
|
timeout?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const STEP_LABELS: Record<string, string> = {
|
||||||
|
"clean check": "Working directory is clean",
|
||||||
|
"upstream check": "Upstream branch exists",
|
||||||
|
"git fetch": "Fetching latest changes",
|
||||||
|
"git rebase": "Rebasing onto upstream",
|
||||||
|
"deps install": "Installing dependencies",
|
||||||
|
build: "Building",
|
||||||
|
"ui:build": "Building UI",
|
||||||
|
"clawdbot doctor": "Running doctor checks",
|
||||||
|
"git rev-parse HEAD (after)": "Verifying update",
|
||||||
|
};
|
||||||
|
|
||||||
|
function getStepLabel(step: UpdateStepInfo): string {
|
||||||
|
return STEP_LABELS[step.name] ?? step.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProgressController = {
|
||||||
|
progress: UpdateStepProgress;
|
||||||
|
stop: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
function createUpdateProgress(enabled: boolean): ProgressController {
|
||||||
|
if (!enabled) {
|
||||||
|
return {
|
||||||
|
progress: {},
|
||||||
|
stop: () => {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let currentSpinner: ReturnType<typeof spinner> | null = null;
|
||||||
|
|
||||||
|
const progress: UpdateStepProgress = {
|
||||||
|
onStepStart: (step) => {
|
||||||
|
currentSpinner = spinner();
|
||||||
|
currentSpinner.start(theme.accent(getStepLabel(step)));
|
||||||
|
},
|
||||||
|
onStepComplete: (step) => {
|
||||||
|
if (!currentSpinner) return;
|
||||||
|
|
||||||
|
const label = getStepLabel(step);
|
||||||
|
const duration = theme.muted(`(${formatDuration(step.durationMs)})`);
|
||||||
|
const icon =
|
||||||
|
step.exitCode === 0 ? theme.success("\u2713") : theme.error("\u2717");
|
||||||
|
|
||||||
|
currentSpinner.stop(`${icon} ${label} ${duration}`);
|
||||||
|
currentSpinner = null;
|
||||||
|
|
||||||
|
if (step.exitCode !== 0 && step.stderrTail) {
|
||||||
|
const lines = step.stderrTail.split("\n").slice(-10);
|
||||||
|
for (const line of lines) {
|
||||||
|
if (line.trim()) {
|
||||||
|
defaultRuntime.log(` ${theme.error(line)}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
progress,
|
||||||
|
stop: () => {
|
||||||
|
if (currentSpinner) {
|
||||||
|
currentSpinner.stop();
|
||||||
|
currentSpinner = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function formatDuration(ms: number): string {
|
function formatDuration(ms: number): string {
|
||||||
if (ms < 1000) return `${ms}ms`;
|
if (ms < 1000) return `${ms}ms`;
|
||||||
const seconds = (ms / 1000).toFixed(1);
|
const seconds = (ms / 1000).toFixed(1);
|
||||||
@@ -27,7 +99,11 @@ function formatStepStatus(exitCode: number | null): string {
|
|||||||
return theme.error("\u2717");
|
return theme.error("\u2717");
|
||||||
}
|
}
|
||||||
|
|
||||||
function printResult(result: UpdateRunResult, opts: UpdateCommandOptions) {
|
type PrintResultOptions = UpdateCommandOptions & {
|
||||||
|
hideSteps?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
function printResult(result: UpdateRunResult, opts: PrintResultOptions) {
|
||||||
if (opts.json) {
|
if (opts.json) {
|
||||||
defaultRuntime.log(JSON.stringify(result, null, 2));
|
defaultRuntime.log(JSON.stringify(result, null, 2));
|
||||||
return;
|
return;
|
||||||
@@ -44,7 +120,6 @@ function printResult(result: UpdateRunResult, opts: UpdateCommandOptions) {
|
|||||||
defaultRuntime.log(
|
defaultRuntime.log(
|
||||||
`${theme.heading("Update Result:")} ${statusColor(result.status.toUpperCase())}`,
|
`${theme.heading("Update Result:")} ${statusColor(result.status.toUpperCase())}`,
|
||||||
);
|
);
|
||||||
defaultRuntime.log(` Mode: ${theme.muted(result.mode)}`);
|
|
||||||
if (result.root) {
|
if (result.root) {
|
||||||
defaultRuntime.log(` Root: ${theme.muted(result.root)}`);
|
defaultRuntime.log(` Root: ${theme.muted(result.root)}`);
|
||||||
}
|
}
|
||||||
@@ -62,7 +137,7 @@ function printResult(result: UpdateRunResult, opts: UpdateCommandOptions) {
|
|||||||
defaultRuntime.log(` After: ${theme.muted(after)}`);
|
defaultRuntime.log(` After: ${theme.muted(after)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.steps.length > 0) {
|
if (!opts.hideSteps && result.steps.length > 0) {
|
||||||
defaultRuntime.log("");
|
defaultRuntime.log("");
|
||||||
defaultRuntime.log(theme.heading("Steps:"));
|
defaultRuntime.log(theme.heading("Steps:"));
|
||||||
for (const step of result.steps) {
|
for (const step of result.steps) {
|
||||||
@@ -70,7 +145,6 @@ function printResult(result: UpdateRunResult, opts: UpdateCommandOptions) {
|
|||||||
const duration = theme.muted(`(${formatDuration(step.durationMs)})`);
|
const duration = theme.muted(`(${formatDuration(step.durationMs)})`);
|
||||||
defaultRuntime.log(` ${status} ${step.name} ${duration}`);
|
defaultRuntime.log(` ${status} ${step.name} ${duration}`);
|
||||||
|
|
||||||
// Show stderr for failed steps
|
|
||||||
if (step.exitCode !== 0 && step.stderrTail) {
|
if (step.exitCode !== 0 && step.stderrTail) {
|
||||||
const lines = step.stderrTail.split("\n").slice(0, 5);
|
const lines = step.stderrTail.split("\n").slice(0, 5);
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
@@ -99,6 +173,8 @@ export async function updateCommand(opts: UpdateCommandOptions): Promise<void> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const showProgress = !opts.json && process.stdout.isTTY;
|
||||||
|
|
||||||
if (!opts.json) {
|
if (!opts.json) {
|
||||||
defaultRuntime.log(theme.heading("Updating Clawdbot..."));
|
defaultRuntime.log(theme.heading("Updating Clawdbot..."));
|
||||||
defaultRuntime.log("");
|
defaultRuntime.log("");
|
||||||
@@ -111,13 +187,18 @@ export async function updateCommand(opts: UpdateCommandOptions): Promise<void> {
|
|||||||
cwd: process.cwd(),
|
cwd: process.cwd(),
|
||||||
})) ?? process.cwd();
|
})) ?? process.cwd();
|
||||||
|
|
||||||
|
const { progress, stop } = createUpdateProgress(showProgress);
|
||||||
|
|
||||||
const result = await runGatewayUpdate({
|
const result = await runGatewayUpdate({
|
||||||
cwd: root,
|
cwd: root,
|
||||||
argv1: process.argv[1],
|
argv1: process.argv[1],
|
||||||
timeoutMs,
|
timeoutMs,
|
||||||
|
progress,
|
||||||
});
|
});
|
||||||
|
|
||||||
printResult(result, opts);
|
stop();
|
||||||
|
|
||||||
|
printResult(result, { ...opts, hideSteps: showProgress });
|
||||||
|
|
||||||
if (result.status === "error") {
|
if (result.status === "error") {
|
||||||
defaultRuntime.exit(1);
|
defaultRuntime.exit(1);
|
||||||
|
|||||||
@@ -30,11 +30,30 @@ type CommandRunner = (
|
|||||||
options: CommandOptions,
|
options: CommandOptions,
|
||||||
) => Promise<{ stdout: string; stderr: string; code: number | null }>;
|
) => Promise<{ stdout: string; stderr: string; code: number | null }>;
|
||||||
|
|
||||||
|
export type UpdateStepInfo = {
|
||||||
|
name: string;
|
||||||
|
command: string;
|
||||||
|
index: number;
|
||||||
|
total: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type UpdateStepCompletion = UpdateStepInfo & {
|
||||||
|
durationMs: number;
|
||||||
|
exitCode: number | null;
|
||||||
|
stderrTail?: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type UpdateStepProgress = {
|
||||||
|
onStepStart?: (step: UpdateStepInfo) => void;
|
||||||
|
onStepComplete?: (step: UpdateStepCompletion) => void;
|
||||||
|
};
|
||||||
|
|
||||||
type UpdateRunnerOptions = {
|
type UpdateRunnerOptions = {
|
||||||
cwd?: string;
|
cwd?: string;
|
||||||
argv1?: string;
|
argv1?: string;
|
||||||
timeoutMs?: number;
|
timeoutMs?: number;
|
||||||
runCommand?: CommandRunner;
|
runCommand?: CommandRunner;
|
||||||
|
progress?: UpdateStepProgress;
|
||||||
};
|
};
|
||||||
|
|
||||||
const DEFAULT_TIMEOUT_MS = 20 * 60_000;
|
const DEFAULT_TIMEOUT_MS = 20 * 60_000;
|
||||||
@@ -142,20 +161,57 @@ async function detectPackageManager(root: string) {
|
|||||||
return "npm";
|
return "npm";
|
||||||
}
|
}
|
||||||
|
|
||||||
async function runStep(
|
type RunStepOptions = {
|
||||||
runCommand: CommandRunner,
|
runCommand: CommandRunner;
|
||||||
name: string,
|
name: string;
|
||||||
argv: string[],
|
argv: string[];
|
||||||
cwd: string,
|
cwd: string;
|
||||||
timeoutMs: number,
|
timeoutMs: number;
|
||||||
env?: NodeJS.ProcessEnv,
|
env?: NodeJS.ProcessEnv;
|
||||||
): Promise<UpdateStepResult> {
|
progress?: UpdateStepProgress;
|
||||||
|
stepIndex: number;
|
||||||
|
totalSteps: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
async function runStep(opts: RunStepOptions): Promise<UpdateStepResult> {
|
||||||
|
const {
|
||||||
|
runCommand,
|
||||||
|
name,
|
||||||
|
argv,
|
||||||
|
cwd,
|
||||||
|
timeoutMs,
|
||||||
|
env,
|
||||||
|
progress,
|
||||||
|
stepIndex,
|
||||||
|
totalSteps,
|
||||||
|
} = opts;
|
||||||
|
const command = argv.join(" ");
|
||||||
|
|
||||||
|
const stepInfo: UpdateStepInfo = {
|
||||||
|
name,
|
||||||
|
command,
|
||||||
|
index: stepIndex,
|
||||||
|
total: totalSteps,
|
||||||
|
};
|
||||||
|
|
||||||
|
progress?.onStepStart?.(stepInfo);
|
||||||
|
|
||||||
const started = Date.now();
|
const started = Date.now();
|
||||||
const result = await runCommand(argv, { cwd, timeoutMs, env });
|
const result = await runCommand(argv, { cwd, timeoutMs, env });
|
||||||
const durationMs = Date.now() - started;
|
const durationMs = Date.now() - started;
|
||||||
|
|
||||||
|
const stderrTail = trimLogTail(result.stderr, MAX_LOG_CHARS);
|
||||||
|
|
||||||
|
progress?.onStepComplete?.({
|
||||||
|
...stepInfo,
|
||||||
|
durationMs,
|
||||||
|
exitCode: result.code,
|
||||||
|
stderrTail,
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name,
|
name,
|
||||||
command: argv.join(" "),
|
command,
|
||||||
cwd,
|
cwd,
|
||||||
durationMs,
|
durationMs,
|
||||||
exitCode: result.code,
|
exitCode: result.code,
|
||||||
@@ -181,6 +237,9 @@ function managerInstallArgs(manager: "pnpm" | "bun" | "npm") {
|
|||||||
return ["npm", "install"];
|
return ["npm", "install"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Total number of visible steps in a successful git update flow
|
||||||
|
const GIT_UPDATE_TOTAL_STEPS = 9;
|
||||||
|
|
||||||
export async function runGatewayUpdate(
|
export async function runGatewayUpdate(
|
||||||
opts: UpdateRunnerOptions = {},
|
opts: UpdateRunnerOptions = {},
|
||||||
): Promise<UpdateRunResult> {
|
): Promise<UpdateRunResult> {
|
||||||
@@ -192,9 +251,33 @@ export async function runGatewayUpdate(
|
|||||||
return { stdout: res.stdout, stderr: res.stderr, code: res.code };
|
return { stdout: res.stdout, stderr: res.stderr, code: res.code };
|
||||||
});
|
});
|
||||||
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
||||||
|
const progress = opts.progress;
|
||||||
const steps: UpdateStepResult[] = [];
|
const steps: UpdateStepResult[] = [];
|
||||||
const candidates = buildStartDirs(opts);
|
const candidates = buildStartDirs(opts);
|
||||||
|
|
||||||
|
let stepIndex = 0;
|
||||||
|
|
||||||
|
const step = (
|
||||||
|
name: string,
|
||||||
|
argv: string[],
|
||||||
|
cwd: string,
|
||||||
|
env?: NodeJS.ProcessEnv,
|
||||||
|
): RunStepOptions => {
|
||||||
|
const currentIndex = stepIndex;
|
||||||
|
stepIndex += 1;
|
||||||
|
return {
|
||||||
|
runCommand,
|
||||||
|
name,
|
||||||
|
argv,
|
||||||
|
cwd,
|
||||||
|
timeoutMs,
|
||||||
|
env,
|
||||||
|
progress,
|
||||||
|
stepIndex: currentIndex,
|
||||||
|
totalSteps: GIT_UPDATE_TOTAL_STEPS,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const pkgRoot = await findPackageRoot(candidates);
|
const pkgRoot = await findPackageRoot(candidates);
|
||||||
|
|
||||||
let gitRoot = await resolveGitRoot(runCommand, candidates, timeoutMs);
|
let gitRoot = await resolveGitRoot(runCommand, candidates, timeoutMs);
|
||||||
@@ -214,40 +297,39 @@ export async function runGatewayUpdate(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (gitRoot && pkgRoot && path.resolve(gitRoot) === path.resolve(pkgRoot)) {
|
if (gitRoot && pkgRoot && path.resolve(gitRoot) === path.resolve(pkgRoot)) {
|
||||||
const beforeSha = (
|
// Get current SHA (not a visible step, no progress)
|
||||||
await runStep(
|
const beforeShaResult = await runCommand(
|
||||||
runCommand,
|
|
||||||
"git rev-parse HEAD",
|
|
||||||
["git", "-C", gitRoot, "rev-parse", "HEAD"],
|
["git", "-C", gitRoot, "rev-parse", "HEAD"],
|
||||||
gitRoot,
|
{ cwd: gitRoot, timeoutMs },
|
||||||
timeoutMs,
|
);
|
||||||
)
|
const beforeSha = beforeShaResult.stdout.trim() || null;
|
||||||
).stdoutTail?.trim();
|
|
||||||
const beforeVersion = await readPackageVersion(gitRoot);
|
const beforeVersion = await readPackageVersion(gitRoot);
|
||||||
|
|
||||||
const statusStep = await runStep(
|
const statusCheck = await runStep(
|
||||||
runCommand,
|
step(
|
||||||
"git status",
|
"clean check",
|
||||||
["git", "-C", gitRoot, "status", "--porcelain"],
|
["git", "-C", gitRoot, "status", "--porcelain"],
|
||||||
gitRoot,
|
gitRoot,
|
||||||
timeoutMs,
|
),
|
||||||
);
|
);
|
||||||
steps.push(statusStep);
|
steps.push(statusCheck);
|
||||||
if ((statusStep.stdoutTail ?? "").trim()) {
|
const hasUncommittedChanges =
|
||||||
|
statusCheck.stdoutTail && statusCheck.stdoutTail.trim().length > 0;
|
||||||
|
if (hasUncommittedChanges) {
|
||||||
return {
|
return {
|
||||||
status: "skipped",
|
status: "skipped",
|
||||||
mode: "git",
|
mode: "git",
|
||||||
root: gitRoot,
|
root: gitRoot,
|
||||||
reason: "dirty",
|
reason: "dirty",
|
||||||
before: { sha: beforeSha ?? null, version: beforeVersion },
|
before: { sha: beforeSha, version: beforeVersion },
|
||||||
steps,
|
steps,
|
||||||
durationMs: Date.now() - startedAt,
|
durationMs: Date.now() - startedAt,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const upstreamStep = await runStep(
|
const upstreamStep = await runStep(
|
||||||
runCommand,
|
step(
|
||||||
"git upstream",
|
"upstream check",
|
||||||
[
|
[
|
||||||
"git",
|
"git",
|
||||||
"-C",
|
"-C",
|
||||||
@@ -258,7 +340,7 @@ export async function runGatewayUpdate(
|
|||||||
"@{upstream}",
|
"@{upstream}",
|
||||||
],
|
],
|
||||||
gitRoot,
|
gitRoot,
|
||||||
timeoutMs,
|
),
|
||||||
);
|
);
|
||||||
steps.push(upstreamStep);
|
steps.push(upstreamStep);
|
||||||
if (upstreamStep.exitCode !== 0) {
|
if (upstreamStep.exitCode !== 0) {
|
||||||
@@ -267,97 +349,88 @@ export async function runGatewayUpdate(
|
|||||||
mode: "git",
|
mode: "git",
|
||||||
root: gitRoot,
|
root: gitRoot,
|
||||||
reason: "no-upstream",
|
reason: "no-upstream",
|
||||||
before: { sha: beforeSha ?? null, version: beforeVersion },
|
before: { sha: beforeSha, version: beforeVersion },
|
||||||
steps,
|
steps,
|
||||||
durationMs: Date.now() - startedAt,
|
durationMs: Date.now() - startedAt,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
steps.push(
|
const fetchStep = await runStep(
|
||||||
await runStep(
|
step(
|
||||||
runCommand,
|
|
||||||
"git fetch",
|
"git fetch",
|
||||||
["git", "-C", gitRoot, "fetch", "--all", "--prune"],
|
["git", "-C", gitRoot, "fetch", "--all", "--prune"],
|
||||||
gitRoot,
|
gitRoot,
|
||||||
timeoutMs,
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
steps.push(fetchStep);
|
||||||
|
|
||||||
const rebaseStep = await runStep(
|
const rebaseStep = await runStep(
|
||||||
runCommand,
|
step(
|
||||||
"git rebase",
|
"git rebase",
|
||||||
["git", "-C", gitRoot, "rebase", "@{upstream}"],
|
["git", "-C", gitRoot, "rebase", "@{upstream}"],
|
||||||
gitRoot,
|
gitRoot,
|
||||||
timeoutMs,
|
),
|
||||||
);
|
);
|
||||||
steps.push(rebaseStep);
|
steps.push(rebaseStep);
|
||||||
if (rebaseStep.exitCode !== 0) {
|
if (rebaseStep.exitCode !== 0) {
|
||||||
steps.push(
|
const abortResult = await runCommand(
|
||||||
await runStep(
|
|
||||||
runCommand,
|
|
||||||
"git rebase --abort",
|
|
||||||
["git", "-C", gitRoot, "rebase", "--abort"],
|
["git", "-C", gitRoot, "rebase", "--abort"],
|
||||||
gitRoot,
|
{ cwd: gitRoot, timeoutMs },
|
||||||
timeoutMs,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
steps.push({
|
||||||
|
name: "git rebase --abort",
|
||||||
|
command: "git rebase --abort",
|
||||||
|
cwd: gitRoot,
|
||||||
|
durationMs: 0,
|
||||||
|
exitCode: abortResult.code,
|
||||||
|
stdoutTail: trimLogTail(abortResult.stdout, MAX_LOG_CHARS),
|
||||||
|
stderrTail: trimLogTail(abortResult.stderr, MAX_LOG_CHARS),
|
||||||
|
});
|
||||||
return {
|
return {
|
||||||
status: "error",
|
status: "error",
|
||||||
mode: "git",
|
mode: "git",
|
||||||
root: gitRoot,
|
root: gitRoot,
|
||||||
reason: "rebase-failed",
|
reason: "rebase-failed",
|
||||||
before: { sha: beforeSha ?? null, version: beforeVersion },
|
before: { sha: beforeSha, version: beforeVersion },
|
||||||
steps,
|
steps,
|
||||||
durationMs: Date.now() - startedAt,
|
durationMs: Date.now() - startedAt,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const manager = await detectPackageManager(gitRoot);
|
const manager = await detectPackageManager(gitRoot);
|
||||||
steps.push(
|
|
||||||
await runStep(
|
const depsStep = await runStep(
|
||||||
runCommand,
|
step("deps install", managerInstallArgs(manager), gitRoot),
|
||||||
"deps install",
|
|
||||||
managerInstallArgs(manager),
|
|
||||||
gitRoot,
|
|
||||||
timeoutMs,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
steps.push(
|
steps.push(depsStep);
|
||||||
await runStep(
|
|
||||||
runCommand,
|
const buildStep = await runStep(
|
||||||
"build",
|
step("build", managerScriptArgs(manager, "build"), gitRoot),
|
||||||
managerScriptArgs(manager, "build"),
|
|
||||||
gitRoot,
|
|
||||||
timeoutMs,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
steps.push(
|
steps.push(buildStep);
|
||||||
await runStep(
|
|
||||||
runCommand,
|
const uiBuildStep = await runStep(
|
||||||
"ui:build",
|
step("ui:build", managerScriptArgs(manager, "ui:build"), gitRoot),
|
||||||
managerScriptArgs(manager, "ui:build"),
|
|
||||||
gitRoot,
|
|
||||||
timeoutMs,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
steps.push(
|
steps.push(uiBuildStep);
|
||||||
await runStep(
|
|
||||||
runCommand,
|
const doctorStep = await runStep(
|
||||||
|
step(
|
||||||
"clawdbot doctor",
|
"clawdbot doctor",
|
||||||
managerScriptArgs(manager, "clawdbot", ["doctor"]),
|
managerScriptArgs(manager, "clawdbot", ["doctor"]),
|
||||||
gitRoot,
|
gitRoot,
|
||||||
timeoutMs,
|
|
||||||
{ CLAWDBOT_UPDATE_IN_PROGRESS: "1" },
|
{ CLAWDBOT_UPDATE_IN_PROGRESS: "1" },
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
steps.push(doctorStep);
|
||||||
|
|
||||||
const failedStep = steps.find((step) => step.exitCode !== 0);
|
const failedStep = steps.find((s) => s.exitCode !== 0);
|
||||||
const afterShaStep = await runStep(
|
const afterShaStep = await runStep(
|
||||||
runCommand,
|
step(
|
||||||
"git rev-parse HEAD (after)",
|
"git rev-parse HEAD (after)",
|
||||||
["git", "-C", gitRoot, "rev-parse", "HEAD"],
|
["git", "-C", gitRoot, "rev-parse", "HEAD"],
|
||||||
gitRoot,
|
gitRoot,
|
||||||
timeoutMs,
|
),
|
||||||
);
|
);
|
||||||
steps.push(afterShaStep);
|
steps.push(afterShaStep);
|
||||||
const afterVersion = await readPackageVersion(gitRoot);
|
const afterVersion = await readPackageVersion(gitRoot);
|
||||||
@@ -367,7 +440,7 @@ export async function runGatewayUpdate(
|
|||||||
mode: "git",
|
mode: "git",
|
||||||
root: gitRoot,
|
root: gitRoot,
|
||||||
reason: failedStep ? failedStep.name : undefined,
|
reason: failedStep ? failedStep.name : undefined,
|
||||||
before: { sha: beforeSha ?? null, version: beforeVersion },
|
before: { sha: beforeSha, version: beforeVersion },
|
||||||
after: {
|
after: {
|
||||||
sha: afterShaStep.stdoutTail?.trim() ?? null,
|
sha: afterShaStep.stdoutTail?.trim() ?? null,
|
||||||
version: afterVersion,
|
version: afterVersion,
|
||||||
|
|||||||
Reference in New Issue
Block a user