mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 10:20:42 +00:00
cron: recover missing split config file
This commit is contained in:
@@ -238,6 +238,40 @@ describe("cron store", () => {
|
||||
expect(stateFile.jobs["job-1"].state.nextRunAtMs).toBe(payload.jobs[0].createdAtMs + 60_000);
|
||||
});
|
||||
|
||||
it("recreates a missing config file without rewriting unchanged state", async () => {
|
||||
const store = await makeStorePath();
|
||||
const statePath = store.storePath.replace(/\.json$/, "-state.json");
|
||||
const payload = makeStore("job-1", true);
|
||||
payload.jobs[0].state = { nextRunAtMs: payload.jobs[0].createdAtMs + 60_000 };
|
||||
|
||||
await saveCronStore(store.storePath, payload);
|
||||
await loadCronStore(store.storePath);
|
||||
const stateRawBefore = await fs.readFile(statePath, "utf-8");
|
||||
await fs.rm(store.storePath);
|
||||
|
||||
const renamedDestinations: string[] = [];
|
||||
const origRename = fs.rename.bind(fs);
|
||||
const spy = vi.spyOn(fs, "rename").mockImplementation(async (src, dest) => {
|
||||
renamedDestinations.push(String(dest));
|
||||
return origRename(src, dest);
|
||||
});
|
||||
|
||||
try {
|
||||
await saveCronStore(store.storePath, payload);
|
||||
} finally {
|
||||
spy.mockRestore();
|
||||
}
|
||||
|
||||
const config = JSON.parse(await fs.readFile(store.storePath, "utf-8"));
|
||||
const stateRawAfter = await fs.readFile(statePath, "utf-8");
|
||||
|
||||
expect(config.jobs[0].id).toBe("job-1");
|
||||
expect(config.jobs[0].state).toEqual({});
|
||||
expect(stateRawAfter).toBe(stateRawBefore);
|
||||
expect(renamedDestinations).toContain(store.storePath);
|
||||
expect(renamedDestinations).not.toContain(statePath);
|
||||
});
|
||||
|
||||
it("migrates legacy inline state into the state sidecar", async () => {
|
||||
const store = await makeStorePath();
|
||||
const statePath = store.storePath.replace(/\.json$/, "-state.json");
|
||||
|
||||
@@ -232,6 +232,25 @@ async function atomicWrite(filePath: string, content: string, dirMode = 0o700):
|
||||
await setSecureFileMode(filePath);
|
||||
}
|
||||
|
||||
async function serializedFileNeedsWrite(
|
||||
filePath: string,
|
||||
expectedJson: string,
|
||||
contentChanged: boolean,
|
||||
): Promise<boolean> {
|
||||
if (contentChanged) {
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
const diskJson = await fs.promises.readFile(filePath, "utf-8");
|
||||
return diskJson !== expectedJson;
|
||||
} catch (err) {
|
||||
if ((err as { code?: unknown })?.code === "ENOENT") {
|
||||
return true;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
export async function saveCronStore(
|
||||
storePath: string,
|
||||
store: CronStoreFile,
|
||||
@@ -247,22 +266,10 @@ export async function saveCronStore(
|
||||
const configChanged = cache?.configJson !== configJson;
|
||||
const stateChanged = cache?.stateJson !== stateJson;
|
||||
const migrating = cache?.needsSplitMigration === true;
|
||||
let stateNeedsWrite = stateChanged;
|
||||
const configNeedsWrite = await serializedFileNeedsWrite(storePath, configJson, configChanged);
|
||||
const stateNeedsWrite = await serializedFileNeedsWrite(statePath, stateJson, stateChanged);
|
||||
|
||||
if (!stateNeedsWrite) {
|
||||
try {
|
||||
const diskStateJson = await fs.promises.readFile(statePath, "utf-8");
|
||||
stateNeedsWrite = diskStateJson !== stateJson;
|
||||
} catch (err) {
|
||||
if ((err as { code?: unknown })?.code === "ENOENT") {
|
||||
stateNeedsWrite = true;
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!configChanged && !stateNeedsWrite && !migrating) {
|
||||
if (!configNeedsWrite && !stateNeedsWrite && !migrating) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -274,7 +281,7 @@ export async function saveCronStore(
|
||||
updatedCache.stateJson = stateJson;
|
||||
}
|
||||
|
||||
if (configChanged || migrating) {
|
||||
if (configNeedsWrite || migrating) {
|
||||
// Determine backup need: only when config actually changed (not migration-only).
|
||||
const skipBackup = opts?.skipBackup === true || !configChanged;
|
||||
if (!skipBackup) {
|
||||
|
||||
Reference in New Issue
Block a user