340 lines · 12.9 KB
import { repoDb } from "./scoped-db";
import { randomId } from "./crypto";
import { loadRepoById, repoPermission } from "./repos";
import { addIssueEvent, loadPullByIssueId, markMerged, mergeGate, MergeError, performMerge } from "./pulls";
import type { PullRow } from "./pulls";
import { loadProtections, protectionFor } from "./protection";
import { listCommitStatuses, requiredCheckResults } from "./statuses";
import { buildSpeculativeMerge, fastForward, inWorkerMergeability } from "./pull-merge";
import { queueMergeFix } from "./mergefix/service";
import { branchSha, listRefs } from "./git/refs";
import { deleteRef } from "./git/writer";
import { triggerPush } from "./events/push-trigger";
import { scheduleWorkflows } from "./ci/scheduler";
import { assertBelowCap } from "./limits";
import { actorViewer, announceMerged, defaultMergeMessage, kickBranch, mergeIdentity } from "./merge-automation";
import type { MergeMethod, RepoRow, Viewer } from "~/lib/types";

const MAX_QUEUE_LENGTH = 50;
const MAX_ENTRIES_PER_PASS = 5;
const CHECK_TIMEOUT_MS = 2 * 60 * 60 * 1000;
/** Safety re-poll while waiting on CI; status updates normally wake the queue first. */
export const QUEUE_POLL_MS = 10 * 60 * 1000;
/** Backoff when a pass could not make progress (e.g. the base moved during landing). */
const RETRY_BACKOFF_MS = 15_000;

export type QueueMethod = "merge" | "squash";

type QueueEntry = {
  id: string;
  issue_id: string;
  method: QueueMethod;
  state: "queued" | "testing";
  speculative_sha: string | null;
  base_sha: string | null;
  head_sha: string | null;
  enqueued_by: string;
  updated_at: number;
};

export function queueRef(baseRef: string, number: number): string {