1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
|
import { getRepoPath } from "./repository";
function pktLine(data: string): string {
const len = (data.length + 4).toString(16).padStart(4, "0");
return `${len}${data}`;
}
function pktFlush(): string {
return "0000";
}
export function infoRefsResponse(
service: string,
advertise: string
): Response {
const body = pktLine(`# service=${service}\n`) + pktFlush() + advertise;
return new Response(body, {
headers: {
"Content-Type": `application/x-${service}-advertisement`,
"Cache-Control": "no-cache",
},
});
}
export async function getInfoRefs(
owner: string,
repo: string,
service: string
): Promise<Response> {
const repoDir = getRepoPath(owner, repo);
const proc = Bun.spawn([service, "--stateless-rpc", "--advertise-refs", repoDir], {
stdout: "pipe",
stderr: "pipe",
});
const stdout = await new Response(proc.stdout).text();
await proc.exited;
return infoRefsResponse(service, stdout);
}
export async function serviceRpc(
owner: string,
repo: string,
service: string,
body: ReadableStream<Uint8Array> | ArrayBuffer | null,
extraEnv?: Record<string, string>
): Promise<Response> {
const repoDir = getRepoPath(owner, repo);
const rawBytes = body
? body instanceof ArrayBuffer
? new Uint8Array(body)
: new Uint8Array(await new Response(body).arrayBuffer())
: new Uint8Array();
const inputBytes =
rawBytes.length >= 2 && rawBytes[0] === 0x1f && rawBytes[1] === 0x8b
? Bun.gunzipSync(rawBytes)
: rawBytes;
const proc = Bun.spawn([service, "--stateless-rpc", repoDir], {
stdin: "pipe",
stdout: "pipe",
stderr: "pipe",
env: extraEnv ? { ...process.env, ...extraEnv } : process.env,
});
proc.stdin.write(inputBytes);
proc.stdin.end();
if (service === "git-upload-pack") {
proc.exited.then((code) => {
if (code !== 0) {
new Response(proc.stderr)
.text()
.then((stderr) =>
console.error(
`[git] ${service} exited ${code} for ${owner}/${repo}: ${stderr}`
)
)
.catch(() => {});
}
});
return new Response(proc.stdout, {
headers: {
"Content-Type": `application/x-${service}-result`,
"Cache-Control": "no-cache",
},
});
}
const stdout = await new Response(proc.stdout).arrayBuffer();
await proc.exited;
return new Response(stdout, {
headers: {
"Content-Type": `application/x-${service}-result`,
"Cache-Control": "no-cache",
},
});
}
|