feat: add non-interactive update option
This commit is contained in:
@@ -447,6 +447,7 @@ describe("update-cli", () => {
|
|||||||
it("requires confirmation on downgrade when non-interactive", async () => {
|
it("requires confirmation on downgrade when non-interactive", async () => {
|
||||||
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-update-"));
|
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-update-"));
|
||||||
try {
|
try {
|
||||||
|
setTty(false);
|
||||||
await fs.writeFile(
|
await fs.writeFile(
|
||||||
path.join(tempDir, "package.json"),
|
path.join(tempDir, "package.json"),
|
||||||
JSON.stringify({ name: "clawdbot", version: "2.0.0" }),
|
JSON.stringify({ name: "clawdbot", version: "2.0.0" }),
|
||||||
@@ -483,4 +484,45 @@ describe("update-cli", () => {
|
|||||||
await fs.rm(tempDir, { recursive: true, force: true });
|
await fs.rm(tempDir, { recursive: true, force: true });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("allows downgrade with --yes in non-interactive mode", async () => {
|
||||||
|
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-update-"));
|
||||||
|
try {
|
||||||
|
setTty(false);
|
||||||
|
await fs.writeFile(
|
||||||
|
path.join(tempDir, "package.json"),
|
||||||
|
JSON.stringify({ name: "clawdbot", version: "2.0.0" }),
|
||||||
|
"utf-8",
|
||||||
|
);
|
||||||
|
|
||||||
|
const { resolveClawdbotPackageRoot } = await import("../infra/clawdbot-root.js");
|
||||||
|
const { resolveNpmChannelTag } = await import("../infra/update-check.js");
|
||||||
|
const { runGatewayUpdate } = await import("../infra/update-runner.js");
|
||||||
|
const { defaultRuntime } = await import("../runtime.js");
|
||||||
|
const { updateCommand } = await import("./update-cli.js");
|
||||||
|
|
||||||
|
vi.mocked(resolveClawdbotPackageRoot).mockResolvedValue(tempDir);
|
||||||
|
vi.mocked(resolveNpmChannelTag).mockResolvedValue({
|
||||||
|
tag: "latest",
|
||||||
|
version: "0.0.1",
|
||||||
|
});
|
||||||
|
vi.mocked(runGatewayUpdate).mockResolvedValue({
|
||||||
|
status: "ok",
|
||||||
|
mode: "npm",
|
||||||
|
steps: [],
|
||||||
|
durationMs: 100,
|
||||||
|
});
|
||||||
|
vi.mocked(defaultRuntime.error).mockClear();
|
||||||
|
vi.mocked(defaultRuntime.exit).mockClear();
|
||||||
|
|
||||||
|
await updateCommand({ yes: true });
|
||||||
|
|
||||||
|
expect(defaultRuntime.error).not.toHaveBeenCalledWith(
|
||||||
|
expect.stringContaining("Downgrade confirmation required."),
|
||||||
|
);
|
||||||
|
expect(runGatewayUpdate).toHaveBeenCalled();
|
||||||
|
} finally {
|
||||||
|
await fs.rm(tempDir, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ export type UpdateCommandOptions = {
|
|||||||
channel?: string;
|
channel?: string;
|
||||||
tag?: string;
|
tag?: string;
|
||||||
timeout?: string;
|
timeout?: string;
|
||||||
|
yes?: boolean;
|
||||||
};
|
};
|
||||||
export type UpdateStatusOptions = {
|
export type UpdateStatusOptions = {
|
||||||
json?: boolean;
|
json?: boolean;
|
||||||
@@ -427,7 +428,7 @@ export async function updateCommand(opts: UpdateCommandOptions): Promise<void> {
|
|||||||
const needsConfirm =
|
const needsConfirm =
|
||||||
currentVersion != null && (targetVersion == null || (cmp != null && cmp > 0));
|
currentVersion != null && (targetVersion == null || (cmp != null && cmp > 0));
|
||||||
|
|
||||||
if (needsConfirm) {
|
if (needsConfirm && !opts.yes) {
|
||||||
if (!process.stdin.isTTY || opts.json) {
|
if (!process.stdin.isTTY || opts.json) {
|
||||||
defaultRuntime.error(
|
defaultRuntime.error(
|
||||||
[
|
[
|
||||||
@@ -667,10 +668,15 @@ export function registerUpdateCli(program: Command) {
|
|||||||
.option("--channel <stable|beta|dev>", "Persist update channel (git + npm)")
|
.option("--channel <stable|beta|dev>", "Persist update channel (git + npm)")
|
||||||
.option("--tag <dist-tag|version>", "Override npm dist-tag or version for this update")
|
.option("--tag <dist-tag|version>", "Override npm dist-tag or version for this update")
|
||||||
.option("--timeout <seconds>", "Timeout for each update step in seconds (default: 1200)")
|
.option("--timeout <seconds>", "Timeout for each update step in seconds (default: 1200)")
|
||||||
|
.option("--yes", "Skip confirmation prompts (non-interactive)", false)
|
||||||
.addHelpText(
|
.addHelpText(
|
||||||
"after",
|
"after",
|
||||||
() =>
|
() =>
|
||||||
`
|
`
|
||||||
|
What this does:
|
||||||
|
- Git checkouts: fetches, rebases, installs deps, builds, and runs doctor
|
||||||
|
- npm installs: updates via detected package manager
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
clawdbot update # Update a source checkout (git)
|
clawdbot update # Update a source checkout (git)
|
||||||
clawdbot update --channel beta # Switch to beta channel (git + npm)
|
clawdbot update --channel beta # Switch to beta channel (git + npm)
|
||||||
@@ -678,10 +684,11 @@ Examples:
|
|||||||
clawdbot update --tag beta # One-off update to a dist-tag or version
|
clawdbot update --tag beta # One-off update to a dist-tag or version
|
||||||
clawdbot update --restart # Update and restart the daemon
|
clawdbot update --restart # Update and restart the daemon
|
||||||
clawdbot update --json # Output result as JSON
|
clawdbot update --json # Output result as JSON
|
||||||
|
clawdbot update --yes # Non-interactive (accept downgrade prompts)
|
||||||
clawdbot --update # Shorthand for clawdbot update
|
clawdbot --update # Shorthand for clawdbot update
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
- For git installs: fetches, rebases, installs deps, builds, and runs doctor
|
- Switch channels with --channel stable|beta|dev
|
||||||
- For global installs: auto-updates via detected package manager when possible (see docs/install/updating.md)
|
- For global installs: auto-updates via detected package manager when possible (see docs/install/updating.md)
|
||||||
- Downgrades require confirmation (can break configuration)
|
- Downgrades require confirmation (can break configuration)
|
||||||
- Skips update if the working directory has uncommitted changes
|
- Skips update if the working directory has uncommitted changes
|
||||||
@@ -696,6 +703,7 @@ ${theme.muted("Docs:")} ${formatDocsLink("/cli/update", "docs.clawd.bot/cli/upda
|
|||||||
channel: opts.channel as string | undefined,
|
channel: opts.channel as string | undefined,
|
||||||
tag: opts.tag as string | undefined,
|
tag: opts.tag as string | undefined,
|
||||||
timeout: opts.timeout as string | undefined,
|
timeout: opts.timeout as string | undefined,
|
||||||
|
yes: Boolean(opts.yes),
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
defaultRuntime.error(String(err));
|
defaultRuntime.error(String(err));
|
||||||
|
|||||||
Reference in New Issue
Block a user