mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-05 00:40:22 +00:00
refactor(config): use source snapshots for config writes
This commit is contained in:
@@ -3,12 +3,12 @@ import type { OpenClawConfig } from "../../config/config.js";
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
readConfigFileSnapshot: vi.fn(),
|
||||
writeConfigFile: vi.fn(),
|
||||
replaceConfigFile: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("../../config/config.js", () => ({
|
||||
readConfigFileSnapshot: (...args: unknown[]) => mocks.readConfigFileSnapshot(...args),
|
||||
writeConfigFile: (...args: unknown[]) => mocks.writeConfigFile(...args),
|
||||
replaceConfigFile: (...args: unknown[]) => mocks.replaceConfigFile(...args),
|
||||
}));
|
||||
|
||||
let loadValidConfigOrThrow: typeof import("./shared.js").loadValidConfigOrThrow;
|
||||
@@ -18,7 +18,7 @@ describe("models/shared", () => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
mocks.readConfigFileSnapshot.mockClear();
|
||||
mocks.writeConfigFile.mockClear();
|
||||
mocks.replaceConfigFile.mockClear();
|
||||
({ loadValidConfigOrThrow, updateConfig } = await import("./shared.js"));
|
||||
});
|
||||
|
||||
@@ -26,6 +26,7 @@ describe("models/shared", () => {
|
||||
const cfg = { providers: {} } as unknown as OpenClawConfig;
|
||||
mocks.readConfigFileSnapshot.mockResolvedValue({
|
||||
valid: true,
|
||||
runtimeConfig: cfg,
|
||||
config: cfg,
|
||||
});
|
||||
|
||||
@@ -48,19 +49,22 @@ describe("models/shared", () => {
|
||||
const cfg = { update: { channel: "stable" } } as unknown as OpenClawConfig;
|
||||
mocks.readConfigFileSnapshot.mockResolvedValue({
|
||||
valid: true,
|
||||
hash: "config-1",
|
||||
sourceConfig: cfg,
|
||||
config: cfg,
|
||||
});
|
||||
mocks.writeConfigFile.mockResolvedValue(undefined);
|
||||
mocks.replaceConfigFile.mockResolvedValue(undefined);
|
||||
|
||||
await updateConfig((current) => ({
|
||||
...current,
|
||||
update: { channel: "beta" },
|
||||
}));
|
||||
|
||||
expect(mocks.writeConfigFile).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
expect(mocks.replaceConfigFile).toHaveBeenCalledWith({
|
||||
nextConfig: expect.objectContaining({
|
||||
update: { channel: "beta" },
|
||||
}),
|
||||
);
|
||||
baseHash: "config-1",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -11,7 +11,7 @@ import { formatCliCommand } from "../../cli/command-format.js";
|
||||
import {
|
||||
type OpenClawConfig,
|
||||
readConfigFileSnapshot,
|
||||
writeConfigFile,
|
||||
replaceConfigFile,
|
||||
} from "../../config/config.js";
|
||||
import { formatConfigIssueLines } from "../../config/issue-format.js";
|
||||
import { toAgentModelListLike } from "../../config/model-input.js";
|
||||
@@ -70,15 +70,22 @@ export async function loadValidConfigOrThrow(): Promise<OpenClawConfig> {
|
||||
const issues = formatConfigIssueLines(snapshot.issues, "-").join("\n");
|
||||
throw new Error(`Invalid config at ${snapshot.path}\n${issues}`);
|
||||
}
|
||||
return snapshot.config;
|
||||
return snapshot.runtimeConfig ?? snapshot.config;
|
||||
}
|
||||
|
||||
export async function updateConfig(
|
||||
mutator: (cfg: OpenClawConfig) => OpenClawConfig,
|
||||
): Promise<OpenClawConfig> {
|
||||
const config = await loadValidConfigOrThrow();
|
||||
const next = mutator(config);
|
||||
await writeConfigFile(next);
|
||||
const snapshot = await readConfigFileSnapshot();
|
||||
if (!snapshot.valid) {
|
||||
const issues = formatConfigIssueLines(snapshot.issues, "-").join("\n");
|
||||
throw new Error(`Invalid config at ${snapshot.path}\n${issues}`);
|
||||
}
|
||||
const next = mutator(structuredClone(snapshot.sourceConfig ?? snapshot.config));
|
||||
await replaceConfigFile({
|
||||
nextConfig: next,
|
||||
baseHash: snapshot.hash,
|
||||
});
|
||||
return next;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user