223 lines · 9.0 KB
import { env } from "cloudflare:workers";
import { first } from "./db";
import { repoDb, stmt } from "./scoped-db";
import { loadRepoById, repoPermission } from "./repos";
import { loadUserById, toViewer } from "./auth";
import { addIssueEvent, checkMergeability, loadPullByIssueId, mergeGate, MergeError, performMerge } from "./pulls";
import type { PullRow } from "./pulls";
import { closeLinkedIssues } from "./pull-links";
import { restackAfterMerge, stackParent } from "./stacks";
import { ownerAccountIds, pathsAwaitingOwners, pullCodeOwners } from "./codeowners";
import { emitWebhook, recordActivity } from "./events/emit";
import { pullUrl } from "./pull-access";
import type { MergeMethod, RepoRow, Viewer } from "~/lib/types";

const MAX_AUTO_MERGES_PER_PASS = 10;
const MAX_STATUS_FANOUT = 10;
const MAX_LISTED_OWNERS = 5;

function displayOwner(token: string): string {
  if (token.startsWith("unresolved:")) return token.slice("unresolved:".length);
  if (token.startsWith("email:")) return `${token.slice("email:".length)} (no verified account)`;
  return `@${token} (no account)`;
}

function queueStub(repoId: string, baseRef: string) {
  return env.MERGE_QUEUE.get(env.MERGE_QUEUE.idFromName(`${repoId}:${baseRef}`));
}

/** Wakes the per-branch merge Durable Object so it re-evaluates auto-merges and the merge queue. */
export async function kickBranch(repoId: string, baseRef: string): Promise<void> {
  await queueStub(repoId, baseRef).kick(repoId, baseRef);
}

async function hasAutomation(repoId: string, issueId: string): Promise<boolean> {
  const row = await repoDb(repoId).first(
    `SELECT 1 FROM pull_auto_merges WHERE issue_id = ?1
     UNION ALL SELECT 1 FROM merge_queue_entries WHERE issue_id = ?1 AND state IN ('queued', 'testing') LIMIT 1`,
    issueId,
  );
  return row !== null;