113 lines · 4.7 KB
import { waitUntil } from "cloudflare:workers";
import { run } from "../db";
import { BACKFILL_START } from "../repos";
import { advertisement, artifactsSource, assertPublicHttpsUrl, relayPack } from "./relay";
import { pushToArtifacts } from "./writer";
import { assertResolvesPublic } from "../net/resolve-guard";
import { badRequest } from "../errors";
import type { RepoRow } from "~/lib/types";

const MAX_SYNC_REFS = 1000;
const MAX_SYNC_BYTES = 1024 * 1024 * 1024;
const ZERO_SHA = "0".repeat(40);

export type RefSyncResult = {
  created: number;
  rejected: string[];
  /** Refs still missing after this pass; call again to continue. */
  skipped: number;
  headBranch: string | null;
};

export type RefSyncOptions = {
  /** Copy just the primary branch (`branch`, else the source HEAD) so the repository opens quickly. */
  only?: "head";
  branch?: string | null;
};

/** Basic auth for a source remote: GitHub and GitLab accept any username with a token as the password. */
export function sourceAuthorization(token: string, username = "git"): string {
  return `Basic ${btoa(`${username}:${token}`)}`;
}

/**
 * Copies branches and tags from a git remote by relaying a packfile straight from the source's
 * upload-pack into Artifacts' receive-pack, so nothing is buffered in the Worker. Each call copies at
 * most MAX_SYNC_REFS refs and reports how many remain; existing refs are never overwritten.
 */
export async function syncRefsFromSource(
  repo: RepoRow,
  sourceUrl: string,