96 lines · 4.0 KB
import { repoDb } from "./scoped-db";
import { badRequest, conflict, forbidden } from "./errors";
import { branchSha, listRefs } from "./git/refs";
import { kickBranch } from "./merge-automation";
import { dequeuePull, enqueuePull } from "./merge-queue";
import { afterPullChange } from "./pull-events";
import { stackParent } from "./stacks";
import { addIssueEvent, MergeError } from "./pulls";
import type { MergeMethod, Viewer } from "~/lib/types";
import type { PullAccess } from "./reviews-write";

function rethrowMerge(error: unknown): never {
  if (error instanceof MergeError) throw conflict(error.message);
  throw error;
}

/** Requires write access. Enabling pins the current head; a missing or stale `expectedHead` is a 409. */
export async function setAutoMerge(
  access: PullAccess,
  viewer: Viewer,
  input: { enabled: boolean; method: MergeMethod; message: string; expectedHead?: string },
): Promise<void> {
  const { pull } = access;
  if (!input.enabled) {
    await repoDb(pull.repo_id).run("DELETE FROM pull_auto_merges WHERE issue_id = ?", pull.issue_id);
    await addIssueEvent(pull, viewer.id, "auto_merge_disabled", {});
    return;
  }
  if (pull.state !== "open") throw badRequest("Pull request is not open");
  if (pull.head_sha !== input.expectedHead) throw conflict("The head branch changed; reload and try again");
  await repoDb(pull.repo_id).run(
    `INSERT INTO pull_auto_merges (issue_id, repo_id, method, message, enabled_by, created_at, head_sha)
     VALUES (?, ?, ?, ?, ?, ?, ?)
     ON CONFLICT (issue_id) DO UPDATE SET method = excluded.method, message = excluded.message,
       enabled_by = excluded.enabled_by, head_sha = excluded.head_sha`,
    pull.issue_id,
    pull.repo_id,
    input.method,
    input.message,
    viewer.id,