Something went wrong. Try again.
mirror your GitHub repos to tangled.org automatically synchub.to
mirror sync tangled github git
Something went wrong. Try again.
2.7 kB · 74 lines
TypeScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475import { installationOctokit, installationToken } from './github-app'import { isTerminalRejection, loadActiveMapping, markMappingError, setLastSyncedRef, terminalRejectionMessage } from './repo-mapping'import { splicePush } from './splice'
const ZERO_SHA = '0000000000000000000000000000000000000000'
export interface PushPayload { installationId: number githubRepoId: number ref: string before: string after: string}
export interface PushResult { status: 'synced' | 'skipped' reason?: 'no-mapping' | 'disabled' | 'already-synced' | 'deletion' | 'repo-gone'}
/** * Mirror a single push from GitHub to the configured knot. * * 1. Look up the repo_mapping (installationId, githubRepoId). Skip if absent * or disabled. * 2. Ref-tip dedupe: if lastSyncedRefs[ref] === after, no-op. Guards against * GitHub redeliveries. This is a cache only; correctness comes from the * protocol-level compare-and-swap in the splice. * 3. Skip ref deletions (after = 0000…). Handled by github.delete. * 4. Splice: open receive-pack to the knot, fetch a thin pack of `after` * from GitHub with the knot's tips as haves, pipe it straight through. * Nothing touches disk. * 5. Update lastSyncedRefs[ref] = after. * * On terminal failures (repo gone from knot, auth rejected, pack too big) we * mark the mapping `status='error'` so the worker stops retrying. A lost * compare-and-swap (`stale-old-sha`) and other transient failures re-throw so * the queue retries with backoff; the retry re-reads the knot's tip. */export async function syncPush(payload: PushPayload): Promise<PushResult> { const loaded = await loadActiveMapping(payload.installationId, payload.githubRepoId) if ('skip' in loaded) return { status: 'skipped', reason: loaded.skip } const { mapping } = loaded
if (payload.after === ZERO_SHA) return { status: 'skipped', reason: 'deletion' }
if (mapping.lastSyncedRefs[payload.ref] === payload.after) { return { status: 'skipped', reason: 'already-synced' } }
const octokit = await installationOctokit(payload.installationId) const token = await installationToken(octokit)
try { const result = await splicePush({ installationId: payload.installationId, repoFullName: mapping.githubFullName, knot: mapping.knot, repoDid: mapping.tangledRepoDid, ref: payload.ref, want: payload.after, token, })
await setLastSyncedRef(mapping.id, payload.ref, result.sha) return { status: 'synced' } } catch (err) { if (isTerminalRejection(err)) { await markMappingError(mapping.id, terminalRejectionMessage(err)) return { status: 'skipped', reason: 'repo-gone' } } throw err }}