mirror your GitHub repos to tangled.org automatically
1

Configure Feed

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

1import { spawn } from 'node:child_process' 2import { afterEach, beforeEach, describe, expect, it } from 'vitest' 3import { Server, utils as sshUtils } from 'ssh2' 4import { ReceivePackSession } from '../../server/utils/git-wire/receive-pack' 5import { ssh2ReceivePackFactory } from '../../server/utils/git-wire/receive-pack' 6import { ZERO_SHA } from '../../server/utils/git-wire/refs' 7import { generateKeypair, pkcs8ToOpenSshPrivate } from '../../server/utils/ssh-keypair' 8import { fakeGithubFetch, GitFixture } from '../utils/git-wire' 9import { fetchPack } from '../../server/utils/git-wire/upload-pack' 10 11async function* fromBuffer(b: Buffer): AsyncGenerator<Buffer> { 12 yield b 13} 14 15async function drain(gen: AsyncGenerator<Buffer>): Promise<Buffer> { 16 const parts: Buffer[] = [] 17 for await (const c of gen) parts.push(c) 18 return Buffer.concat(parts) 19} 20 21/** 22 * An in-process ssh2 server that authorises one public key and runs the real 23 * `git-receive-pack` against the given bare repo on exec. Mirrors the knot's 24 * `git@host: git-receive-pack '<path>'` surface so the ssh2 factory is exercised 25 * end to end. 26 */ 27function startKnotServer(authorizedPubKey: string, repoFor: (path: string) => string | null) { 28 const hostKey = sshUtils.generateKeyPairSync('ed25519').private 29 const parsed = sshUtils.parseKey(authorizedPubKey) 30 if (parsed instanceof Error) throw parsed 31 const allowed = Array.isArray(parsed) ? parsed[0]! : parsed 32 const allowedSSH = allowed.getPublicSSH() 33 34 const server = new Server({ hostKeys: [hostKey] }, client => { 35 client.on('authentication', ctx => { 36 if (ctx.method === 'publickey' && ctx.key.algo === allowed.type && ctx.key.data.equals(allowedSSH)) { 37 ctx.accept() 38 return 39 } 40 ctx.reject() 41 }) 42 client.on('ready', () => { 43 client.on('session', accept => { 44 accept().once('exec', (acceptExec, _reject, info) => { 45 const match = info.command.match(/^git-receive-pack '(.+)'$/) 46 const repo = match ? repoFor(match[1]!) : null 47 const stream = acceptExec() 48 if (!repo) { 49 stream.stderr.write('repository not found\n') 50 stream.exit(128) 51 stream.end() 52 return 53 } 54 const child = spawn('git-receive-pack', [repo], { stdio: ['pipe', 'pipe', 'pipe'] }) 55 stream.pipe(child.stdin) 56 child.stdout.pipe(stream) 57 child.stderr.on('data', (d: Buffer) => stream.stderr.write(d)) 58 child.on('close', code => { stream.exit(code ?? 0); stream.end() }) 59 }) 60 }) 61 }) 62 }) 63 64 return new Promise<{ port: number, close: () => void }>(resolve => { 65 server.listen(0, '127.0.0.1', () => { 66 resolve({ port: (server.address() as { port: number }).port, close: () => server.close() }) 67 }) 68 }) 69} 70 71describe('ssh2ReceivePackFactory (against an in-process ssh2 knot)', () => { 72 let fx: GitFixture 73 let realFetch: typeof globalThis.fetch 74 let knotServer: { port: number, close: () => void } | null = null 75 76 beforeEach(() => { 77 fx = new GitFixture() 78 realFetch = globalThis.fetch 79 }) 80 81 afterEach(() => { 82 globalThis.fetch = realFetch 83 knotServer?.close() 84 knotServer = null 85 fx.cleanup() 86 }) 87 88 async function packFor(ghBare: string, want: string, haves: string[]): Promise<Buffer> { 89 globalThis.fetch = fakeGithubFetch(new Map([['owner/repo', ghBare]])) as unknown as typeof globalThis.fetch 90 const { pack } = await fetchPack({ repoFullName: 'owner/repo', token: 't', want, haves, maxBytes: 1 << 30 }) 91 return drain(pack) 92 } 93 94 it('pushes a ref to the knot over a real ssh2 connection', async () => { 95 const gh = fx.initBare('gh.git') 96 const work = fx.initWork('work') 97 const sha = fx.commit(work, 'a.txt', 'hello') 98 fx.pushTo(work, gh, 'HEAD:refs/heads/main') 99 const knot = fx.initBare('knot.git') 100 101 const key = generateKeypair('synchub.to/1') 102 knotServer = await startKnotServer(key.publicKeyOpenSsh, p => (p === '/repo-did' ? knot : null)) 103 104 const factory = ssh2ReceivePackFactory({ 105 host: '127.0.0.1', 106 port: knotServer.port, 107 repoPath: '/repo-did', 108 // Mirror production: the worker hands ssh2 the OpenSSH-format key that 109 // `loadSshKeyForInstall` derives from the stored PKCS#8 PEM. 110 privateKey: pkcs8ToOpenSshPrivate(key.privateKeyPem, 'synchub.to/1'), 111 }) 112 113 const session = await ReceivePackSession.open(factory) 114 await session.push([{ ref: 'refs/heads/main', old: ZERO_SHA, next: sha }], fromBuffer(await packFor(gh, sha, []))) 115 116 expect(fx.revParse(knot, 'refs/heads/main')).toBe(sha) 117 }) 118 119 it('surfaces a connection failure as a caught WireError, not an uncaught throw', async () => { 120 const key = generateKeypair('synchub.to/1') 121 // Nothing listening on this port: connect() emits 'error' (ECONNREFUSED). 122 const factory = ssh2ReceivePackFactory({ 123 host: '127.0.0.1', 124 port: 1, 125 repoPath: '/repo-did', 126 privateKey: pkcs8ToOpenSshPrivate(key.privateKeyPem, 'synchub.to/1'), 127 }) 128 await expect(ReceivePackSession.open(factory)).rejects.toThrow(/ssh error|ECONNREFUSED|advertisement|end of stream/) 129 }) 130})