This commit is contained in:
parent
816881c2cc
commit
3379abf99b
9 changed files with 57 additions and 36 deletions
|
|
@ -1,4 +1,4 @@
|
|||
# git-webhooks
|
||||
# patchbay
|
||||
|
||||
Containerized Bun service for GitHub and jojo.build webhooks.
|
||||
|
||||
|
|
@ -6,10 +6,13 @@ Containerized Bun service for GitHub and jojo.build webhooks.
|
|||
|
||||
```text
|
||||
GET /healthz
|
||||
POST /git-webhooks/jojo
|
||||
POST /git-webhooks/github
|
||||
POST /patchbay/jojo
|
||||
POST /patchbay/github
|
||||
```
|
||||
|
||||
Existing `/git-webhooks/jojo` and `/git-webhooks/github` routes remain
|
||||
compatibility aliases for existing webhook registrations.
|
||||
|
||||
## Environment
|
||||
|
||||
```text
|
||||
|
|
|
|||
2
bun.lock
2
bun.lock
|
|
@ -3,7 +3,7 @@
|
|||
"configVersion": 1,
|
||||
"workspaces": {
|
||||
"": {
|
||||
"name": "@peezy.tech/git-webhooks",
|
||||
"name": "@peezy.tech/patchbay",
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest",
|
||||
"typescript": "^5.9.3",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name": "@peezy.tech/git-webhooks",
|
||||
"name": "@peezy.tech/patchbay",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
|
|
|||
|
|
@ -132,7 +132,7 @@ export function buildDiscordPayload(input: DiscordNotification): DiscordPayload
|
|||
].filter((item): item is DiscordEmbedField => item !== null);
|
||||
|
||||
return {
|
||||
username: "git-webhooks",
|
||||
username: "patchbay",
|
||||
embeds: [
|
||||
{
|
||||
title: feedTitle(signal).slice(0, 256),
|
||||
|
|
@ -166,7 +166,7 @@ export function buildDiscordPayload(input: DiscordNotification): DiscordPayload
|
|||
].filter((item): item is DiscordEmbedField => item !== null);
|
||||
|
||||
return {
|
||||
username: "git-webhooks",
|
||||
username: "patchbay",
|
||||
embeds: [
|
||||
{
|
||||
title: eventTitle(event).slice(0, 256),
|
||||
|
|
@ -175,7 +175,7 @@ export function buildDiscordPayload(input: DiscordNotification): DiscordPayload
|
|||
fields,
|
||||
timestamp: event.receivedAt,
|
||||
footer: {
|
||||
text: "git-webhooks",
|
||||
text: "patchbay",
|
||||
},
|
||||
},
|
||||
],
|
||||
|
|
|
|||
|
|
@ -101,10 +101,10 @@ export function createHandler(config: ServerConfig): (request: Request) => Promi
|
|||
if (url.pathname === "/healthz") {
|
||||
return textResponse("ok\n");
|
||||
}
|
||||
if (url.pathname === "/git-webhooks/github") {
|
||||
if (url.pathname === "/patchbay/github" || url.pathname === "/git-webhooks/github") {
|
||||
return handleGithub(request, config, store);
|
||||
}
|
||||
if (url.pathname === "/git-webhooks/jojo") {
|
||||
if (url.pathname === "/patchbay/jojo" || url.pathname === "/git-webhooks/jojo") {
|
||||
return handleJojo(request, config, store);
|
||||
}
|
||||
return jsonResponse({ error: "not_found" }, { status: 404 });
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ const pushEvent: GitWebhookEvent = {
|
|||
receivedAt: "2026-05-12T21:00:00.000Z",
|
||||
repo: {
|
||||
owner: "peezy-tech",
|
||||
name: "git-webhooks",
|
||||
fullName: "peezy-tech/git-webhooks",
|
||||
name: "patchbay",
|
||||
fullName: "peezy-tech/patchbay",
|
||||
},
|
||||
sender: {
|
||||
username: "matamune",
|
||||
|
|
@ -20,7 +20,7 @@ const pushEvent: GitWebhookEvent = {
|
|||
after: "0123456789abcdef",
|
||||
raw: {
|
||||
head_commit: {
|
||||
url: "https://jojo.build/peezy-tech/git-webhooks/commit/0123456789abcdef",
|
||||
url: "https://jojo.build/peezy-tech/patchbay/commit/0123456789abcdef",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
@ -68,7 +68,7 @@ describe("discord notifications", () => {
|
|||
id: "jojo:delivery-1:main_push",
|
||||
kind: "main_push",
|
||||
provider: "jojo",
|
||||
repoFullName: "peezy-tech/git-webhooks",
|
||||
repoFullName: "peezy-tech/patchbay",
|
||||
ref: "refs/heads/main",
|
||||
sha: "0123456789abcdef",
|
||||
deliveryId: "delivery-1",
|
||||
|
|
@ -76,9 +76,9 @@ describe("discord notifications", () => {
|
|||
},
|
||||
});
|
||||
|
||||
expect(payload.username).toBe("git-webhooks");
|
||||
expect(payload.embeds[0].title).toBe("[jojo] peezy-tech/git-webhooks push to main");
|
||||
expect(payload.embeds[0].url).toBe("https://jojo.build/peezy-tech/git-webhooks/commit/0123456789abcdef");
|
||||
expect(payload.username).toBe("patchbay");
|
||||
expect(payload.embeds[0].title).toBe("[jojo] peezy-tech/patchbay push to main");
|
||||
expect(payload.embeds[0].url).toBe("https://jojo.build/peezy-tech/patchbay/commit/0123456789abcdef");
|
||||
expect(payload.embeds[0].fields).toContainEqual({ name: "Queued", value: "main_push", inline: true });
|
||||
});
|
||||
|
||||
|
|
@ -115,7 +115,7 @@ describe("discord notifications", () => {
|
|||
return new Response(null, { status: 204 });
|
||||
});
|
||||
|
||||
expect(JSON.parse(body).embeds[0].title).toBe("[jojo] peezy-tech/git-webhooks push to main");
|
||||
expect(JSON.parse(body).embeds[0].title).toBe("[jojo] peezy-tech/patchbay push to main");
|
||||
});
|
||||
|
||||
test("throws on Discord failure", async () => {
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ describe("feed watcher", () => {
|
|||
});
|
||||
|
||||
test("first poll primes state without emitting old entries", async () => {
|
||||
const dataDir = await mkdtemp(join(tmpdir(), "git-webhooks-feed-"));
|
||||
const dataDir = await mkdtemp(join(tmpdir(), "patchbay-feed-"));
|
||||
const sourcesPath = join(dataDir, "sources.json");
|
||||
await writeFile(sourcesPath, JSON.stringify({ sources: [source] }), "utf8");
|
||||
|
||||
|
|
@ -105,7 +105,7 @@ describe("feed watcher", () => {
|
|||
});
|
||||
|
||||
test("later polls emit new entries and release fork sync jobs", async () => {
|
||||
const dataDir = await mkdtemp(join(tmpdir(), "git-webhooks-feed-"));
|
||||
const dataDir = await mkdtemp(join(tmpdir(), "patchbay-feed-"));
|
||||
const sourcesPath = join(dataDir, "sources.json");
|
||||
const releaseSource: FeedSourceConfig = {
|
||||
...source,
|
||||
|
|
|
|||
|
|
@ -3,10 +3,10 @@ import { normalizeGithubEvent } from "../src/providers/github";
|
|||
import { normalizeJojoEvent } from "../src/providers/jojo";
|
||||
|
||||
const repository = {
|
||||
name: "git-webhooks",
|
||||
full_name: "peezy-tech/git-webhooks",
|
||||
clone_url: "https://example.test/peezy-tech/git-webhooks.git",
|
||||
ssh_url: "git@example.test:peezy-tech/git-webhooks.git",
|
||||
name: "patchbay",
|
||||
full_name: "peezy-tech/patchbay",
|
||||
clone_url: "https://example.test/peezy-tech/patchbay.git",
|
||||
ssh_url: "git@example.test:peezy-tech/patchbay.git",
|
||||
default_branch: "main",
|
||||
owner: { login: "peezy-tech" },
|
||||
};
|
||||
|
|
@ -28,7 +28,7 @@ describe("provider normalization", () => {
|
|||
|
||||
expect(event.provider).toBe("github");
|
||||
expect(event.event).toBe("push");
|
||||
expect(event.repo?.fullName).toBe("peezy-tech/git-webhooks");
|
||||
expect(event.repo?.fullName).toBe("peezy-tech/patchbay");
|
||||
expect(event.sender?.username).toBe("peezy");
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -23,15 +23,15 @@ async function signedRequest(path: string, provider: "github" | "jojo", secret:
|
|||
|
||||
describe("server", () => {
|
||||
test("healthz returns ok", async () => {
|
||||
const handler = createHandler({ githubSecret: "gh", jojoSecret: "jojo", dataDir: await mkdtemp(join(tmpdir(), "git-webhooks-")) });
|
||||
const handler = createHandler({ githubSecret: "gh", jojoSecret: "jojo", dataDir: await mkdtemp(join(tmpdir(), "patchbay-")) });
|
||||
const response = await handler(new Request("http://localhost/healthz"));
|
||||
expect(response.status).toBe(200);
|
||||
expect(await response.text()).toBe("ok\n");
|
||||
});
|
||||
|
||||
test("rejects invalid signatures", async () => {
|
||||
const handler = createHandler({ githubSecret: "gh", jojoSecret: "jojo", dataDir: await mkdtemp(join(tmpdir(), "git-webhooks-")) });
|
||||
const response = await handler(new Request("http://localhost/git-webhooks/github", {
|
||||
const handler = createHandler({ githubSecret: "gh", jojoSecret: "jojo", dataDir: await mkdtemp(join(tmpdir(), "patchbay-")) });
|
||||
const response = await handler(new Request("http://localhost/patchbay/github", {
|
||||
method: "POST",
|
||||
headers: { "x-hub-signature-256": "sha256=bad" },
|
||||
body: "{}",
|
||||
|
|
@ -40,14 +40,14 @@ describe("server", () => {
|
|||
});
|
||||
|
||||
test("accepts jojo main pushes and queues a job", async () => {
|
||||
const dataDir = await mkdtemp(join(tmpdir(), "git-webhooks-"));
|
||||
const dataDir = await mkdtemp(join(tmpdir(), "patchbay-"));
|
||||
const handler = createHandler({ githubSecret: "gh", jojoSecret: "jojo", dataDir });
|
||||
const request = await signedRequest("/git-webhooks/jojo", "jojo", "jojo", {
|
||||
const request = await signedRequest("/patchbay/jojo", "jojo", "jojo", {
|
||||
ref: "refs/heads/main",
|
||||
after: "abc123",
|
||||
repository: {
|
||||
name: "git-webhooks",
|
||||
full_name: "peezy-tech/git-webhooks",
|
||||
name: "patchbay",
|
||||
full_name: "peezy-tech/patchbay",
|
||||
owner: { username: "peezy-tech" },
|
||||
},
|
||||
});
|
||||
|
|
@ -58,12 +58,30 @@ describe("server", () => {
|
|||
expect(await readFile(join(dataDir, "jobs.jsonl"), "utf8")).toContain("\"kind\":\"main_push\"");
|
||||
});
|
||||
|
||||
test("keeps legacy git-webhooks routes as aliases", async () => {
|
||||
const dataDir = await mkdtemp(join(tmpdir(), "patchbay-"));
|
||||
const handler = createHandler({ githubSecret: "gh", jojoSecret: "jojo", dataDir });
|
||||
const request = await signedRequest("/git-webhooks/jojo", "jojo", "jojo", {
|
||||
ref: "refs/heads/main",
|
||||
after: "abc123",
|
||||
repository: {
|
||||
name: "patchbay",
|
||||
full_name: "peezy-tech/patchbay",
|
||||
owner: { username: "peezy-tech" },
|
||||
},
|
||||
});
|
||||
|
||||
const response = await handler(request);
|
||||
expect(response.status).toBe(202);
|
||||
expect(await readFile(join(dataDir, "events.jsonl"), "utf8")).toContain("\"provider\":\"jojo\"");
|
||||
});
|
||||
|
||||
test("continues accepting webhooks when Discord returns an error", async () => {
|
||||
const originalFetch = globalThis.fetch;
|
||||
globalThis.fetch = (async () => new Response("bad", { status: 500 })) as unknown as typeof fetch;
|
||||
|
||||
try {
|
||||
const dataDir = await mkdtemp(join(tmpdir(), "git-webhooks-"));
|
||||
const dataDir = await mkdtemp(join(tmpdir(), "patchbay-"));
|
||||
const handler = createHandler({
|
||||
githubSecret: "gh",
|
||||
jojoSecret: "jojo",
|
||||
|
|
@ -73,12 +91,12 @@ describe("server", () => {
|
|||
notifyEvents: new Set(["push"]),
|
||||
},
|
||||
});
|
||||
const request = await signedRequest("/git-webhooks/jojo", "jojo", "jojo", {
|
||||
const request = await signedRequest("/patchbay/jojo", "jojo", "jojo", {
|
||||
ref: "refs/heads/main",
|
||||
after: "abc123",
|
||||
repository: {
|
||||
name: "git-webhooks",
|
||||
full_name: "peezy-tech/git-webhooks",
|
||||
name: "patchbay",
|
||||
full_name: "peezy-tech/patchbay",
|
||||
owner: { username: "peezy-tech" },
|
||||
},
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue