fix(update): harden root selection
This commit is contained in:
30
src/cli/run-main.test.ts
Normal file
30
src/cli/run-main.test.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { rewriteUpdateFlagArgv } from "./run-main.js";
|
||||
|
||||
describe("rewriteUpdateFlagArgv", () => {
|
||||
it("leaves argv unchanged when --update is absent", () => {
|
||||
const argv = ["node", "entry.js", "status"];
|
||||
expect(rewriteUpdateFlagArgv(argv)).toBe(argv);
|
||||
});
|
||||
|
||||
it("rewrites --update into the update command", () => {
|
||||
expect(rewriteUpdateFlagArgv(["node", "entry.js", "--update"])).toEqual([
|
||||
"node",
|
||||
"entry.js",
|
||||
"update",
|
||||
]);
|
||||
});
|
||||
|
||||
it("preserves global flags that appear before --update", () => {
|
||||
expect(
|
||||
rewriteUpdateFlagArgv(["node", "entry.js", "--profile", "p", "--update"]),
|
||||
).toEqual(["node", "entry.js", "--profile", "p", "update"]);
|
||||
});
|
||||
|
||||
it("keeps update options after the rewritten command", () => {
|
||||
expect(
|
||||
rewriteUpdateFlagArgv(["node", "entry.js", "--update", "--json"]),
|
||||
).toEqual(["node", "entry.js", "update", "--json"]);
|
||||
});
|
||||
});
|
||||
@@ -8,7 +8,15 @@ import { ensureClawdbotCliOnPath } from "../infra/path-env.js";
|
||||
import { assertSupportedRuntime } from "../infra/runtime-guard.js";
|
||||
import { installUnhandledRejectionHandler } from "../infra/unhandled-rejections.js";
|
||||
import { enableConsoleCapture } from "../logging.js";
|
||||
import { updateCommand } from "./update-cli.js";
|
||||
|
||||
export function rewriteUpdateFlagArgv(argv: string[]): string[] {
|
||||
const index = argv.indexOf("--update");
|
||||
if (index === -1) return argv;
|
||||
|
||||
const next = [...argv];
|
||||
next.splice(index, 1, "update");
|
||||
return next;
|
||||
}
|
||||
|
||||
export async function runCli(argv: string[] = process.argv) {
|
||||
loadDotEnv({ quiet: true });
|
||||
@@ -21,12 +29,6 @@ export async function runCli(argv: string[] = process.argv) {
|
||||
// Enforce the minimum supported runtime before doing any work.
|
||||
assertSupportedRuntime();
|
||||
|
||||
// Handle --update flag before full program parsing
|
||||
if (argv.includes("--update")) {
|
||||
await updateCommand({});
|
||||
return;
|
||||
}
|
||||
|
||||
const { buildProgram } = await import("./program.js");
|
||||
const program = buildProgram();
|
||||
|
||||
@@ -42,7 +44,7 @@ export async function runCli(argv: string[] = process.argv) {
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
await program.parseAsync(argv);
|
||||
await program.parseAsync(rewriteUpdateFlagArgv(argv));
|
||||
}
|
||||
|
||||
export function isCliMainModule(): boolean {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { Command } from "commander";
|
||||
|
||||
import { resolveClawdbotPackageRoot } from "../infra/clawdbot-root.js";
|
||||
import {
|
||||
runGatewayUpdate,
|
||||
type UpdateRunResult,
|
||||
@@ -103,8 +104,15 @@ export async function updateCommand(opts: UpdateCommandOptions): Promise<void> {
|
||||
defaultRuntime.log("");
|
||||
}
|
||||
|
||||
const root =
|
||||
(await resolveClawdbotPackageRoot({
|
||||
moduleUrl: import.meta.url,
|
||||
argv1: process.argv[1],
|
||||
cwd: process.cwd(),
|
||||
})) ?? process.cwd();
|
||||
|
||||
const result = await runGatewayUpdate({
|
||||
cwd: process.cwd(),
|
||||
cwd: root,
|
||||
argv1: process.argv[1],
|
||||
timeoutMs,
|
||||
});
|
||||
@@ -124,6 +132,18 @@ export async function updateCommand(opts: UpdateCommandOptions): Promise<void> {
|
||||
),
|
||||
);
|
||||
}
|
||||
if (result.reason === "not-git-install") {
|
||||
defaultRuntime.log(
|
||||
theme.warn(
|
||||
"Skipped: this Clawdbot install isn't a git checkout. Update via your package manager, then run `clawdbot doctor` and `clawdbot daemon restart`.",
|
||||
),
|
||||
);
|
||||
defaultRuntime.log(
|
||||
theme.muted(
|
||||
"Examples: `npm i -g clawdbot@latest` or `pnpm add -g clawdbot@latest`",
|
||||
),
|
||||
);
|
||||
}
|
||||
defaultRuntime.exit(0);
|
||||
return;
|
||||
}
|
||||
@@ -141,9 +161,7 @@ export async function updateCommand(opts: UpdateCommandOptions): Promise<void> {
|
||||
}
|
||||
} catch (err) {
|
||||
if (!opts.json) {
|
||||
defaultRuntime.log(
|
||||
theme.warn(`Daemon restart failed: ${String(err)}`),
|
||||
);
|
||||
defaultRuntime.log(theme.warn(`Daemon restart failed: ${String(err)}`));
|
||||
defaultRuntime.log(
|
||||
theme.muted(
|
||||
"You may need to restart the daemon manually: clawdbot daemon restart",
|
||||
@@ -179,14 +197,14 @@ export function registerUpdateCli(program: Command) {
|
||||
"after",
|
||||
`
|
||||
Examples:
|
||||
clawdbot update # Update from git or package manager
|
||||
clawdbot update # Update a source checkout (git)
|
||||
clawdbot update --restart # Update and restart the daemon
|
||||
clawdbot update --json # Output result as JSON
|
||||
clawdbot --update # Shorthand for clawdbot update
|
||||
|
||||
Notes:
|
||||
- For git installs: fetches, rebases, installs deps, builds, and runs doctor
|
||||
- For npm installs: runs package manager update command
|
||||
- For npm installs: use npm/pnpm to reinstall (see docs/install/updating.md)
|
||||
- Skips update if the working directory has uncommitted changes
|
||||
`,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user