@usepipr/runtime 0.3.8 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -12
- package/dist/{commands-C5_GHGxN.d.mts → commands-0cyQHxpm.d.mts} +129 -215
- package/dist/commands-0cyQHxpm.d.mts.map +1 -0
- package/dist/{commands-RY37Y0rG.mjs → commands-46Q2ZDt0.mjs} +4251 -3768
- package/dist/commands-46Q2ZDt0.mjs.map +1 -0
- package/dist/index.d.mts +17 -25
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +411 -2
- package/dist/index.mjs.map +1 -0
- package/dist/internal/docs.d.mts +13 -0
- package/dist/internal/docs.d.mts.map +1 -0
- package/dist/internal/docs.mjs +2 -0
- package/dist/internal/testing.d.mts +180 -2
- package/dist/internal/testing.d.mts.map +1 -0
- package/dist/internal/testing.mjs +2 -2
- package/dist/official-github-workflow-Cx-Yhh86.mjs +2753 -0
- package/dist/official-github-workflow-Cx-Yhh86.mjs.map +1 -0
- package/dist/pi/runtime-tools-extension.mjs +1 -1
- package/dist/recipes-w72EpAOi.d.mts +27 -0
- package/dist/recipes-w72EpAOi.d.mts.map +1 -0
- package/dist/{runtime-tools-core-CW1xenzy.mjs → runtime-tools-core-D8_WsbL_.mjs} +9 -4
- package/dist/runtime-tools-core-D8_WsbL_.mjs.map +1 -0
- package/package.json +9 -5
- package/dist/commands-C5_GHGxN.d.mts.map +0 -1
- package/dist/commands-RY37Y0rG.mjs.map +0 -1
- package/dist/runtime-tools-core-CW1xenzy.mjs.map +0 -1
package/dist/index.d.mts
CHANGED
|
@@ -1,28 +1,20 @@
|
|
|
1
|
-
import { A as
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
contents: string;
|
|
6
|
-
};
|
|
7
|
-
type OfficialInitRecipeWorkflowEnvSecret = {
|
|
8
|
-
env: string;
|
|
9
|
-
secret: string;
|
|
10
|
-
};
|
|
11
|
-
type OfficialInitRecipe = {
|
|
12
|
-
id: string;
|
|
13
|
-
title: string;
|
|
14
|
-
description: string;
|
|
15
|
-
sourceTools: readonly string[];
|
|
16
|
-
configTs: string;
|
|
17
|
-
files?: readonly OfficialInitRecipeFile[];
|
|
18
|
-
workflowEnvSecrets?: readonly OfficialInitRecipeWorkflowEnvSecret[];
|
|
19
|
-
docsDetailsMdx?: string;
|
|
20
|
-
};
|
|
1
|
+
import { A as DiffManifest, C as RuntimeLogSink, D as PublicationResult, E as PublicationError, F as RuntimeSettings, I as OfficialInitAdapter, L as supportedOfficialInitAdapters, M as PlatformInfo, N as ProviderConfig, O as ChangeRequestEventContext, P as RepositoryRef, S as RuntimeLogRecord, a as runInspectCommand, c as DryRunCommandOptions, d as HostRunCommandResult, f as InitCommandOptions, g as RuntimeCommandOptions, h as LocalReviewCommandResult, i as runInitCommand, j as PiprConfig, k as ChangeRequestRef, l as DryRunCommandResult, m as LocalReviewCommandOptions, n as runHostRunCommand, o as runLocalReviewCommand, p as InspectCommandResult, s as runValidateCommand, t as runDryRunCommand, u as HostRunCommandOptions } from "./commands-0cyQHxpm.mjs";
|
|
2
|
+
import { a as OfficialInitRecipeFile, i as OfficialInitRecipe, n as listOfficialInitRecipes, r as supportedOfficialInitRecipes, t as OfficialInitRecipeId } from "./recipes-w72EpAOi.mjs";
|
|
3
|
+
//#region src/hosts/webhook.d.ts
|
|
4
|
+
type WebhookHost = "gitlab" | "azure-devops" | "bitbucket";
|
|
21
5
|
//#endregion
|
|
22
|
-
//#region src/
|
|
23
|
-
declare
|
|
24
|
-
|
|
25
|
-
|
|
6
|
+
//#region src/host-run/webhook-server.d.ts
|
|
7
|
+
declare function runWebhookServer(options: {
|
|
8
|
+
host: WebhookHost;
|
|
9
|
+
workspace: string;
|
|
10
|
+
configDir: string;
|
|
11
|
+
databasePath: string;
|
|
12
|
+
expectedRepository: string;
|
|
13
|
+
secret: string;
|
|
14
|
+
port: number;
|
|
15
|
+
hostname?: string;
|
|
16
|
+
env?: NodeJS.ProcessEnv;
|
|
17
|
+
}): Promise<void>;
|
|
26
18
|
//#endregion
|
|
27
|
-
export { type
|
|
19
|
+
export { type ChangeRequestEventContext, type ChangeRequestRef, type DiffManifest, type DryRunCommandOptions, type DryRunCommandResult, type HostRunCommandOptions, type HostRunCommandResult, type InitCommandOptions, type InspectCommandResult, type LocalReviewCommandOptions, type LocalReviewCommandResult, type OfficialInitAdapter, type OfficialInitRecipe, type OfficialInitRecipeFile, type OfficialInitRecipeId, type PiprConfig, type PlatformInfo, type ProviderConfig, PublicationError, type PublicationResult, type RepositoryRef, type RuntimeCommandOptions, type RuntimeLogRecord, type RuntimeLogSink, type RuntimeSettings, type WebhookHost, listOfficialInitRecipes, runDryRunCommand, runHostRunCommand, runInitCommand, runInspectCommand, runLocalReviewCommand, runValidateCommand, runWebhookServer, supportedOfficialInitAdapters, supportedOfficialInitRecipes };
|
|
28
20
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/hosts/webhook.ts","../src/host-run/webhook-server.ts"],"mappings":";;;KAIY;;;iBCwIU,iBAAiB;EACrC,MAAM;EACN;EACA;EACA;EACA;EACA;EACA;EACA;EACA,MAAM,OAAO;IACX"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,411 @@
|
|
|
1
|
-
import { a as runInspectCommand,
|
|
2
|
-
|
|
1
|
+
import { a as runInspectCommand, d as createGitLabClient, f as createBitbucketClient, h as supportedOfficialInitAdapters, i as runInitCommand, l as PublicationError, m as azureOrganizationFromUrl, n as runHostRunCommand, o as runLocalReviewCommand, p as createAzureDevOpsClient, s as runValidateCommand, t as runDryRunCommand } from "./commands-46Q2ZDt0.mjs";
|
|
2
|
+
import { n as listOfficialInitRecipes, o as supportedOfficialInitRecipes } from "./official-github-workflow-Cx-Yhh86.mjs";
|
|
3
|
+
import { mkdir, mkdtemp, rm } from "node:fs/promises";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
import os from "node:os";
|
|
7
|
+
import { Database } from "bun:sqlite";
|
|
8
|
+
import { createHmac, timingSafeEqual } from "node:crypto";
|
|
9
|
+
//#region src/hosts/webhook-shared.ts
|
|
10
|
+
function parseWebhookJson(value) {
|
|
11
|
+
try {
|
|
12
|
+
return JSON.parse(value);
|
|
13
|
+
} catch {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
function webhookSecretsEqual(supplied, expected) {
|
|
18
|
+
if (!supplied) return false;
|
|
19
|
+
const expectedBytes = Buffer.from(expected);
|
|
20
|
+
const suppliedBytes = Buffer.from(supplied);
|
|
21
|
+
return expectedBytes.length === suppliedBytes.length && timingSafeEqual(expectedBytes, suppliedBytes);
|
|
22
|
+
}
|
|
23
|
+
//#endregion
|
|
24
|
+
//#region src/hosts/azure-devops/webhook.ts
|
|
25
|
+
const eventSchema$1 = z.looseObject({
|
|
26
|
+
id: z.string().min(1),
|
|
27
|
+
eventType: z.enum([
|
|
28
|
+
"git.pullrequest.created",
|
|
29
|
+
"git.pullrequest.updated",
|
|
30
|
+
"ms.vss-code.git-pullrequest-comment-event"
|
|
31
|
+
]),
|
|
32
|
+
subscriptionId: z.string().min(1),
|
|
33
|
+
notificationId: z.union([z.number(), z.string()]).transform(String).optional(),
|
|
34
|
+
resource: z.looseObject({
|
|
35
|
+
repository: z.looseObject({
|
|
36
|
+
id: z.string().min(1),
|
|
37
|
+
project: z.looseObject({ id: z.string().min(1) })
|
|
38
|
+
}).optional(),
|
|
39
|
+
pullRequest: z.looseObject({ repository: z.looseObject({
|
|
40
|
+
id: z.string().min(1),
|
|
41
|
+
project: z.looseObject({ id: z.string().min(1) })
|
|
42
|
+
}) }).optional()
|
|
43
|
+
}),
|
|
44
|
+
resourceContainers: z.looseObject({
|
|
45
|
+
account: z.looseObject({ baseUrl: z.string().url() }),
|
|
46
|
+
project: z.looseObject({ id: z.string().min(1) })
|
|
47
|
+
})
|
|
48
|
+
});
|
|
49
|
+
function createAzureDevOpsWebhookProtocol() {
|
|
50
|
+
return {
|
|
51
|
+
host: "azure-devops",
|
|
52
|
+
async resolveExpectedRepository(env, repository) {
|
|
53
|
+
const client = createAzureDevOpsClient(env);
|
|
54
|
+
const resolved = await client.getRepository(repository);
|
|
55
|
+
const subscriptionId = env.PIPR_AZURE_SUBSCRIPTION_ID;
|
|
56
|
+
if (!subscriptionId) throw new Error("PIPR_AZURE_SUBSCRIPTION_ID is required for Azure DevOps webhooks");
|
|
57
|
+
return {
|
|
58
|
+
organization: client.organization,
|
|
59
|
+
projectId: resolved.projectId,
|
|
60
|
+
repositoryId: resolved.id,
|
|
61
|
+
subscriptionId
|
|
62
|
+
};
|
|
63
|
+
},
|
|
64
|
+
verifySecret(headers, secret, _payload) {
|
|
65
|
+
return webhookSecretsEqual(headers.get("X-Pipr-Webhook-Secret") ?? basicPassword(headers.get("Authorization")), secret);
|
|
66
|
+
},
|
|
67
|
+
matchesExpectedRepository(payload, expected) {
|
|
68
|
+
if (!isExpectedRepository$1(expected)) return false;
|
|
69
|
+
const event = eventSchema$1.safeParse(parseWebhookJson(payload));
|
|
70
|
+
if (!event.success) return false;
|
|
71
|
+
const repository = event.data.resource.repository ?? event.data.resource.pullRequest?.repository;
|
|
72
|
+
return azureOrganizationFromUrl(event.data.resourceContainers.account.baseUrl) === expected.organization && event.data.resourceContainers.project.id === expected.projectId && repository?.project.id === expected.projectId && repository.id === expected.repositoryId && event.data.subscriptionId === expected.subscriptionId;
|
|
73
|
+
},
|
|
74
|
+
deliveryId(_headers, payload) {
|
|
75
|
+
const event = eventSchema$1.safeParse(parseWebhookJson(payload));
|
|
76
|
+
return event.success ? `azure-devops:${event.data.subscriptionId}:${event.data.id}:${event.data.notificationId ?? "initial"}` : void 0;
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
function isExpectedRepository$1(value) {
|
|
81
|
+
return typeof value === "object" && value !== null && "organization" in value && "projectId" in value && "repositoryId" in value && "subscriptionId" in value;
|
|
82
|
+
}
|
|
83
|
+
function basicPassword(value) {
|
|
84
|
+
if (!value?.startsWith("Basic ")) return null;
|
|
85
|
+
try {
|
|
86
|
+
return Buffer.from(value.slice(6), "base64").toString().split(":").slice(1).join(":") || null;
|
|
87
|
+
} catch {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
//#endregion
|
|
92
|
+
//#region src/hosts/bitbucket/webhook.ts
|
|
93
|
+
const eventSchema = z.looseObject({
|
|
94
|
+
repository: z.looseObject({
|
|
95
|
+
uuid: z.string().min(1),
|
|
96
|
+
full_name: z.string().min(1)
|
|
97
|
+
}),
|
|
98
|
+
pullrequest: z.looseObject({ id: z.number().int().positive() })
|
|
99
|
+
});
|
|
100
|
+
function createBitbucketWebhookProtocol() {
|
|
101
|
+
return {
|
|
102
|
+
host: "bitbucket",
|
|
103
|
+
async resolveExpectedRepository(env, expectedRepository) {
|
|
104
|
+
const client = createBitbucketClient(env);
|
|
105
|
+
if (expectedRepository !== client.repository) throw new Error(`Bitbucket --repository '${expectedRepository}' does not match BITBUCKET_REPO_SLUG '${client.repository}'`);
|
|
106
|
+
const repository = await client.getRepository();
|
|
107
|
+
return {
|
|
108
|
+
uuid: repository.uuid,
|
|
109
|
+
fullName: repository.fullName
|
|
110
|
+
};
|
|
111
|
+
},
|
|
112
|
+
verifySecret(headers, secret, payload) {
|
|
113
|
+
return verifyBitbucketSignature(payload, headers.get("X-Hub-Signature"), secret);
|
|
114
|
+
},
|
|
115
|
+
matchesExpectedRepository(payload, expected) {
|
|
116
|
+
const event = eventSchema.safeParse(parseWebhookJson(payload));
|
|
117
|
+
return event.success && typeof expected === "object" && expected !== null && "uuid" in expected && "fullName" in expected && event.data.repository.uuid === expected.uuid && event.data.repository.full_name === expected.fullName;
|
|
118
|
+
},
|
|
119
|
+
deliveryId(headers, payload) {
|
|
120
|
+
const request = headers.get("X-Request-UUID");
|
|
121
|
+
const hook = headers.get("X-Hook-UUID");
|
|
122
|
+
if (!request || !hook) return void 0;
|
|
123
|
+
return `bitbucket:${hook}:${request}:${createHmac("sha256", hook).update(payload).digest("hex").slice(0, 16)}`;
|
|
124
|
+
},
|
|
125
|
+
eventName(headers) {
|
|
126
|
+
return headers.get("X-Event-Key") ?? void 0;
|
|
127
|
+
},
|
|
128
|
+
runtimeEnv(eventName) {
|
|
129
|
+
return eventName ? { BITBUCKET_EVENT_KEY: eventName } : {};
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
function verifyBitbucketSignature(payload, signature, secret) {
|
|
134
|
+
if (!signature?.startsWith("sha256=")) return false;
|
|
135
|
+
const supplied = Buffer.from(signature.slice(7), "hex");
|
|
136
|
+
const expected = createHmac("sha256", secret).update(payload).digest();
|
|
137
|
+
return supplied.length === expected.length && timingSafeEqual(supplied, expected);
|
|
138
|
+
}
|
|
139
|
+
//#endregion
|
|
140
|
+
//#region src/hosts/gitlab/webhook.ts
|
|
141
|
+
const projectSchema = z.looseObject({ project: z.looseObject({
|
|
142
|
+
id: z.union([z.number(), z.string()]).transform(String),
|
|
143
|
+
path_with_namespace: z.string().min(1)
|
|
144
|
+
}) });
|
|
145
|
+
function createGitLabWebhookProtocol() {
|
|
146
|
+
return {
|
|
147
|
+
host: "gitlab",
|
|
148
|
+
resolveExpectedRepository: (env, repository) => createGitLabClient(env).getProject(repository),
|
|
149
|
+
verifySecret(headers, secret, _payload) {
|
|
150
|
+
return webhookSecretsEqual(headers.get("X-Gitlab-Token"), secret);
|
|
151
|
+
},
|
|
152
|
+
matchesExpectedRepository(payload, expected) {
|
|
153
|
+
if (!isExpectedRepository(expected)) return false;
|
|
154
|
+
const event = projectSchema.safeParse(parseWebhookJson(payload));
|
|
155
|
+
return event.success && event.data.project.id === expected.id && event.data.project.path_with_namespace === expected.path;
|
|
156
|
+
},
|
|
157
|
+
deliveryId(headers) {
|
|
158
|
+
const id = headers.get("X-Gitlab-Webhook-UUID") ?? headers.get("X-Gitlab-Event-UUID");
|
|
159
|
+
return id ? `gitlab:${id}` : void 0;
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
function isExpectedRepository(value) {
|
|
164
|
+
return typeof value === "object" && value !== null && "id" in value && "path" in value;
|
|
165
|
+
}
|
|
166
|
+
//#endregion
|
|
167
|
+
//#region src/hosts/webhook.ts
|
|
168
|
+
function createCodeHostWebhookProtocol(host) {
|
|
169
|
+
if (host === "gitlab") return createGitLabWebhookProtocol();
|
|
170
|
+
if (host === "azure-devops") return createAzureDevOpsWebhookProtocol();
|
|
171
|
+
if (host === "bitbucket") return createBitbucketWebhookProtocol();
|
|
172
|
+
throw new Error(`Unsupported webhook host: ${host}`);
|
|
173
|
+
}
|
|
174
|
+
//#endregion
|
|
175
|
+
//#region src/host-run/webhook-server.ts
|
|
176
|
+
const MAX_WEBHOOK_PAYLOAD_BYTES = 2 * 1024 * 1024;
|
|
177
|
+
function createWebhookIngress(options) {
|
|
178
|
+
const maxPayloadBytes = options.maxPayloadBytes ?? MAX_WEBHOOK_PAYLOAD_BYTES;
|
|
179
|
+
const protocol = createCodeHostWebhookProtocol(options.host);
|
|
180
|
+
return async (request) => {
|
|
181
|
+
const healthResponse = webhookHealthResponse(request);
|
|
182
|
+
if (healthResponse) return healthResponse;
|
|
183
|
+
const authenticated = await readAuthenticatedWebhookPayload(request, maxPayloadBytes, options.secret, protocol.verifySecret);
|
|
184
|
+
if (authenticated instanceof Response) return authenticated;
|
|
185
|
+
const payload = authenticated;
|
|
186
|
+
if (!protocol.matchesExpectedRepository(payload, options.expectedRepository)) return new Response("Repository mismatch", { status: 403 });
|
|
187
|
+
const id = protocol.deliveryId(request.headers, payload);
|
|
188
|
+
if (!id) return new Response("Missing delivery id", { status: 400 });
|
|
189
|
+
const result = options.store.enqueue({
|
|
190
|
+
id,
|
|
191
|
+
host: options.host,
|
|
192
|
+
payload,
|
|
193
|
+
eventName: protocol.eventName?.(request.headers)
|
|
194
|
+
});
|
|
195
|
+
if (result === "full") return new Response("Queue Full", {
|
|
196
|
+
status: 503,
|
|
197
|
+
headers: { "Retry-After": "30" }
|
|
198
|
+
});
|
|
199
|
+
return new Response(result === "created" ? "Accepted" : "Duplicate", { status: result === "created" ? 202 : 200 });
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
function webhookHealthResponse(request) {
|
|
203
|
+
if (request.method !== "GET" && request.method !== "HEAD") return void 0;
|
|
204
|
+
return new URL(request.url).pathname === "/healthz" ? new Response("OK") : void 0;
|
|
205
|
+
}
|
|
206
|
+
async function readAuthenticatedWebhookPayload(request, maxPayloadBytes, secret, verify) {
|
|
207
|
+
if (request.method !== "POST") return new Response("Method Not Allowed", { status: 405 });
|
|
208
|
+
const payload = await request.text();
|
|
209
|
+
if (new TextEncoder().encode(payload).byteLength > maxPayloadBytes) return new Response("Payload Too Large", { status: 413 });
|
|
210
|
+
return verify(request.headers, secret, payload) ? payload : new Response("Unauthorized", { status: 401 });
|
|
211
|
+
}
|
|
212
|
+
async function processNextWebhookDelivery(options) {
|
|
213
|
+
const delivery = options.store.next();
|
|
214
|
+
if (!delivery) return false;
|
|
215
|
+
try {
|
|
216
|
+
await options.run(delivery);
|
|
217
|
+
options.store.complete(delivery.id);
|
|
218
|
+
options.log?.(`webhook delivery completed: ${delivery.id.slice(0, 200)}`);
|
|
219
|
+
} catch (error) {
|
|
220
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
221
|
+
options.store.fail(delivery.id, message.slice(0, 1e3));
|
|
222
|
+
options.log?.(`webhook delivery failed and was retained for retry or inspection: ${delivery.id.slice(0, 200)}`);
|
|
223
|
+
}
|
|
224
|
+
return true;
|
|
225
|
+
}
|
|
226
|
+
function createWebhookQueueProcessor(options) {
|
|
227
|
+
let active;
|
|
228
|
+
let stopped = false;
|
|
229
|
+
return {
|
|
230
|
+
run() {
|
|
231
|
+
if (stopped) return Promise.resolve();
|
|
232
|
+
active ??= (async () => {
|
|
233
|
+
try {
|
|
234
|
+
while (await processNextWebhookDelivery(options));
|
|
235
|
+
} catch {
|
|
236
|
+
options.log?.("webhook queue processing failed; the next interval will retry");
|
|
237
|
+
}
|
|
238
|
+
})().finally(() => {
|
|
239
|
+
active = void 0;
|
|
240
|
+
});
|
|
241
|
+
return active;
|
|
242
|
+
},
|
|
243
|
+
async stop() {
|
|
244
|
+
stopped = true;
|
|
245
|
+
await active;
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
async function runWebhookServer(options) {
|
|
250
|
+
await mkdir(path.dirname(path.resolve(options.databasePath)), { recursive: true });
|
|
251
|
+
const env = {
|
|
252
|
+
...process.env,
|
|
253
|
+
...options.env
|
|
254
|
+
};
|
|
255
|
+
const expectedRepository = await createCodeHostWebhookProtocol(options.host).resolveExpectedRepository(env, options.expectedRepository);
|
|
256
|
+
const store = new SqliteWebhookDeliveryStore(options.databasePath);
|
|
257
|
+
const ingress = createWebhookIngress({
|
|
258
|
+
host: options.host,
|
|
259
|
+
secret: options.secret,
|
|
260
|
+
expectedRepository,
|
|
261
|
+
store
|
|
262
|
+
});
|
|
263
|
+
const processor = createWebhookQueueProcessor({
|
|
264
|
+
store,
|
|
265
|
+
run: (delivery) => runWebhookDelivery(delivery, {
|
|
266
|
+
...options,
|
|
267
|
+
env
|
|
268
|
+
}),
|
|
269
|
+
log: console.error
|
|
270
|
+
});
|
|
271
|
+
const server = Bun.serve({
|
|
272
|
+
hostname: options.hostname ?? "127.0.0.1",
|
|
273
|
+
port: options.port,
|
|
274
|
+
maxRequestBodySize: MAX_WEBHOOK_PAYLOAD_BYTES,
|
|
275
|
+
fetch: ingress
|
|
276
|
+
});
|
|
277
|
+
const interval = setInterval(() => void processor.run(), 250);
|
|
278
|
+
const shutdown = Promise.withResolvers();
|
|
279
|
+
let stopping;
|
|
280
|
+
const stop = () => {
|
|
281
|
+
stopping ??= (async () => {
|
|
282
|
+
clearInterval(interval);
|
|
283
|
+
server.stop();
|
|
284
|
+
await processor.stop();
|
|
285
|
+
store.close();
|
|
286
|
+
})();
|
|
287
|
+
stopping.then(shutdown.resolve, shutdown.reject);
|
|
288
|
+
};
|
|
289
|
+
process.once("SIGINT", stop);
|
|
290
|
+
process.once("SIGTERM", stop);
|
|
291
|
+
try {
|
|
292
|
+
console.error(`pipr webhook server listening on ${server.hostname}:${server.port}`);
|
|
293
|
+
await processor.run();
|
|
294
|
+
await shutdown.promise;
|
|
295
|
+
} finally {
|
|
296
|
+
process.off("SIGINT", stop);
|
|
297
|
+
process.off("SIGTERM", stop);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
var SqliteWebhookDeliveryStore = class {
|
|
301
|
+
database;
|
|
302
|
+
maxPendingDeliveries;
|
|
303
|
+
maxRetainedPayloadBytes;
|
|
304
|
+
maxRetainedDeliveries;
|
|
305
|
+
constructor(databasePath, options = {}) {
|
|
306
|
+
this.database = new Database(databasePath, {
|
|
307
|
+
create: true,
|
|
308
|
+
strict: true
|
|
309
|
+
});
|
|
310
|
+
this.maxPendingDeliveries = options.maxPendingDeliveries ?? 1e3;
|
|
311
|
+
this.maxRetainedPayloadBytes = options.maxRetainedPayloadBytes ?? 32 * 1024 * 1024;
|
|
312
|
+
this.maxRetainedDeliveries = options.maxRetainedDeliveries ?? 1e4;
|
|
313
|
+
this.database.exec("PRAGMA journal_mode = WAL");
|
|
314
|
+
this.database.transaction(() => {
|
|
315
|
+
this.database.exec(`
|
|
316
|
+
CREATE TABLE IF NOT EXISTS webhook_deliveries (
|
|
317
|
+
id TEXT PRIMARY KEY,
|
|
318
|
+
host TEXT NOT NULL,
|
|
319
|
+
payload TEXT,
|
|
320
|
+
event_name TEXT,
|
|
321
|
+
status TEXT NOT NULL,
|
|
322
|
+
attempts INTEGER NOT NULL DEFAULT 0,
|
|
323
|
+
error TEXT,
|
|
324
|
+
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
325
|
+
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
326
|
+
);
|
|
327
|
+
`);
|
|
328
|
+
if (!this.database.query("PRAGMA table_info(webhook_deliveries)").all().some((column) => column.name === "event_name")) this.database.exec("ALTER TABLE webhook_deliveries ADD COLUMN event_name TEXT");
|
|
329
|
+
this.database.query("UPDATE webhook_deliveries SET status = CASE WHEN attempts < 3 THEN 'pending' ELSE 'failed' END, payload = CASE WHEN attempts < 3 THEN payload ELSE NULL END, error = CASE WHEN attempts < 3 THEN error ELSE COALESCE(error, 'delivery interrupted during final attempt') END, updated_at = CURRENT_TIMESTAMP WHERE status = 'processing'").run();
|
|
330
|
+
})();
|
|
331
|
+
}
|
|
332
|
+
enqueue(delivery) {
|
|
333
|
+
return this.database.transaction(() => {
|
|
334
|
+
if (this.database.query("SELECT id FROM webhook_deliveries WHERE id = ?").get(delivery.id)) return "duplicate";
|
|
335
|
+
const retained = this.database.query("SELECT COALESCE(SUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END), 0) AS count, COALESCE(SUM(length(CAST(payload AS BLOB))), 0) AS bytes FROM webhook_deliveries WHERE payload IS NOT NULL").get();
|
|
336
|
+
const payloadBytes = Buffer.byteLength(delivery.payload);
|
|
337
|
+
if (!retained || retained.count >= this.maxPendingDeliveries || retained.bytes + payloadBytes > this.maxRetainedPayloadBytes) return "full";
|
|
338
|
+
this.database.query("INSERT INTO webhook_deliveries (id, host, payload, event_name, status) VALUES (?, ?, ?, ?, 'pending')").run(delivery.id, delivery.host, delivery.payload, delivery.eventName ?? null);
|
|
339
|
+
return "created";
|
|
340
|
+
})();
|
|
341
|
+
}
|
|
342
|
+
next() {
|
|
343
|
+
return this.database.transaction(() => {
|
|
344
|
+
const row = this.database.query("SELECT id, host, payload, event_name AS eventName FROM webhook_deliveries WHERE status = 'pending' AND attempts < 3 ORDER BY created_at, id LIMIT 1").get();
|
|
345
|
+
if (!row) return void 0;
|
|
346
|
+
this.database.query("UPDATE webhook_deliveries SET status = 'processing', attempts = attempts + 1, updated_at = CURRENT_TIMESTAMP WHERE id = ?").run(row.id);
|
|
347
|
+
return row.eventName ? row : {
|
|
348
|
+
id: row.id,
|
|
349
|
+
host: row.host,
|
|
350
|
+
payload: row.payload
|
|
351
|
+
};
|
|
352
|
+
})();
|
|
353
|
+
}
|
|
354
|
+
complete(id) {
|
|
355
|
+
this.database.transaction(() => {
|
|
356
|
+
this.database.query("UPDATE webhook_deliveries SET status = 'completed', payload = NULL, error = NULL, updated_at = CURRENT_TIMESTAMP WHERE id = ?").run(id);
|
|
357
|
+
this.pruneTerminalDeliveries();
|
|
358
|
+
})();
|
|
359
|
+
}
|
|
360
|
+
fail(id, error) {
|
|
361
|
+
this.database.transaction(() => {
|
|
362
|
+
this.database.query("UPDATE webhook_deliveries SET status = CASE WHEN attempts < 3 THEN 'pending' ELSE 'failed' END, payload = CASE WHEN attempts < 3 THEN payload ELSE NULL END, error = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?").run(error, id);
|
|
363
|
+
this.pruneTerminalDeliveries();
|
|
364
|
+
})();
|
|
365
|
+
}
|
|
366
|
+
close() {
|
|
367
|
+
this.database.close();
|
|
368
|
+
}
|
|
369
|
+
pruneTerminalDeliveries() {
|
|
370
|
+
this.database.query("DELETE FROM webhook_deliveries WHERE id IN (SELECT id FROM webhook_deliveries WHERE status IN ('completed', 'failed') ORDER BY updated_at DESC, id DESC LIMIT -1 OFFSET ?)").run(this.maxRetainedDeliveries);
|
|
371
|
+
}
|
|
372
|
+
};
|
|
373
|
+
async function runWebhookDelivery(delivery, options, runHostRun = runHostRunCommand) {
|
|
374
|
+
const directory = await mkdtemp(path.join(os.tmpdir(), "pipr-webhook-"));
|
|
375
|
+
const eventPath = path.join(directory, "event.json");
|
|
376
|
+
const protocol = createCodeHostWebhookProtocol(delivery.host);
|
|
377
|
+
try {
|
|
378
|
+
await Bun.write(eventPath, delivery.payload);
|
|
379
|
+
await runHostRun({
|
|
380
|
+
rootDir: options.workspace,
|
|
381
|
+
configDir: options.configDir,
|
|
382
|
+
host: delivery.host,
|
|
383
|
+
eventPath,
|
|
384
|
+
env: {
|
|
385
|
+
...process.env,
|
|
386
|
+
...options.env,
|
|
387
|
+
PIPR_CODE_HOST: delivery.host,
|
|
388
|
+
...protocol.runtimeEnv?.(delivery.eventName)
|
|
389
|
+
},
|
|
390
|
+
dryRun: false,
|
|
391
|
+
logSink: consoleRuntimeLogSink
|
|
392
|
+
});
|
|
393
|
+
} finally {
|
|
394
|
+
await rm(directory, {
|
|
395
|
+
recursive: true,
|
|
396
|
+
force: true
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
const consoleRuntimeLogSink = {
|
|
401
|
+
log(record) {
|
|
402
|
+
console.log(JSON.stringify(record));
|
|
403
|
+
},
|
|
404
|
+
async group(_name, run) {
|
|
405
|
+
return await run();
|
|
406
|
+
}
|
|
407
|
+
};
|
|
408
|
+
//#endregion
|
|
409
|
+
export { PublicationError, listOfficialInitRecipes, runDryRunCommand, runHostRunCommand, runInitCommand, runInspectCommand, runLocalReviewCommand, runValidateCommand, runWebhookServer, supportedOfficialInitAdapters, supportedOfficialInitRecipes };
|
|
410
|
+
|
|
411
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["eventSchema","isExpectedRepository"],"sources":["../src/hosts/webhook-shared.ts","../src/hosts/azure-devops/webhook.ts","../src/hosts/bitbucket/webhook.ts","../src/hosts/gitlab/webhook.ts","../src/hosts/webhook.ts","../src/host-run/webhook-server.ts"],"sourcesContent":["import { timingSafeEqual } from \"node:crypto\";\n\nexport function parseWebhookJson(value: string): unknown {\n try {\n return JSON.parse(value);\n } catch {\n return undefined;\n }\n}\n\nexport function webhookSecretsEqual(supplied: string | null, expected: string): boolean {\n if (!supplied) return false;\n const expectedBytes = Buffer.from(expected);\n const suppliedBytes = Buffer.from(supplied);\n return (\n expectedBytes.length === suppliedBytes.length && timingSafeEqual(expectedBytes, suppliedBytes)\n );\n}\n","import { z } from \"zod\";\nimport type { CodeHostWebhookProtocol } from \"../webhook.js\";\nimport { parseWebhookJson, webhookSecretsEqual } from \"../webhook-shared.js\";\nimport { createAzureDevOpsClient } from \"./client.js\";\nimport { azureOrganizationFromUrl } from \"./coordinates.js\";\n\nconst eventSchema = z.looseObject({\n id: z.string().min(1),\n eventType: z.enum([\n \"git.pullrequest.created\",\n \"git.pullrequest.updated\",\n \"ms.vss-code.git-pullrequest-comment-event\",\n ]),\n subscriptionId: z.string().min(1),\n notificationId: z.union([z.number(), z.string()]).transform(String).optional(),\n resource: z.looseObject({\n repository: z\n .looseObject({ id: z.string().min(1), project: z.looseObject({ id: z.string().min(1) }) })\n .optional(),\n pullRequest: z\n .looseObject({\n repository: z.looseObject({\n id: z.string().min(1),\n project: z.looseObject({ id: z.string().min(1) }),\n }),\n })\n .optional(),\n }),\n resourceContainers: z.looseObject({\n account: z.looseObject({ baseUrl: z.string().url() }),\n project: z.looseObject({ id: z.string().min(1) }),\n }),\n});\n\ntype ExpectedRepository = {\n organization: string;\n projectId: string;\n repositoryId: string;\n subscriptionId: string;\n};\n\nexport function createAzureDevOpsWebhookProtocol(): CodeHostWebhookProtocol {\n return {\n host: \"azure-devops\",\n async resolveExpectedRepository(env, repository) {\n const client = createAzureDevOpsClient(env);\n const resolved = await client.getRepository(repository);\n const subscriptionId = env.PIPR_AZURE_SUBSCRIPTION_ID;\n if (!subscriptionId)\n throw new Error(\"PIPR_AZURE_SUBSCRIPTION_ID is required for Azure DevOps webhooks\");\n return {\n organization: client.organization,\n projectId: resolved.projectId,\n repositoryId: resolved.id,\n subscriptionId,\n };\n },\n verifySecret(headers, secret, _payload) {\n return webhookSecretsEqual(\n headers.get(\"X-Pipr-Webhook-Secret\") ?? basicPassword(headers.get(\"Authorization\")),\n secret,\n );\n },\n matchesExpectedRepository(payload, expected) {\n if (!isExpectedRepository(expected)) return false;\n const event = eventSchema.safeParse(parseWebhookJson(payload));\n if (!event.success) return false;\n const repository =\n event.data.resource.repository ?? event.data.resource.pullRequest?.repository;\n return (\n azureOrganizationFromUrl(event.data.resourceContainers.account.baseUrl) ===\n expected.organization &&\n event.data.resourceContainers.project.id === expected.projectId &&\n repository?.project.id === expected.projectId &&\n repository.id === expected.repositoryId &&\n event.data.subscriptionId === expected.subscriptionId\n );\n },\n deliveryId(_headers, payload) {\n const event = eventSchema.safeParse(parseWebhookJson(payload));\n return event.success\n ? `azure-devops:${event.data.subscriptionId}:${event.data.id}:${event.data.notificationId ?? \"initial\"}`\n : undefined;\n },\n };\n}\n\nfunction isExpectedRepository(value: unknown): value is ExpectedRepository {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"organization\" in value &&\n \"projectId\" in value &&\n \"repositoryId\" in value &&\n \"subscriptionId\" in value\n );\n}\n\nfunction basicPassword(value: string | null): string | null {\n if (!value?.startsWith(\"Basic \")) return null;\n try {\n return Buffer.from(value.slice(6), \"base64\").toString().split(\":\").slice(1).join(\":\") || null;\n } catch {\n return null;\n }\n}\n","import { createHmac, timingSafeEqual } from \"node:crypto\";\nimport { z } from \"zod\";\nimport type { CodeHostWebhookProtocol } from \"../webhook.js\";\nimport { parseWebhookJson } from \"../webhook-shared.js\";\nimport { createBitbucketClient } from \"./client.js\";\n\nconst eventSchema = z.looseObject({\n repository: z.looseObject({ uuid: z.string().min(1), full_name: z.string().min(1) }),\n pullrequest: z.looseObject({ id: z.number().int().positive() }),\n});\n\nexport function createBitbucketWebhookProtocol(): CodeHostWebhookProtocol {\n return {\n host: \"bitbucket\",\n async resolveExpectedRepository(env, expectedRepository) {\n const client = createBitbucketClient(env);\n if (expectedRepository !== client.repository)\n throw new Error(\n `Bitbucket --repository '${expectedRepository}' does not match BITBUCKET_REPO_SLUG '${client.repository}'`,\n );\n const repository = await client.getRepository();\n return { uuid: repository.uuid, fullName: repository.fullName };\n },\n verifySecret(headers, secret, payload) {\n return verifyBitbucketSignature(payload, headers.get(\"X-Hub-Signature\"), secret);\n },\n matchesExpectedRepository(payload, expected) {\n const event = eventSchema.safeParse(parseWebhookJson(payload));\n return (\n event.success &&\n typeof expected === \"object\" &&\n expected !== null &&\n \"uuid\" in expected &&\n \"fullName\" in expected &&\n event.data.repository.uuid === expected.uuid &&\n event.data.repository.full_name === expected.fullName\n );\n },\n deliveryId(headers, payload) {\n const request = headers.get(\"X-Request-UUID\");\n const hook = headers.get(\"X-Hook-UUID\");\n if (!request || !hook) return undefined;\n const digest = createHmac(\"sha256\", hook).update(payload).digest(\"hex\").slice(0, 16);\n return `bitbucket:${hook}:${request}:${digest}`;\n },\n eventName(headers) {\n return headers.get(\"X-Event-Key\") ?? undefined;\n },\n runtimeEnv(eventName) {\n return eventName ? { BITBUCKET_EVENT_KEY: eventName } : {};\n },\n };\n}\n\nfunction verifyBitbucketSignature(payload: string, signature: string | null, secret: string) {\n if (!signature?.startsWith(\"sha256=\")) return false;\n const supplied = Buffer.from(signature.slice(7), \"hex\");\n const expected = createHmac(\"sha256\", secret).update(payload).digest();\n return supplied.length === expected.length && timingSafeEqual(supplied, expected);\n}\n","import { z } from \"zod\";\nimport type { CodeHostWebhookProtocol } from \"../webhook.js\";\nimport { parseWebhookJson, webhookSecretsEqual } from \"../webhook-shared.js\";\nimport { createGitLabClient } from \"./client.js\";\n\nconst projectSchema = z.looseObject({\n project: z.looseObject({\n id: z.union([z.number(), z.string()]).transform(String),\n path_with_namespace: z.string().min(1),\n }),\n});\n\ntype ExpectedRepository = { id: string; path: string };\n\nexport function createGitLabWebhookProtocol(): CodeHostWebhookProtocol {\n return {\n host: \"gitlab\",\n resolveExpectedRepository: (env, repository) => createGitLabClient(env).getProject(repository),\n verifySecret(headers, secret, _payload) {\n return webhookSecretsEqual(headers.get(\"X-Gitlab-Token\"), secret);\n },\n matchesExpectedRepository(payload, expected) {\n if (!isExpectedRepository(expected)) return false;\n const event = projectSchema.safeParse(parseWebhookJson(payload));\n return (\n event.success &&\n event.data.project.id === expected.id &&\n event.data.project.path_with_namespace === expected.path\n );\n },\n deliveryId(headers) {\n const id = headers.get(\"X-Gitlab-Webhook-UUID\") ?? headers.get(\"X-Gitlab-Event-UUID\");\n return id ? `gitlab:${id}` : undefined;\n },\n };\n}\n\nfunction isExpectedRepository(value: unknown): value is ExpectedRepository {\n return typeof value === \"object\" && value !== null && \"id\" in value && \"path\" in value;\n}\n","import { createAzureDevOpsWebhookProtocol } from \"./azure-devops/webhook.js\";\nimport { createBitbucketWebhookProtocol } from \"./bitbucket/webhook.js\";\nimport { createGitLabWebhookProtocol } from \"./gitlab/webhook.js\";\n\nexport type WebhookHost = \"gitlab\" | \"azure-devops\" | \"bitbucket\";\n\nexport type CodeHostWebhookProtocol = {\n host: WebhookHost;\n resolveExpectedRepository(env: NodeJS.ProcessEnv, repository: string): Promise<unknown>;\n verifySecret(headers: Headers, secret: string, payload: string): boolean;\n matchesExpectedRepository(payload: string, expected: unknown): boolean;\n deliveryId(headers: Headers, payload: string): string | undefined;\n eventName?(headers: Headers): string | undefined;\n runtimeEnv?(eventName: string | undefined): NodeJS.ProcessEnv;\n};\n\nexport function createCodeHostWebhookProtocol(host: WebhookHost): CodeHostWebhookProtocol {\n if (host === \"gitlab\") return createGitLabWebhookProtocol();\n if (host === \"azure-devops\") return createAzureDevOpsWebhookProtocol();\n if (host === \"bitbucket\") return createBitbucketWebhookProtocol();\n throw new Error(`Unsupported webhook host: ${host}`);\n}\n","import { Database } from \"bun:sqlite\";\nimport { mkdir, mkdtemp, rm } from \"node:fs/promises\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { createCodeHostWebhookProtocol, type WebhookHost } from \"../hosts/webhook.js\";\nimport type { RuntimeLogSink } from \"../shared/logging.js\";\nimport { runHostRunCommand } from \"./commands.js\";\n\nconst MAX_WEBHOOK_PAYLOAD_BYTES = 2 * 1024 * 1024;\n\nexport type { WebhookHost } from \"../hosts/webhook.js\";\n\nexport type WebhookDelivery = {\n id: string;\n host: WebhookHost;\n payload: string;\n eventName?: string;\n};\n\nexport type WebhookDeliveryStore = {\n enqueue(delivery: WebhookDelivery): \"created\" | \"duplicate\" | \"full\";\n next(): WebhookDelivery | undefined;\n complete(id: string): void;\n fail(id: string, error: string): void;\n};\n\nexport function createWebhookIngress(options: {\n host: WebhookHost;\n secret: string;\n expectedRepository: unknown;\n store: WebhookDeliveryStore;\n maxPayloadBytes?: number;\n}) {\n const maxPayloadBytes = options.maxPayloadBytes ?? MAX_WEBHOOK_PAYLOAD_BYTES;\n const protocol = createCodeHostWebhookProtocol(options.host);\n return async (request: Request): Promise<Response> => {\n const healthResponse = webhookHealthResponse(request);\n if (healthResponse) return healthResponse;\n const authenticated = await readAuthenticatedWebhookPayload(\n request,\n maxPayloadBytes,\n options.secret,\n protocol.verifySecret,\n );\n if (authenticated instanceof Response) return authenticated;\n const payload = authenticated;\n if (!protocol.matchesExpectedRepository(payload, options.expectedRepository)) {\n return new Response(\"Repository mismatch\", { status: 403 });\n }\n const id = protocol.deliveryId(request.headers, payload);\n if (!id) {\n return new Response(\"Missing delivery id\", { status: 400 });\n }\n const result = options.store.enqueue({\n id,\n host: options.host,\n payload,\n eventName: protocol.eventName?.(request.headers),\n });\n if (result === \"full\") {\n return new Response(\"Queue Full\", { status: 503, headers: { \"Retry-After\": \"30\" } });\n }\n return new Response(result === \"created\" ? \"Accepted\" : \"Duplicate\", {\n status: result === \"created\" ? 202 : 200,\n });\n };\n}\n\nfunction webhookHealthResponse(request: Request): Response | undefined {\n if (request.method !== \"GET\" && request.method !== \"HEAD\") return undefined;\n return new URL(request.url).pathname === \"/healthz\" ? new Response(\"OK\") : undefined;\n}\n\nasync function readAuthenticatedWebhookPayload(\n request: Request,\n maxPayloadBytes: number,\n secret: string,\n verify: (headers: Headers, secret: string, payload: string) => boolean,\n): Promise<string | Response> {\n if (request.method !== \"POST\") return new Response(\"Method Not Allowed\", { status: 405 });\n const payload = await request.text();\n if (new TextEncoder().encode(payload).byteLength > maxPayloadBytes)\n return new Response(\"Payload Too Large\", { status: 413 });\n return verify(request.headers, secret, payload)\n ? payload\n : new Response(\"Unauthorized\", { status: 401 });\n}\n\nexport async function processNextWebhookDelivery(options: {\n store: WebhookDeliveryStore;\n run: (delivery: WebhookDelivery) => Promise<unknown>;\n log?: (message: string) => void;\n}): Promise<boolean> {\n const delivery = options.store.next();\n if (!delivery) return false;\n try {\n await options.run(delivery);\n options.store.complete(delivery.id);\n options.log?.(`webhook delivery completed: ${delivery.id.slice(0, 200)}`);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n options.store.fail(delivery.id, message.slice(0, 1_000));\n options.log?.(\n `webhook delivery failed and was retained for retry or inspection: ${delivery.id.slice(0, 200)}`,\n );\n }\n return true;\n}\n\nexport function createWebhookQueueProcessor(options: {\n store: WebhookDeliveryStore;\n run: (delivery: WebhookDelivery) => Promise<unknown>;\n log?: (message: string) => void;\n}) {\n let active: Promise<void> | undefined;\n let stopped = false;\n\n return {\n run(): Promise<void> {\n if (stopped) return Promise.resolve();\n active ??= (async () => {\n try {\n while (await processNextWebhookDelivery(options)) {\n // Deliveries run serially because each checkout mutates the shared workspace.\n }\n } catch {\n options.log?.(\"webhook queue processing failed; the next interval will retry\");\n }\n })().finally(() => {\n active = undefined;\n });\n return active;\n },\n async stop(): Promise<void> {\n stopped = true;\n await active;\n },\n };\n}\n\nexport async function runWebhookServer(options: {\n host: WebhookHost;\n workspace: string;\n configDir: string;\n databasePath: string;\n expectedRepository: string;\n secret: string;\n port: number;\n hostname?: string;\n env?: NodeJS.ProcessEnv;\n}): Promise<void> {\n await mkdir(path.dirname(path.resolve(options.databasePath)), { recursive: true });\n const env = { ...process.env, ...options.env };\n const protocol = createCodeHostWebhookProtocol(options.host);\n const expectedRepository = await protocol.resolveExpectedRepository(\n env,\n options.expectedRepository,\n );\n const store = new SqliteWebhookDeliveryStore(options.databasePath);\n const ingress = createWebhookIngress({\n host: options.host,\n secret: options.secret,\n expectedRepository,\n store,\n });\n const processor = createWebhookQueueProcessor({\n store,\n run: (delivery) => runWebhookDelivery(delivery, { ...options, env }),\n log: console.error,\n });\n const server = Bun.serve({\n hostname: options.hostname ?? \"127.0.0.1\",\n port: options.port,\n maxRequestBodySize: MAX_WEBHOOK_PAYLOAD_BYTES,\n fetch: ingress,\n });\n const interval = setInterval(() => void processor.run(), 250);\n const shutdown = Promise.withResolvers<void>();\n let stopping: Promise<void> | undefined;\n const stop = () => {\n stopping ??= (async () => {\n clearInterval(interval);\n server.stop();\n await processor.stop();\n store.close();\n })();\n void stopping.then(shutdown.resolve, shutdown.reject);\n };\n process.once(\"SIGINT\", stop);\n process.once(\"SIGTERM\", stop);\n try {\n console.error(`pipr webhook server listening on ${server.hostname}:${server.port}`);\n await processor.run();\n await shutdown.promise;\n } finally {\n process.off(\"SIGINT\", stop);\n process.off(\"SIGTERM\", stop);\n }\n}\n\nexport class SqliteWebhookDeliveryStore implements WebhookDeliveryStore {\n private readonly database: Database;\n private readonly maxPendingDeliveries: number;\n private readonly maxRetainedPayloadBytes: number;\n private readonly maxRetainedDeliveries: number;\n\n constructor(\n databasePath: string,\n options: {\n maxPendingDeliveries?: number;\n maxRetainedPayloadBytes?: number;\n maxRetainedDeliveries?: number;\n } = {},\n ) {\n this.database = new Database(databasePath, { create: true, strict: true });\n this.maxPendingDeliveries = options.maxPendingDeliveries ?? 1_000;\n this.maxRetainedPayloadBytes = options.maxRetainedPayloadBytes ?? 32 * 1024 * 1024;\n this.maxRetainedDeliveries = options.maxRetainedDeliveries ?? 10_000;\n this.database.exec(\"PRAGMA journal_mode = WAL\");\n this.database.transaction(() => {\n this.database.exec(`\n CREATE TABLE IF NOT EXISTS webhook_deliveries (\n id TEXT PRIMARY KEY,\n host TEXT NOT NULL,\n payload TEXT,\n event_name TEXT,\n status TEXT NOT NULL,\n attempts INTEGER NOT NULL DEFAULT 0,\n error TEXT,\n created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,\n updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP\n );\n `);\n const columns = this.database\n .query<{ name: string }, []>(\"PRAGMA table_info(webhook_deliveries)\")\n .all();\n if (!columns.some((column) => column.name === \"event_name\")) {\n this.database.exec(\"ALTER TABLE webhook_deliveries ADD COLUMN event_name TEXT\");\n }\n this.database\n .query(\n \"UPDATE webhook_deliveries SET status = CASE WHEN attempts < 3 THEN 'pending' ELSE 'failed' END, payload = CASE WHEN attempts < 3 THEN payload ELSE NULL END, error = CASE WHEN attempts < 3 THEN error ELSE COALESCE(error, 'delivery interrupted during final attempt') END, updated_at = CURRENT_TIMESTAMP WHERE status = 'processing'\",\n )\n .run();\n })();\n }\n\n enqueue(delivery: WebhookDelivery): \"created\" | \"duplicate\" | \"full\" {\n return this.database.transaction(() => {\n const existing = this.database\n .query<{ id: string }, [string]>(\"SELECT id FROM webhook_deliveries WHERE id = ?\")\n .get(delivery.id);\n if (existing) return \"duplicate\";\n const retained = this.database\n .query<{ count: number; bytes: number }, []>(\n \"SELECT COALESCE(SUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END), 0) AS count, COALESCE(SUM(length(CAST(payload AS BLOB))), 0) AS bytes FROM webhook_deliveries WHERE payload IS NOT NULL\",\n )\n .get();\n const payloadBytes = Buffer.byteLength(delivery.payload);\n if (\n !retained ||\n retained.count >= this.maxPendingDeliveries ||\n retained.bytes + payloadBytes > this.maxRetainedPayloadBytes\n ) {\n return \"full\";\n }\n this.database\n .query(\n \"INSERT INTO webhook_deliveries (id, host, payload, event_name, status) VALUES (?, ?, ?, ?, 'pending')\",\n )\n .run(delivery.id, delivery.host, delivery.payload, delivery.eventName ?? null);\n return \"created\";\n })();\n }\n\n next(): WebhookDelivery | undefined {\n return this.database.transaction(() => {\n const row = this.database\n .query<{ id: string; host: WebhookHost; payload: string; eventName?: string }, []>(\n \"SELECT id, host, payload, event_name AS eventName FROM webhook_deliveries WHERE status = 'pending' AND attempts < 3 ORDER BY created_at, id LIMIT 1\",\n )\n .get();\n if (!row) return undefined;\n this.database\n .query(\n \"UPDATE webhook_deliveries SET status = 'processing', attempts = attempts + 1, updated_at = CURRENT_TIMESTAMP WHERE id = ?\",\n )\n .run(row.id);\n return row.eventName ? row : { id: row.id, host: row.host, payload: row.payload };\n })();\n }\n\n complete(id: string): void {\n this.database.transaction(() => {\n this.database\n .query(\n \"UPDATE webhook_deliveries SET status = 'completed', payload = NULL, error = NULL, updated_at = CURRENT_TIMESTAMP WHERE id = ?\",\n )\n .run(id);\n this.pruneTerminalDeliveries();\n })();\n }\n\n fail(id: string, error: string): void {\n this.database.transaction(() => {\n this.database\n .query(\n \"UPDATE webhook_deliveries SET status = CASE WHEN attempts < 3 THEN 'pending' ELSE 'failed' END, payload = CASE WHEN attempts < 3 THEN payload ELSE NULL END, error = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?\",\n )\n .run(error, id);\n this.pruneTerminalDeliveries();\n })();\n }\n\n close(): void {\n this.database.close();\n }\n\n private pruneTerminalDeliveries(): void {\n // SQLite uses LIMIT -1 to leave the result unbounded after skipping the retained rows.\n this.database\n .query(\n \"DELETE FROM webhook_deliveries WHERE id IN (SELECT id FROM webhook_deliveries WHERE status IN ('completed', 'failed') ORDER BY updated_at DESC, id DESC LIMIT -1 OFFSET ?)\",\n )\n .run(this.maxRetainedDeliveries);\n }\n}\n\nexport async function runWebhookDelivery(\n delivery: WebhookDelivery,\n options: {\n workspace: string;\n configDir: string;\n env?: NodeJS.ProcessEnv;\n },\n runHostRun: typeof runHostRunCommand = runHostRunCommand,\n): Promise<void> {\n const directory = await mkdtemp(path.join(os.tmpdir(), \"pipr-webhook-\"));\n const eventPath = path.join(directory, \"event.json\");\n const protocol = createCodeHostWebhookProtocol(delivery.host);\n try {\n await Bun.write(eventPath, delivery.payload);\n await runHostRun({\n rootDir: options.workspace,\n configDir: options.configDir,\n host: delivery.host,\n eventPath,\n env: {\n ...process.env,\n ...options.env,\n PIPR_CODE_HOST: delivery.host,\n ...protocol.runtimeEnv?.(delivery.eventName),\n },\n dryRun: false,\n logSink: consoleRuntimeLogSink,\n });\n } finally {\n await rm(directory, { recursive: true, force: true });\n }\n}\n\nconst consoleRuntimeLogSink: RuntimeLogSink = {\n log(record) {\n console.log(JSON.stringify(record));\n },\n async group(_name, run) {\n return await run();\n },\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { n as listOfficialInitRecipes } from "../recipes-w72EpAOi.mjs";
|
|
2
|
+
//#region src/config/official-github-workflow.d.ts
|
|
3
|
+
type RenderOfficialGithubWorkflowOptions = {
|
|
4
|
+
relativeConfigDir?: string;
|
|
5
|
+
recipe?: string;
|
|
6
|
+
minimal?: boolean;
|
|
7
|
+
includeReleasePleaseVersionMarker?: boolean;
|
|
8
|
+
};
|
|
9
|
+
/** Internal shared renderer for `pipr init` and generated recipe docs. */
|
|
10
|
+
declare function renderOfficialGithubWorkflow(options?: RenderOfficialGithubWorkflowOptions): string;
|
|
11
|
+
//#endregion
|
|
12
|
+
export { listOfficialInitRecipes, renderOfficialGithubWorkflow };
|
|
13
|
+
//# sourceMappingURL=docs.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docs.d.mts","names":[],"sources":["../../src/config/official-github-workflow.ts"],"mappings":";;KAIY;EACV;EACA;EACA;EACA;;;iBAIc,6BACd,UAAS"}
|