Skip to content

Commit c4a8329

Browse files
committed
fix(cron): run legacy cron store migration in gateway fast path
1 parent 794bd89 commit c4a8329

2 files changed

Lines changed: 45 additions & 0 deletions

File tree

src/cli/gateway-cli/run.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -546,6 +546,21 @@ export async function runGatewayCommand(opts: GatewayRunOpts) {
546546
void maybeLogPendingControlUiBuild(cfg).catch((err: unknown) => {
547547
gatewayLog.warn(`Control UI asset check failed: ${String(err)}`);
548548
});
549+
// Gateway fast path skips ensureCliExecutionBootstrap, so the normal state migration
550+
// preflight never runs. Without this the scheduler loads 0 jobs from empty SQLite.
551+
try {
552+
const { repairLegacyCronStoreWithoutPrompt } =
553+
await import("../../commands/doctor/cron/index.js");
554+
const cronMigration = await repairLegacyCronStoreWithoutPrompt({ cfg });
555+
for (const change of cronMigration.changes) {
556+
gatewayLog.info(change);
557+
}
558+
for (const warning of cronMigration.warnings) {
559+
gatewayLog.warn(warning);
560+
}
561+
} catch (err) {
562+
gatewayLog.warn(`Cron store migration check failed: ${formatErrorMessage(err)}`);
563+
}
549564
const portOverride = parsePort(opts.port);
550565
if (opts.port !== undefined && portOverride === null) {
551566
defaultRuntime.error(formatInvalidPortOption("--port"));

src/commands/doctor/cron/index.test.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
collectLegacyWhatsAppCrontabHealthWarning,
1717
maybeRepairLegacyCronStore,
1818
noteLegacyWhatsAppCrontabHealthCheck,
19+
repairLegacyCronStoreWithoutPrompt,
1920
} from "./index.js";
2021

2122
type TerminalNote = (message: string, title?: string) => void;
@@ -1058,3 +1059,32 @@ describe("legacy WhatsApp crontab health check", () => {
10581059
expect(noteMock).not.toHaveBeenCalled();
10591060
});
10601061
});
1062+
1063+
describe("repairLegacyCronStoreWithoutPrompt", () => {
1064+
it("migrates legacy JSON cron store to SQLite and returns a change summary", async () => {
1065+
const storePath = await makeTempStorePath();
1066+
await writeCronStore(storePath, [createLegacyCronJob()]);
1067+
1068+
const result = await repairLegacyCronStoreWithoutPrompt({
1069+
cfg: createCronConfig(storePath),
1070+
});
1071+
1072+
expect(result.changes).toHaveLength(1);
1073+
expect(result.changes[0]).toContain("Cron store migrated to SQLite");
1074+
expect(result.warnings).toHaveLength(0);
1075+
const jobs = await readPersistedJobs(storePath);
1076+
expect(jobs).toHaveLength(1);
1077+
});
1078+
1079+
it("returns empty result when no legacy store exists", async () => {
1080+
const storePath = await makeTempStorePath();
1081+
await writeCurrentCronStore(storePath, [createCurrentCronJob()]);
1082+
1083+
const result = await repairLegacyCronStoreWithoutPrompt({
1084+
cfg: createCronConfig(storePath),
1085+
});
1086+
1087+
expect(result.changes).toHaveLength(0);
1088+
expect(result.warnings).toHaveLength(0);
1089+
});
1090+
});

0 commit comments

Comments
 (0)