fix: clarify dirty dev update error

This commit is contained in:
Peter Steinberger
2026-04-06 00:57:40 +01:00
parent 379bc1c032
commit c4cc557604
3 changed files with 41 additions and 8 deletions

View File

@@ -120,6 +120,7 @@ Docs: https://docs.openclaw.ai
- Discord/image generation: include the real generated `MEDIA:` paths in tool output and avoid duplicate plain-output media requeueing so Discord image replies stop pointing at missing local files.
- Slack: route live DM replies back to the concrete inbound DM channel while keeping persisted routing metadata user-scoped, so normal assistant replies stop disappearing when pairing and system messages still arrive. (#59030) Thanks @afurm.
- Discord/reply tags: strip leaked `[[reply_to_current]]` control tags from preview text and honor explicit reply-tag threading during final delivery, so Discord replies stay attached to the triggering message instead of printing reply metadata into chat.
- CLI/update: block `openclaw update --channel dev` with a clearer explainer when the git checkout has edited local files, instead of failing later once commit-switching work starts.
- Telegram: fix current-model checks in the model picker, HTML-format non-default `/model` confirmations, explicit topic replies, persisted reaction ownership across restarts, caption-media placeholder and `file_id` preservation on download failure, and upgraded-install inbound image reads. (#60384, #60042, #59634, #59207, #59948, #59971) Thanks @sfuminya, @GitZhangChi, @dashhuang, @samzong, @v1p0r, and @neeravmakwana.
- Telegram: restore DM voice-note preflight transcription so direct-message audio stops arriving as raw `<media:audio>` placeholders. (#61008) Thanks @manueltarouca.
- Telegram/reasoning: only create a Telegram reasoning preview lane when the session is explicitly `reasoning:stream`, so hidden `<think>` traces from streamed replies stop surfacing as chat previews on normal sessions. Thanks @vincentkoc.

View File

@@ -127,13 +127,17 @@ vi.mock("../process/exec.js", () => ({
runCommandWithTimeout: vi.fn(),
}));
vi.mock("../utils.js", () => ({
displayString: (input: string) => input,
isRecord: (value: unknown) =>
typeof value === "object" && value !== null && !Array.isArray(value),
pathExists: (...args: unknown[]) => pathExists(...args),
resolveConfigDir: () => "/tmp/openclaw-config",
}));
vi.mock("../utils.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../utils.js")>();
return {
...actual,
displayString: (input: string) => input,
isRecord: (value: unknown) =>
typeof value === "object" && value !== null && !Array.isArray(value),
pathExists: (...args: unknown[]) => pathExists(...args),
resolveConfigDir: () => "/tmp/openclaw-config",
};
});
vi.mock("../plugins/update.js", () => ({
syncPluginsForUpdateChannel: (...args: unknown[]) => syncPluginsForUpdateChannel(...args),
@@ -1034,7 +1038,31 @@ describe("update-cli", () => {
"Skipped plugin update sync in the pre-update CLI process after switching to a git install.",
);
});
it("explains why git updates cannot run with edited files", async () => {
vi.mocked(defaultRuntime.log).mockClear();
vi.mocked(defaultRuntime.error).mockClear();
vi.mocked(defaultRuntime.exit).mockClear();
vi.mocked(runGatewayUpdate).mockResolvedValue({
status: "skipped",
mode: "git",
reason: "dirty",
steps: [],
durationMs: 100,
} satisfies UpdateRunResult);
await updateCommand({ channel: "dev" });
const errors = vi.mocked(defaultRuntime.error).mock.calls.map((call) => String(call[0]));
const logs = vi.mocked(defaultRuntime.log).mock.calls.map((call) => String(call[0]));
expect(errors.join("\n")).toContain("Update blocked: local files are edited in this checkout.");
expect(logs.join("\n")).toContain(
"Git-based updates need a clean working tree before they can switch commits, fetch, or rebase.",
);
expect(logs.join("\n")).toContain(
"Commit, stash, or discard the local changes, then rerun `openclaw update`.",
);
expect(defaultRuntime.exit).toHaveBeenCalledWith(0);
});
it.each([
{
name: "refreshes service env when already installed",

View File

@@ -1012,11 +1012,15 @@ export async function updateCommand(opts: UpdateCommandOptions): Promise<void> {
if (result.status === "skipped") {
if (result.reason === "dirty") {
defaultRuntime.error(theme.error("Update blocked: local files are edited in this checkout."));
defaultRuntime.log(
theme.warn(
"Skipped: working directory has uncommitted changes. Commit or stash them first.",
"Git-based updates need a clean working tree before they can switch commits, fetch, or rebase.",
),
);
defaultRuntime.log(
theme.muted("Commit, stash, or discard the local changes, then rerun `openclaw update`."),
);
}
if (result.reason === "not-git-install") {
defaultRuntime.log(