192 lines · 7.4 KB
import { env } from "cloudflare:workers";
import { repoDb, type ScopedDb } from "../scoped-db";
import { locate } from "../scoped-db/locators";
import { claimHost, siteDb, siteDbById } from "./store";
import { SITE_LIMITS, slotKey } from "./config";
import { commitLabel, pullLabel } from "./naming";
import { isOrphanSite } from "./sync";
import type { SiteDeploymentRow, SiteRow, SlotPointer } from "./config";

type AliasSite = Pick<SiteRow, "id" | "repo_id" | "label" | "commit_previews">;
type AliasDeployment = Pick<SiteDeploymentRow, "id" | "environment" | "branch" | "sha">;

type OpenPull = { number: number };

/** Open pull requests of the site's own repository whose head is `branch`; fork pulls never get an alias. */
async function openPullsFor(site: AliasSite, branch: string): Promise<OpenPull[]> {
  if (!site.repo_id) return [];
  return repoDb(site.repo_id).all<OpenPull>(
    `SELECT i.number FROM pull_requests p JOIN issues i ON i.id = p.issue_id
     WHERE p.repo_id = ? AND p.head_repo_id = ? AND p.head_ref = ? AND i.state = 'open' LIMIT ?`,
    site.repo_id,
    site.repo_id,
    branch,
    SITE_LIMITS.previewsPerPush,
  );
}

/** Writes an alias pointer unless another live site already owns that host. */
async function point(site: AliasSite, label: string, deploymentId: string): Promise<boolean> {
  const existing = await env.KV.get<SlotPointer>(slotKey(label), "json");
  if (existing && existing.siteId !== site.id && !(await isOrphanSite(existing.siteId))) return false;
  const pointer: SlotPointer = { siteId: site.id, deploymentId };
  await env.KV.put(slotKey(label), JSON.stringify(pointer));
  await claimHost(label, site.id);
  return true;
}

async function record(
  site: AliasSite,
  label: string,