80 lines · 3.0 KB
import { waitUntil } from "cloudflare:workers";
import { hasPermission, loadRepoById, repoPermission } from "./repos";
import { loadUserById, toViewer } from "./auth";
import type { RepoRow } from "~/lib/types";
import { checkMergeability, loadPullByIssueId } from "./pulls";
import { scheduleWorkflows } from "./ci/scheduler";
import { requestCodeOwnerReviews } from "./review-requests";
import { emitWebhook, publicUrl } from "./events/emit";
import type { Viewer } from "~/lib/types";

/**
 * Pull requests from forks run untrusted code on our containers, so (like GitHub) runs from actors
 * without write access to the base repository wait for a maintainer to approve them.
 */
export async function forkRunNeedsApproval(
  baseRepo: RepoRow,
  headRepo: RepoRow,
  actorId: string | null,
): Promise<boolean> {
  if (baseRepo.id === headRepo.id) return false;
  const actor = actorId ? await loadUserById(actorId) : null;
  return !hasPermission(await repoPermission(baseRepo, actor ? toViewer(actor) : null), "write");
}

/** Kicks off CI and mergeability checks for a pull request lifecycle event without blocking the request. */
export function afterPullChange(
  issueId: string,
  action: "opened" | "reopened" | "synchronize" | "ready_for_review",
  actor: Viewer,
): void {
  waitUntil(
    (async () => {
      const pull = await loadPullByIssueId(issueId);
      if (!pull) return;
      const [baseRepo, headRepo] = await Promise.all([loadRepoById(pull.repo_id), loadRepoById(pull.head_repo_id)]);
      if (!baseRepo || !headRepo) return;
      await emitWebhook(baseRepo.id, "pull_request", {
        action,
        number: pull.number,
        pull_request: {