mirror your GitHub repos to tangled.org automatically
1

Configure Feed

Select the types of activity you want to include in your feed.

1/** 2 * Typed failures from the git wire splice. `reason` drives the worker's 3 * retry-vs-give-up decision in `sync-push.ts` / `sync-ref.ts`. 4 * 5 * - repo-gone the knot no longer has the repo (or our key was revoked 6 * such that it reports "not found"); terminal, mark error. 7 * - auth-rejected ssh public-key auth refused; terminal, mark error. 8 * - stale-old-sha our compare-and-swap lost: the knot's ref moved between 9 * reading its advertisement and sending the command, or a 10 * concurrent worker won. Transient; retry re-reads the tip. 11 * - too-big the pack exceeded the configured byte cap; terminal, it 12 * will never fit. 13 * - other anything unclassified; transient, let the queue retry. 14 */ 15export type WireFailureReason 16 = | 'repo-gone' 17 | 'auth-rejected' 18 | 'stale-old-sha' 19 | 'too-big' 20 | 'other' 21 22export class WireError extends Error { 23 constructor(message: string) { 24 super(message) 25 this.name = 'WireError' 26 } 27} 28 29export class RemoteRejectedError extends WireError { 30 constructor(message: string, public readonly reason: WireFailureReason) { 31 super(message) 32 this.name = 'RemoteRejectedError' 33 } 34} 35 36/** 37 * Classify ssh / sshd / knot stderr (the child process's stderr band, since 38 * we deliberately do not request side-band multiplexing). Returns null when 39 * nothing matches so the caller can fall back to a generic transient error. 40 */ 41export function classifySshStderr(stderr: string): RemoteRejectedError | null { 42 const lc = stderr.toLowerCase() 43 if (lc.includes('repository not found') || lc.includes('does not exist') || lc.includes('does not appear to be a git repository')) { 44 return new RemoteRejectedError(stderr.trim(), 'repo-gone') 45 } 46 if (lc.includes('permission denied') || (lc.includes('publickey') && lc.includes('denied'))) { 47 return new RemoteRejectedError(stderr.trim(), 'auth-rejected') 48 } 49 return null 50} 51 52/** 53 * Classify a receive-pack `ng <ref> <reason>` rejection. Any rejection that 54 * means "the ref's current value is not what you said" maps to stale-old-sha 55 * so the worker retries against a fresh advertisement. git phrases this two 56 * ways: `non-fast-forward` / `stale info` when updating a moved ref, and 57 * `failed to update ref` (stderr: "reference already exists") when our command 58 * claimed a create but the ref already exists. 59 */ 60export function classifyNgReason(reason: string): RemoteRejectedError { 61 const lc = reason.toLowerCase() 62 if ( 63 lc.includes('non-fast-forward') 64 || lc.includes('fetch first') 65 || lc.includes('stale info') 66 || lc.includes('not a fast forward') 67 || lc.includes('failed to update ref') 68 || lc.includes('reference already exists') 69 ) { 70 return new RemoteRejectedError(reason.trim(), 'stale-old-sha') 71 } 72 if (lc.includes('not found') || lc.includes('does not exist')) { 73 return new RemoteRejectedError(reason.trim(), 'repo-gone') 74 } 75 return new RemoteRejectedError(reason.trim(), 'other') 76}