285 lines · 10.7 KB
import { env } from "cloudflare:workers";
import { siteTeardownFor } from "./sites/sync";
import { assertPublicHttpsUrl } from "./git/relay";
import { assertResolvesPublic } from "./net/resolve-guard";
import { batch, first, run, stmt } from "./db";
import { randomId } from "./crypto";
import { artifactsNameFor, isArtifactsError, openArtifactsRepo } from "./git/artifacts";
import { artifactsCall } from "./git/artifacts-call";
import { invalidateRefs, refreshRefs } from "./git/refs";
import { linkForkObjects, purgeRepoLfs } from "./lfs/objects";
import { withStore } from "./git/store";
import { commitChanges } from "./git/writer";
import { signatureNow } from "./git/objects";
import { hasPrivateForkAncestor, loadRepoById } from "./repos";
import { badRequest, conflict } from "./errors";
import { encryptRepoSecret } from "./secrets";
import { describeProvisioningError, markProvisioningFailed, startProvisioning } from "./repo-provisioning";
import { emitWebhook, publicUrl, recordActivity } from "./events/emit";
import type { RepoRow, Viewer, Visibility } from "~/lib/types";

const DEFAULT_BRANCH = "main";
const PENDING_REPO_TIMEOUT_MS = 30 * 60 * 1000;
/** Artifacts still reports progress: allow very large imports and forks to keep going. */
const MAX_IN_PROGRESS_MS = 6 * 60 * 60 * 1000;
const encoder = new TextEncoder();

async function ensureNameFree(ownerId: string, name: string): Promise<void> {
  const existing = await first<{ id: string }>("SELECT id FROM repos WHERE owner_id = ? AND name = ?", ownerId, name);
  if (existing) throw conflict("A repository with that name already exists");
}

async function insertRepo(input: {
  id: string;
  ownerId: string;
  name: string;
  artifactsName: string;
  remote: string;
  description: string | null;
  visibility: Visibility;
  defaultBranch: string;