@shipfox/api-integration-github 11.0.0 → 12.2.0
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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +34 -0
- package/dist/api/shared-installation-token-cache.d.ts.map +1 -1
- package/dist/core/agent-tools.d.ts.map +1 -1
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/source-control.d.ts +2 -1
- package/dist/core/source-control.d.ts.map +1 -1
- package/dist/core/source-control.js +56 -1
- package/dist/core/source-control.js.map +1 -1
- package/dist/core/webhook-processor.d.ts +2 -1
- package/dist/core/webhook-processor.d.ts.map +1 -1
- package/dist/core/webhook-processor.js +1 -0
- package/dist/core/webhook-processor.js.map +1 -1
- package/dist/core/webhook.d.ts +3 -2
- package/dist/core/webhook.d.ts.map +1 -1
- package/dist/core/webhook.js +103 -1
- package/dist/core/webhook.js.map +1 -1
- package/dist/db/db.d.ts +2 -2
- package/dist/db/schema/installations.d.ts +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/presentation/routes/webhooks.d.ts +2 -1
- package/dist/presentation/routes/webhooks.d.ts.map +1 -1
- package/dist/presentation/routes/webhooks.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +8 -8
- package/src/connection-external-url.test.ts +1 -0
- package/src/core/agent-tools.test.ts +1 -0
- package/src/core/source-control.test.ts +80 -0
- package/src/core/source-control.ts +72 -0
- package/src/core/webhook-processor.test.ts +4 -0
- package/src/core/webhook-processor.ts +3 -0
- package/src/core/webhook.test.ts +197 -2
- package/src/core/webhook.ts +150 -0
- package/src/index.test.ts +1 -0
- package/src/index.ts +3 -0
- package/src/presentation/routes/install.test.ts +2 -1
- package/src/presentation/routes/webhooks.test.ts +49 -1
- package/src/presentation/routes/webhooks.ts +2 -0
- package/tsconfig.build.tsbuildinfo +1 -1
package/src/core/webhook.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type GithubPushPayloadDto,
|
|
3
|
+
githubInstallationRepositoriesPayloadSchema,
|
|
3
4
|
githubPushPayloadSchema,
|
|
5
|
+
githubRepositoryRenamedPayloadSchema,
|
|
4
6
|
githubWebhookActionSchema,
|
|
5
7
|
githubWebhookInstallationSchema,
|
|
6
8
|
} from '@shipfox/api-integration-github-dto';
|
|
@@ -10,8 +12,10 @@ import {
|
|
|
10
12
|
type IntegrationTx,
|
|
11
13
|
type PublishIntegrationEventReceivedFn,
|
|
12
14
|
type PublishSourcePushFn,
|
|
15
|
+
type PublishSourceRepositoryUpdatedFn,
|
|
13
16
|
type RecordDeliveryOnlyFn,
|
|
14
17
|
type SourcePushPayload,
|
|
18
|
+
type SourceRepositoryIdentity,
|
|
15
19
|
} from '@shipfox/api-integration-spi';
|
|
16
20
|
import {logger} from '@shipfox/node-opentelemetry';
|
|
17
21
|
import {getGithubInstallationByInstallationId} from '#db/installations.js';
|
|
@@ -27,6 +31,7 @@ export interface HandleGithubEventParams {
|
|
|
27
31
|
event: string;
|
|
28
32
|
payload: unknown;
|
|
29
33
|
publishIntegrationEventReceived: PublishIntegrationEventReceivedFn;
|
|
34
|
+
publishSourceRepositoryUpdated: PublishSourceRepositoryUpdatedFn;
|
|
30
35
|
publishSourcePush: PublishSourcePushFn;
|
|
31
36
|
recordDeliveryOnly: RecordDeliveryOnlyFn;
|
|
32
37
|
getIntegrationConnectionById: GetIntegrationConnectionByIdFn;
|
|
@@ -39,6 +44,7 @@ export type HandleGithubEventOutcome =
|
|
|
39
44
|
| 'duplicate-envelope'
|
|
40
45
|
| 'published-push-envelope-only'
|
|
41
46
|
| 'duplicate-push-envelope-only'
|
|
47
|
+
| 'fork-pull-request'
|
|
42
48
|
| 'unknown-installation'
|
|
43
49
|
| 'missing-connection'
|
|
44
50
|
| 'inactive-connection'
|
|
@@ -53,11 +59,88 @@ function isBranchDeletion(after: string): boolean {
|
|
|
53
59
|
return after === DELETED_BRANCH_SHA;
|
|
54
60
|
}
|
|
55
61
|
|
|
62
|
+
interface GithubRepositoryIdentity {
|
|
63
|
+
id?: string;
|
|
64
|
+
fullName?: string;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
68
|
+
return typeof value === 'object' && value !== null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function repositoryIdentity(value: unknown): GithubRepositoryIdentity | undefined {
|
|
72
|
+
if (!isRecord(value)) return undefined;
|
|
73
|
+
|
|
74
|
+
const id =
|
|
75
|
+
(typeof value.id === 'number' && Number.isInteger(value.id) ? String(value.id) : undefined) ??
|
|
76
|
+
(typeof value.id === 'string' && value.id.trim() ? value.id.trim() : undefined);
|
|
77
|
+
const fullName =
|
|
78
|
+
typeof value.full_name === 'string' && value.full_name.trim()
|
|
79
|
+
? value.full_name.trim().toLowerCase()
|
|
80
|
+
: undefined;
|
|
81
|
+
if (!id && !fullName) return undefined;
|
|
82
|
+
|
|
83
|
+
return {...(id ? {id} : {}), ...(fullName ? {fullName} : {})};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function areSameRepositories(
|
|
87
|
+
head: GithubRepositoryIdentity,
|
|
88
|
+
base: GithubRepositoryIdentity,
|
|
89
|
+
): boolean {
|
|
90
|
+
if (head.id && base.id) return head.id === base.id;
|
|
91
|
+
if (head.fullName && base.fullName) return head.fullName === base.fullName;
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function pullRequestRepositories(
|
|
96
|
+
payload: unknown,
|
|
97
|
+
):
|
|
98
|
+
| {head: GithubRepositoryIdentity | undefined; base: GithubRepositoryIdentity | undefined}
|
|
99
|
+
| undefined {
|
|
100
|
+
if (!isRecord(payload) || !isRecord(payload.pull_request)) return undefined;
|
|
101
|
+
|
|
102
|
+
const pullRequest = payload.pull_request;
|
|
103
|
+
const head = isRecord(pullRequest.head) ? repositoryIdentity(pullRequest.head.repo) : undefined;
|
|
104
|
+
const base =
|
|
105
|
+
(isRecord(pullRequest.base) ? repositoryIdentity(pullRequest.base.repo) : undefined) ??
|
|
106
|
+
repositoryIdentity(payload.repository);
|
|
107
|
+
return {head, base};
|
|
108
|
+
}
|
|
109
|
+
|
|
56
110
|
export async function handleGithubEvent(
|
|
57
111
|
params: HandleGithubEventParams,
|
|
58
112
|
): Promise<HandleGithubEventResult> {
|
|
59
113
|
const actionEnvelope = githubWebhookActionSchema.safeParse(params.payload);
|
|
60
114
|
const action = actionEnvelope.success ? actionEnvelope.data.action : undefined;
|
|
115
|
+
|
|
116
|
+
const pullRequestRepos = pullRequestRepositories(params.payload);
|
|
117
|
+
if (
|
|
118
|
+
pullRequestRepos &&
|
|
119
|
+
(!pullRequestRepos.head ||
|
|
120
|
+
!pullRequestRepos.base ||
|
|
121
|
+
!areSameRepositories(pullRequestRepos.head, pullRequestRepos.base))
|
|
122
|
+
) {
|
|
123
|
+
logger().info(
|
|
124
|
+
{
|
|
125
|
+
deliveryId: params.deliveryId,
|
|
126
|
+
event: params.event,
|
|
127
|
+
reason:
|
|
128
|
+
!pullRequestRepos.head || !pullRequestRepos.base
|
|
129
|
+
? 'repository_unresolved'
|
|
130
|
+
: 'repositories_differ',
|
|
131
|
+
headRepository: pullRequestRepos.head,
|
|
132
|
+
baseRepository: pullRequestRepos.base,
|
|
133
|
+
},
|
|
134
|
+
'github webhook: fork or indeterminate pull request, dropping',
|
|
135
|
+
);
|
|
136
|
+
await params.recordDeliveryOnly({
|
|
137
|
+
tx: params.tx,
|
|
138
|
+
provider: GITHUB_SOURCE,
|
|
139
|
+
deliveryId: params.deliveryId,
|
|
140
|
+
});
|
|
141
|
+
return {outcome: 'fork-pull-request'};
|
|
142
|
+
}
|
|
143
|
+
|
|
61
144
|
const installationEnvelope = githubWebhookInstallationSchema.safeParse(params.payload);
|
|
62
145
|
const installationId = installationEnvelope.success
|
|
63
146
|
? installationEnvelope.data.installation?.id
|
|
@@ -157,6 +240,30 @@ export async function handleGithubEvent(
|
|
|
157
240
|
}
|
|
158
241
|
|
|
159
242
|
const eventName = action ? `${params.event}.${action}` : params.event;
|
|
243
|
+
const repositories = normalizeRepositoryUpdates(params.event, action, params.payload);
|
|
244
|
+
if (repositories) {
|
|
245
|
+
const result = await params.publishSourceRepositoryUpdated({
|
|
246
|
+
tx: params.tx,
|
|
247
|
+
provider: GITHUB_SOURCE,
|
|
248
|
+
source: connection.slug,
|
|
249
|
+
workspaceId: connection.workspaceId,
|
|
250
|
+
connectionId: connection.id,
|
|
251
|
+
connectionName: connection.displayName,
|
|
252
|
+
deliveryId: params.deliveryId,
|
|
253
|
+
receivedAt: new Date().toISOString(),
|
|
254
|
+
rawPayload: params.payload,
|
|
255
|
+
event: eventName,
|
|
256
|
+
repositories,
|
|
257
|
+
});
|
|
258
|
+
return withInstallationTokenCleanup(
|
|
259
|
+
{outcome: result.published ? 'published' : 'duplicate'},
|
|
260
|
+
params.event,
|
|
261
|
+
action,
|
|
262
|
+
connection.workspaceId,
|
|
263
|
+
installationId,
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
|
|
160
267
|
const result = await publishGithubEnvelopeOnly({
|
|
161
268
|
tx: params.tx,
|
|
162
269
|
deliveryId: params.deliveryId,
|
|
@@ -174,6 +281,49 @@ export async function handleGithubEvent(
|
|
|
174
281
|
);
|
|
175
282
|
}
|
|
176
283
|
|
|
284
|
+
function normalizeRepositoryUpdates(
|
|
285
|
+
event: string,
|
|
286
|
+
action: string | undefined,
|
|
287
|
+
payload: unknown,
|
|
288
|
+
): SourceRepositoryIdentity[] | undefined {
|
|
289
|
+
if (event === 'repository' && action === 'renamed') {
|
|
290
|
+
const parsed = githubRepositoryRenamedPayloadSchema.safeParse(payload);
|
|
291
|
+
if (!parsed.success) return undefined;
|
|
292
|
+
return [toSourceRepositoryIdentity(parsed.data.repository)];
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (event !== 'installation_repositories' || (action !== 'added' && action !== 'removed')) {
|
|
296
|
+
return undefined;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const parsed = githubInstallationRepositoriesPayloadSchema.safeParse(payload);
|
|
300
|
+
if (!parsed.success) return undefined;
|
|
301
|
+
|
|
302
|
+
const repositories = new Map<number, SourceRepositoryIdentity>();
|
|
303
|
+
for (const repository of [
|
|
304
|
+
...parsed.data.repositories_added,
|
|
305
|
+
...parsed.data.repositories_removed,
|
|
306
|
+
]) {
|
|
307
|
+
const normalized = toSourceRepositoryIdentity(repository);
|
|
308
|
+
repositories.set(repository.id, normalized);
|
|
309
|
+
}
|
|
310
|
+
return repositories.size > 0 ? [...repositories.values()] : undefined;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function toSourceRepositoryIdentity(repository: {
|
|
314
|
+
id: number;
|
|
315
|
+
name: string;
|
|
316
|
+
owner: {login: string};
|
|
317
|
+
default_branch: string;
|
|
318
|
+
}): SourceRepositoryIdentity {
|
|
319
|
+
return {
|
|
320
|
+
externalRepositoryId: buildProviderRepositoryId(GITHUB_SOURCE, String(repository.id)),
|
|
321
|
+
owner: repository.owner.login,
|
|
322
|
+
name: repository.name,
|
|
323
|
+
defaultBranch: repository.default_branch,
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
|
|
177
327
|
function shouldDeleteInstallationTokenSecret(event: string, action: string | undefined): boolean {
|
|
178
328
|
return event === 'installation' && (action === 'deleted' || action === 'suspend');
|
|
179
329
|
}
|
package/src/index.test.ts
CHANGED
|
@@ -25,6 +25,7 @@ describe('createGithubIntegrationProvider', () => {
|
|
|
25
25
|
connectGithubInstallation: vi.fn() as never,
|
|
26
26
|
coreDb: vi.fn() as never,
|
|
27
27
|
publishIntegrationEventReceived: vi.fn(() => Promise.resolve({published: false})),
|
|
28
|
+
publishSourceRepositoryUpdated: vi.fn(() => Promise.resolve({published: false})),
|
|
28
29
|
publishSourcePush: vi.fn(() => Promise.resolve({published: false})),
|
|
29
30
|
recordDeliveryOnly: vi.fn(() => Promise.resolve()),
|
|
30
31
|
getIntegrationConnectionById: vi.fn(() => Promise.resolve(undefined)),
|
package/src/index.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type {
|
|
|
2
2
|
GetIntegrationConnectionByIdFn,
|
|
3
3
|
PublishIntegrationEventReceivedFn,
|
|
4
4
|
PublishSourcePushFn,
|
|
5
|
+
PublishSourceRepositoryUpdatedFn,
|
|
5
6
|
RecordDeliveryOnlyFn,
|
|
6
7
|
} from '@shipfox/api-integration-spi';
|
|
7
8
|
import type {NodePgDatabase} from 'drizzle-orm/node-postgres';
|
|
@@ -69,6 +70,7 @@ export interface CreateGithubIntegrationProviderOptions
|
|
|
69
70
|
github?: GithubApiClient | undefined;
|
|
70
71
|
coreDb: () => NodePgDatabase<Record<string, unknown>>;
|
|
71
72
|
publishIntegrationEventReceived: PublishIntegrationEventReceivedFn;
|
|
73
|
+
publishSourceRepositoryUpdated: PublishSourceRepositoryUpdatedFn;
|
|
72
74
|
publishSourcePush: PublishSourcePushFn;
|
|
73
75
|
recordDeliveryOnly: RecordDeliveryOnlyFn;
|
|
74
76
|
getIntegrationConnectionById: GetIntegrationConnectionByIdFn;
|
|
@@ -129,6 +131,7 @@ export function createGithubIntegrationProvider(options: CreateGithubIntegration
|
|
|
129
131
|
createGithubWebhookRoutes({
|
|
130
132
|
coreDb: options.coreDb,
|
|
131
133
|
publishIntegrationEventReceived: options.publishIntegrationEventReceived,
|
|
134
|
+
publishSourceRepositoryUpdated: options.publishSourceRepositoryUpdated,
|
|
132
135
|
publishSourcePush: options.publishSourcePush,
|
|
133
136
|
recordDeliveryOnly: options.recordDeliveryOnly,
|
|
134
137
|
getIntegrationConnectionById: options.getIntegrationConnectionById,
|
|
@@ -100,9 +100,10 @@ async function createTestApp(options: CreateTestAppOptions = {}): Promise<Fastif
|
|
|
100
100
|
|
|
101
101
|
return Promise.resolve(connection);
|
|
102
102
|
}),
|
|
103
|
-
// Webhook receiver dependencies
|
|
103
|
+
// Webhook receiver dependencies: install/OAuth tests don't exercise them.
|
|
104
104
|
coreDb: vi.fn() as never,
|
|
105
105
|
publishIntegrationEventReceived: vi.fn(() => Promise.resolve({published: false})),
|
|
106
|
+
publishSourceRepositoryUpdated: vi.fn(() => Promise.resolve({published: false})),
|
|
106
107
|
publishSourcePush: vi.fn(() => Promise.resolve({published: false})),
|
|
107
108
|
recordDeliveryOnly: vi.fn(() => Promise.resolve()),
|
|
108
109
|
getIntegrationConnectionById: vi.fn(() => Promise.resolve(undefined)),
|
|
@@ -11,6 +11,7 @@ import {createGithubWebhookRoutes} from './webhooks.js';
|
|
|
11
11
|
const WEBHOOK_SECRET = 'test-webhook-secret';
|
|
12
12
|
|
|
13
13
|
// The route persists through injected core functions (publishSourcePush,
|
|
14
|
+
// publishSourceRepositoryUpdated,
|
|
14
15
|
// recordDeliveryOnly, getIntegrationConnectionById) that @shipfox/api-integration-core
|
|
15
16
|
// owns and wires in production. github only orchestrates them, so here we fake that
|
|
16
17
|
// interface with spies and assert the route's own behavior: signature/payload
|
|
@@ -34,6 +35,7 @@ function fakeConnection(overrides: Partial<IntegrationConnection> = {}): Integra
|
|
|
34
35
|
interface TestApp {
|
|
35
36
|
app: FastifyInstance;
|
|
36
37
|
publishIntegrationEventReceived: ReturnType<typeof vi.fn>;
|
|
38
|
+
publishSourceRepositoryUpdated: ReturnType<typeof vi.fn>;
|
|
37
39
|
publishSourcePush: ReturnType<typeof vi.fn>;
|
|
38
40
|
recordDeliveryOnly: ReturnType<typeof vi.fn>;
|
|
39
41
|
getIntegrationConnectionById: ReturnType<typeof vi.fn>;
|
|
@@ -41,6 +43,7 @@ interface TestApp {
|
|
|
41
43
|
|
|
42
44
|
async function createTestApp(options: {connection?: IntegrationConnection} = {}): Promise<TestApp> {
|
|
43
45
|
const publishIntegrationEventReceived = vi.fn(() => Promise.resolve({published: true}));
|
|
46
|
+
const publishSourceRepositoryUpdated = vi.fn(() => Promise.resolve({published: true}));
|
|
44
47
|
const publishSourcePush = vi.fn(() => Promise.resolve({published: true}));
|
|
45
48
|
const recordDeliveryOnly = vi.fn(() => Promise.resolve());
|
|
46
49
|
const getIntegrationConnectionById = vi.fn(() =>
|
|
@@ -49,6 +52,7 @@ async function createTestApp(options: {connection?: IntegrationConnection} = {})
|
|
|
49
52
|
const routes = createGithubWebhookRoutes({
|
|
50
53
|
coreDb: db,
|
|
51
54
|
publishIntegrationEventReceived,
|
|
55
|
+
publishSourceRepositoryUpdated,
|
|
52
56
|
publishSourcePush,
|
|
53
57
|
recordDeliveryOnly,
|
|
54
58
|
getIntegrationConnectionById,
|
|
@@ -58,6 +62,7 @@ async function createTestApp(options: {connection?: IntegrationConnection} = {})
|
|
|
58
62
|
return {
|
|
59
63
|
app,
|
|
60
64
|
publishIntegrationEventReceived,
|
|
65
|
+
publishSourceRepositoryUpdated,
|
|
61
66
|
publishSourcePush,
|
|
62
67
|
recordDeliveryOnly,
|
|
63
68
|
getIntegrationConnectionById,
|
|
@@ -250,7 +255,12 @@ describe('GitHub webhook route', () => {
|
|
|
250
255
|
const rawPayload = {
|
|
251
256
|
action: 'opened',
|
|
252
257
|
installation: {id: installationId},
|
|
253
|
-
|
|
258
|
+
repository: {id: 42, full_name: 'shipfox/platform'},
|
|
259
|
+
pull_request: {
|
|
260
|
+
number: 17,
|
|
261
|
+
head: {repo: {id: 42, full_name: 'shipfox/platform'}},
|
|
262
|
+
base: {repo: {id: 42, full_name: 'shipfox/platform'}},
|
|
263
|
+
},
|
|
254
264
|
};
|
|
255
265
|
const body = JSON.stringify(rawPayload);
|
|
256
266
|
|
|
@@ -278,6 +288,44 @@ describe('GitHub webhook route', () => {
|
|
|
278
288
|
});
|
|
279
289
|
});
|
|
280
290
|
|
|
291
|
+
it('records and drops a pull request opened from a fork', async () => {
|
|
292
|
+
const installationId = 7787;
|
|
293
|
+
const connection = fakeConnection();
|
|
294
|
+
await seedInstallation(installationId, connection.id);
|
|
295
|
+
const {
|
|
296
|
+
app,
|
|
297
|
+
publishIntegrationEventReceived,
|
|
298
|
+
publishSourcePush,
|
|
299
|
+
recordDeliveryOnly,
|
|
300
|
+
getIntegrationConnectionById,
|
|
301
|
+
} = await createTestApp({connection});
|
|
302
|
+
const deliveryId = randomUUID();
|
|
303
|
+
const rawPayload = {
|
|
304
|
+
action: 'opened',
|
|
305
|
+
installation: {id: installationId},
|
|
306
|
+
repository: {id: 42, full_name: 'shipfox/platform'},
|
|
307
|
+
pull_request: {
|
|
308
|
+
head: {repo: {id: 84, full_name: 'contributor/platform'}},
|
|
309
|
+
base: {repo: {id: 42, full_name: 'shipfox/platform'}},
|
|
310
|
+
},
|
|
311
|
+
};
|
|
312
|
+
const body = JSON.stringify(rawPayload);
|
|
313
|
+
|
|
314
|
+
const res = await app.inject({
|
|
315
|
+
method: 'POST',
|
|
316
|
+
url: '/webhooks/integrations/github',
|
|
317
|
+
headers: signedHeaders(body, 'pull_request', deliveryId),
|
|
318
|
+
payload: body,
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
expect(res.statusCode).toBe(204);
|
|
322
|
+
expect(publishIntegrationEventReceived).not.toHaveBeenCalled();
|
|
323
|
+
expect(publishSourcePush).not.toHaveBeenCalled();
|
|
324
|
+
expect(recordDeliveryOnly).toHaveBeenCalledTimes(1);
|
|
325
|
+
expect(recordDeliveryOnly.mock.calls[0]?.[0]).toMatchObject({provider: 'github', deliveryId});
|
|
326
|
+
expect(getIntegrationConnectionById).not.toHaveBeenCalled();
|
|
327
|
+
});
|
|
328
|
+
|
|
281
329
|
it('publishes a generic envelope for a non-push event without an action', async () => {
|
|
282
330
|
const installationId = 7785;
|
|
283
331
|
const connection = fakeConnection();
|
|
@@ -3,6 +3,7 @@ import type {
|
|
|
3
3
|
GetIntegrationConnectionByIdFn,
|
|
4
4
|
PublishIntegrationEventReceivedFn,
|
|
5
5
|
PublishSourcePushFn,
|
|
6
|
+
PublishSourceRepositoryUpdatedFn,
|
|
6
7
|
RecordDeliveryOnlyFn,
|
|
7
8
|
StoredWebhookRequest,
|
|
8
9
|
WebhookProcessingResult,
|
|
@@ -28,6 +29,7 @@ const DELIVERY_HEADER = 'x-github-delivery';
|
|
|
28
29
|
export interface CreateGithubWebhookRoutesOptions {
|
|
29
30
|
coreDb: () => NodePgDatabase<Record<string, unknown>>;
|
|
30
31
|
publishIntegrationEventReceived: PublishIntegrationEventReceivedFn;
|
|
32
|
+
publishSourceRepositoryUpdated: PublishSourceRepositoryUpdatedFn;
|
|
31
33
|
publishSourcePush: PublishSourcePushFn;
|
|
32
34
|
recordDeliveryOnly: RecordDeliveryOnlyFn;
|
|
33
35
|
getIntegrationConnectionById: GetIntegrationConnectionByIdFn;
|