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
|
import { Hono } from "hono";
import { getInfoRefs, serviceRpc } from "../git/protocol";
import { repoExists } from "../git/repository";
import { onPostReceive } from "../hooks/post-receive";
import { invalidateRepoCache } from "../lib/cache";
const git = new Hono();
git.get("/:owner/:repo.git/info/refs", async (c) => {
const { owner, repo } = c.req.param();
const service = c.req.query("service");
if (!service || !["git-upload-pack", "git-receive-pack"].includes(service)) {
return c.text("Invalid service", 400);
}
if (!(await repoExists(owner, repo))) {
return c.text("Repository not found", 404);
}
return getInfoRefs(owner, repo, service);
});
git.get("/:owner/:repo.git/HEAD", async (c) => {
const { owner, repo } = c.req.param();
if (!(await repoExists(owner, repo))) {
return c.text("Repository not found", 404);
}
const path = `repos/${owner}/${repo}.git/HEAD`;
const file = Bun.file(path);
if (!(await file.exists())) return c.text("Not found", 404);
return c.text(await file.text());
});
git.post("/:owner/:repo.git/git-upload-pack", async (c) => {
const { owner, repo } = c.req.param();
if (!(await repoExists(owner, repo))) {
return c.text("Repository not found", 404);
}
return serviceRpc(owner, repo, "git-upload-pack", c.req.raw.body);
});
git.post("/:owner/:repo.git/git-receive-pack", async (c) => {
const { owner, repo } = c.req.param();
if (!(await repoExists(owner, repo))) {
return c.text("Repository not found", 404);
}
const bodyBuffer = await c.req.arrayBuffer();
const response = await serviceRpc(
owner,
repo,
"git-receive-pack",
bodyBuffer
);
invalidateRepoCache(owner, repo);
const refs = parseReceivePackRefs(new Uint8Array(bodyBuffer));
if (refs.length > 0) {
onPostReceive(owner, repo, refs).catch((err) =>
console.error("[post-receive] hook error:", err)
);
}
return response;
});
function parseReceivePackRefs(
data: Uint8Array
): Array<{ oldSha: string; newSha: string; refName: string }> {
const text = new TextDecoder().decode(data);
const refs: Array<{ oldSha: string; newSha: string; refName: string }> = [];
let offset = 0;
while (offset < text.length) {
const lenHex = text.slice(offset, offset + 4);
const len = parseInt(lenHex, 16);
if (len === 0) {
offset += 4;
break;
}
if (len < 4) break;
const line = text.slice(offset + 4, offset + len);
offset += len;
const match = line.match(
/^([0-9a-f]{40}) ([0-9a-f]{40}) ([^\0\n]+)/
);
if (match) {
refs.push({
oldSha: match[1],
newSha: match[2],
refName: match[3].trim(),
});
}
}
return refs;
}
export default git;
|