import { DurableObject } from "cloudflare:workers"; import { processAutoMerges } from "./merge-automation"; import { processQueue } from "./merge-queue"; const TARGET_KEY = "target"; const DIRTY_KEY = "dirty"; const RETRY_ON_ERROR_MS = 60_000; type Target = { repoId: string; baseRef: string }; /** * One instance per (repository, base branch). Alarms are delivered one at a time, so every auto-merge * and merge-queue landing into a branch is serialized here; D1 remains the source of truth. */ export class MergeQueue extends DurableObject { async kick(repoId: string, baseRef: string): Promise { await this.ctx.storage.put({ [TARGET_KEY]: { repoId, baseRef }, [DIRTY_KEY]: true }); await this.ctx.storage.setAlarm(Date.now()); } override async alarm(): Promise { const target = await this.ctx.storage.get(TARGET_KEY); if (!target) return; await this.ctx.storage.put(DIRTY_KEY, false); try { await processAutoMerges(target.repoId, target.baseRef); const delay = await processQueue(target.repoId, target.baseRef); const rekicked = (await this.ctx.storage.get(DIRTY_KEY)) === true; if (rekicked) await this.ctx.storage.setAlarm(Date.now()); else if (delay !== null) await this.ctx.storage.setAlarm(Date.now() + delay); } catch (error) { console.error("Merge queue pass failed", error); await this.ctx.storage.setAlarm(Date.now() + RETRY_ON_ERROR_MS); } } }