import { advertisement, artifactsSource, assertPublicHttpsUrl, relayPack } from "./relay"; import { pushToArtifacts } from "./writer"; 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[]; skipped: number }; /** * Artifacts imports only copy one branch. This copies every other branch and tag from the source by * relaying a packfile straight from the source's upload-pack into Artifacts' receive-pack, so nothing * is buffered in the Worker. Existing refs are never overwritten. */ export async function syncRefsFromSource(repo: RepoRow, sourceUrl: string): Promise { assertPublicHttpsUrl(sourceUrl); const [source, target] = await Promise.all([ advertisement({ url: sourceUrl, authorization: null }), artifactsSource(repo).then((self) => advertisement(self)), ]); const existingNames = new Set(target.map((ref) => ref.name)); const allMissing = source.filter((ref) => !existingNames.has(ref.name)); const missing = allMissing.slice(0, MAX_SYNC_REFS); const result: RefSyncResult = { created: 0, rejected: [], skipped: allMissing.length - missing.length }; if (missing.length === 0) return result; const present = new Set(target.map((ref) => ref.sha)); const wants = [...new Set(missing.map((ref) => ref.sha))].filter((sha) => !present.has(sha)); if (wants.length === 0) { await pushToArtifacts( repo, missing.map((ref) => ({ ref: ref.name, oldSha: ZERO_SHA, newSha: ref.sha })), [], ); return { ...result, created: missing.length }; } const report = await relayPack({ source: { url: sourceUrl, authorization: null }, target: repo, wants, haves: [...present], commands: missing, maxBytes: MAX_SYNC_BYTES, }); return { ...result, created: report.accepted, rejected: report.rejected }; }