@rawdash/connector-github 0.15.0 → 0.17.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/README.md +48 -64
- package/dist/index.d.ts +606 -4
- package/dist/index.js +372 -96
- package/dist/index.js.map +1 -1
- package/package.json +5 -4
package/dist/index.js
CHANGED
|
@@ -1,21 +1,62 @@
|
|
|
1
1
|
// ../../connector-shared/dist/index.js
|
|
2
2
|
var HTTP_CLIENT_VERSION = "0.0.0";
|
|
3
3
|
var DEFAULT_USER_AGENT = `rawdash-connector/${HTTP_CLIENT_VERSION} (+https://rawdash.dev)`;
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
function connectorUserAgent(connectorId) {
|
|
5
|
+
return `rawdash-connector-${connectorId}/${HTTP_CLIENT_VERSION} (+https://rawdash.dev)`;
|
|
6
|
+
}
|
|
7
|
+
function standardRateLimitPolicy(config) {
|
|
8
|
+
const { remainingHeader, resetHeader, resetUnit, resetFallbackMs } = config;
|
|
9
|
+
const multiplier = resetUnit === "s" ? 1e3 : 1;
|
|
10
|
+
return {
|
|
11
|
+
parse(h) {
|
|
12
|
+
const remainingRaw = h.get(remainingHeader);
|
|
13
|
+
if (remainingRaw === null || remainingRaw.trim() === "") {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
const remaining = Number(remainingRaw);
|
|
17
|
+
if (!Number.isFinite(remaining)) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
const resetRaw = h.get(resetHeader);
|
|
21
|
+
if (resetRaw === null) {
|
|
22
|
+
if (resetFallbackMs === void 0) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
remaining,
|
|
27
|
+
resetAt: new Date(Date.now() + resetFallbackMs)
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
if (resetRaw.trim() === "") {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
const reset = Number(resetRaw);
|
|
34
|
+
if (!Number.isFinite(reset) || reset < 0) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
const resetMs = reset * multiplier;
|
|
38
|
+
if (!Number.isFinite(resetMs)) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
return { remaining, resetAt: new Date(resetMs) };
|
|
10
42
|
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function sanitizeAllowedUrl(options) {
|
|
46
|
+
const { url, host, pathname, protocol = "https:" } = options;
|
|
47
|
+
if (url === null) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
try {
|
|
51
|
+
const u = new URL(url);
|
|
52
|
+
if (u.protocol !== protocol || u.host !== host || u.pathname !== pathname) {
|
|
14
53
|
return null;
|
|
15
54
|
}
|
|
16
|
-
return
|
|
55
|
+
return u.toString();
|
|
56
|
+
} catch {
|
|
57
|
+
return null;
|
|
17
58
|
}
|
|
18
|
-
}
|
|
59
|
+
}
|
|
19
60
|
function parseLinkHeader(header) {
|
|
20
61
|
if (!header) {
|
|
21
62
|
return {};
|
|
@@ -34,7 +75,11 @@ function parseLinkHeader(header) {
|
|
|
34
75
|
import {
|
|
35
76
|
BaseConnector,
|
|
36
77
|
defineConfigFields,
|
|
37
|
-
|
|
78
|
+
defineConnectorDoc,
|
|
79
|
+
defineResources,
|
|
80
|
+
makeChunkedCursorGuard,
|
|
81
|
+
paginateChunked,
|
|
82
|
+
schemasFromResources
|
|
38
83
|
} from "@rawdash/core";
|
|
39
84
|
import { z } from "zod";
|
|
40
85
|
var configFields = defineConfigFields(
|
|
@@ -56,12 +101,41 @@ var configFields = defineConfigFields(
|
|
|
56
101
|
})
|
|
57
102
|
})
|
|
58
103
|
);
|
|
104
|
+
var doc = defineConnectorDoc({
|
|
105
|
+
displayName: "GitHub",
|
|
106
|
+
category: "engineering",
|
|
107
|
+
brandColor: "#181717",
|
|
108
|
+
tagline: "Sync pull requests, issues, deployments, releases, CI runs, and contributor activity from a GitHub repository.",
|
|
109
|
+
vendor: {
|
|
110
|
+
name: "GitHub",
|
|
111
|
+
apiDocs: "https://docs.github.com/rest",
|
|
112
|
+
website: "https://github.com"
|
|
113
|
+
},
|
|
114
|
+
auth: {
|
|
115
|
+
summary: "A personal access token is optional for public repositories but required for private repos and to avoid the low unauthenticated rate limit.",
|
|
116
|
+
setup: [
|
|
117
|
+
"Open GitHub \u2192 Settings \u2192 Developer settings \u2192 Personal access tokens.",
|
|
118
|
+
"Generate a token with the `repo` scope (read access is sufficient).",
|
|
119
|
+
'Store it as a secret and reference it from the connector config as `token: secret("GITHUB_TOKEN")`.'
|
|
120
|
+
]
|
|
121
|
+
},
|
|
122
|
+
rateLimit: "Unauthenticated requests share GitHub\u2019s low 60 requests/hour limit; an authenticated token raises it to 5,000 requests/hour.",
|
|
123
|
+
limitations: [
|
|
124
|
+
"The GitHub REST API can return the same item more than once within a sync (cursor pagination overlapping a mutating collection, retried requests, or an item surfaced via multiple endpoints). Each resource dedupes by stable id before writing, keeping the last copy seen.",
|
|
125
|
+
"Public repositories without a token are subject to GitHub\u2019s low unauthenticated rate limit."
|
|
126
|
+
]
|
|
127
|
+
});
|
|
59
128
|
var githubCredentials = {
|
|
60
129
|
token: {
|
|
61
130
|
description: "GitHub personal access token",
|
|
62
131
|
auth: "optional"
|
|
63
132
|
}
|
|
64
133
|
};
|
|
134
|
+
var githubRateLimit = standardRateLimitPolicy({
|
|
135
|
+
remainingHeader: "x-ratelimit-remaining",
|
|
136
|
+
resetHeader: "x-ratelimit-reset",
|
|
137
|
+
resetUnit: "s"
|
|
138
|
+
});
|
|
65
139
|
var PHASE_ORDER = [
|
|
66
140
|
"repo_stats",
|
|
67
141
|
"workflow_runs",
|
|
@@ -71,6 +145,23 @@ var PHASE_ORDER = [
|
|
|
71
145
|
"releases",
|
|
72
146
|
"contributors"
|
|
73
147
|
];
|
|
148
|
+
var PHASE_RESOURCES = {
|
|
149
|
+
repo_stats: ["repo"],
|
|
150
|
+
workflow_runs: ["workflow_run"],
|
|
151
|
+
pull_requests: ["pull_request"],
|
|
152
|
+
issues: ["issue"],
|
|
153
|
+
deployments: ["deployment"],
|
|
154
|
+
releases: ["release"],
|
|
155
|
+
contributors: ["contributor"]
|
|
156
|
+
};
|
|
157
|
+
function selectPhases(allowlist) {
|
|
158
|
+
if (allowlist === void 0) {
|
|
159
|
+
return PHASE_ORDER;
|
|
160
|
+
}
|
|
161
|
+
return PHASE_ORDER.filter(
|
|
162
|
+
(phase) => PHASE_RESOURCES[phase].some((r) => allowlist.has(r))
|
|
163
|
+
);
|
|
164
|
+
}
|
|
74
165
|
var CONTRIBUTORS_SKIPPED = /* @__PURE__ */ Symbol("contributors-skipped");
|
|
75
166
|
function dedupeByKey(items, keyFn, resource) {
|
|
76
167
|
if (items.length < 2) {
|
|
@@ -92,23 +183,9 @@ function dedupeByKey(items, keyFn, resource) {
|
|
|
92
183
|
}
|
|
93
184
|
return Array.from(seen.values());
|
|
94
185
|
}
|
|
95
|
-
|
|
96
|
-
if (typeof value !== "object" || value === null) {
|
|
97
|
-
return false;
|
|
98
|
-
}
|
|
99
|
-
const v = value;
|
|
100
|
-
if (typeof v.phase !== "string") {
|
|
101
|
-
return false;
|
|
102
|
-
}
|
|
103
|
-
if (!PHASE_ORDER.includes(v.phase)) {
|
|
104
|
-
return false;
|
|
105
|
-
}
|
|
106
|
-
if (v.page !== null && typeof v.page !== "string") {
|
|
107
|
-
return false;
|
|
108
|
-
}
|
|
109
|
-
return true;
|
|
110
|
-
}
|
|
186
|
+
var isGitHubSyncCursor = makeChunkedCursorGuard(PHASE_ORDER);
|
|
111
187
|
var workflowRunsResponseSchema = z.object({
|
|
188
|
+
total_count: z.number().int().optional(),
|
|
112
189
|
workflow_runs: z.array(
|
|
113
190
|
z.object({
|
|
114
191
|
id: z.number().int(),
|
|
@@ -119,7 +196,33 @@ var workflowRunsResponseSchema = z.object({
|
|
|
119
196
|
actor: z.object({ login: z.string().min(1) }).nullable(),
|
|
120
197
|
created_at: z.iso.datetime(),
|
|
121
198
|
updated_at: z.iso.datetime(),
|
|
122
|
-
run_attempt: z.number().int()
|
|
199
|
+
run_attempt: z.number().int(),
|
|
200
|
+
artifacts_url: z.string().optional(),
|
|
201
|
+
cancel_url: z.string().optional(),
|
|
202
|
+
check_suite_id: z.number().int().optional(),
|
|
203
|
+
check_suite_node_id: z.string().optional(),
|
|
204
|
+
check_suite_url: z.string().optional(),
|
|
205
|
+
display_title: z.string().optional(),
|
|
206
|
+
event: z.string().optional(),
|
|
207
|
+
head_commit: z.unknown().optional(),
|
|
208
|
+
head_repository: z.unknown().optional(),
|
|
209
|
+
head_sha: z.string().optional(),
|
|
210
|
+
html_url: z.string().optional(),
|
|
211
|
+
jobs_url: z.string().optional(),
|
|
212
|
+
logs_url: z.string().optional(),
|
|
213
|
+
node_id: z.string().optional(),
|
|
214
|
+
path: z.string().optional(),
|
|
215
|
+
previous_attempt_url: z.string().nullable().optional(),
|
|
216
|
+
pull_requests: z.array(z.unknown()).optional(),
|
|
217
|
+
referenced_workflows: z.array(z.unknown()).optional(),
|
|
218
|
+
repository: z.unknown().optional(),
|
|
219
|
+
rerun_url: z.string().optional(),
|
|
220
|
+
run_number: z.number().int().optional(),
|
|
221
|
+
run_started_at: z.iso.datetime().optional(),
|
|
222
|
+
triggering_actor: z.object({ login: z.string().min(1) }).optional(),
|
|
223
|
+
url: z.string().optional(),
|
|
224
|
+
workflow_id: z.number().int().optional(),
|
|
225
|
+
workflow_url: z.string().optional()
|
|
123
226
|
})
|
|
124
227
|
)
|
|
125
228
|
});
|
|
@@ -129,9 +232,58 @@ var pullRequestsSchema = z.array(
|
|
|
129
232
|
title: z.string(),
|
|
130
233
|
state: z.string(),
|
|
131
234
|
draft: z.boolean(),
|
|
132
|
-
user: z.object({
|
|
235
|
+
user: z.object({
|
|
236
|
+
login: z.string().min(1),
|
|
237
|
+
avatar_url: z.string().optional(),
|
|
238
|
+
events_url: z.string().optional(),
|
|
239
|
+
followers_url: z.string().optional(),
|
|
240
|
+
following_url: z.string().optional(),
|
|
241
|
+
gists_url: z.string().optional(),
|
|
242
|
+
gravatar_id: z.string().nullable().optional(),
|
|
243
|
+
html_url: z.string().optional(),
|
|
244
|
+
id: z.number().int().optional(),
|
|
245
|
+
node_id: z.string().optional(),
|
|
246
|
+
organizations_url: z.string().optional(),
|
|
247
|
+
received_events_url: z.string().optional(),
|
|
248
|
+
repos_url: z.string().optional(),
|
|
249
|
+
site_admin: z.boolean().optional(),
|
|
250
|
+
starred_url: z.string().optional(),
|
|
251
|
+
subscriptions_url: z.string().optional(),
|
|
252
|
+
type: z.string().optional(),
|
|
253
|
+
url: z.string().optional(),
|
|
254
|
+
user_view_type: z.string().optional()
|
|
255
|
+
}),
|
|
133
256
|
created_at: z.iso.datetime(),
|
|
134
|
-
updated_at: z.iso.datetime()
|
|
257
|
+
updated_at: z.iso.datetime(),
|
|
258
|
+
_links: z.unknown().optional(),
|
|
259
|
+
active_lock_reason: z.string().nullable().optional(),
|
|
260
|
+
assignee: z.unknown().optional(),
|
|
261
|
+
assignees: z.unknown().optional(),
|
|
262
|
+
author_association: z.string().optional(),
|
|
263
|
+
auto_merge: z.unknown().optional(),
|
|
264
|
+
base: z.unknown().optional(),
|
|
265
|
+
body: z.string().nullable().optional(),
|
|
266
|
+
closed_at: z.string().nullable().optional(),
|
|
267
|
+
comments_url: z.string().optional(),
|
|
268
|
+
commits_url: z.string().optional(),
|
|
269
|
+
diff_url: z.string().optional(),
|
|
270
|
+
head: z.unknown().optional(),
|
|
271
|
+
html_url: z.string().optional(),
|
|
272
|
+
id: z.number().int().optional(),
|
|
273
|
+
issue_url: z.string().optional(),
|
|
274
|
+
labels: z.unknown().optional(),
|
|
275
|
+
locked: z.boolean().optional(),
|
|
276
|
+
merge_commit_sha: z.string().nullable().optional(),
|
|
277
|
+
merged_at: z.string().nullable().optional(),
|
|
278
|
+
milestone: z.unknown().optional(),
|
|
279
|
+
node_id: z.string().optional(),
|
|
280
|
+
patch_url: z.string().optional(),
|
|
281
|
+
requested_reviewers: z.unknown().optional(),
|
|
282
|
+
requested_teams: z.unknown().optional(),
|
|
283
|
+
review_comment_url: z.string().optional(),
|
|
284
|
+
review_comments_url: z.string().optional(),
|
|
285
|
+
statuses_url: z.string().optional(),
|
|
286
|
+
url: z.string().optional()
|
|
135
287
|
})
|
|
136
288
|
);
|
|
137
289
|
var reviewsSchema = z.array(
|
|
@@ -148,10 +300,34 @@ var issuesSchema = z.array(
|
|
|
148
300
|
state: z.string(),
|
|
149
301
|
labels: z.array(z.object({ name: z.string() })),
|
|
150
302
|
assignees: z.array(z.object({ login: z.string().min(1) })),
|
|
151
|
-
user: z.object({ login: z.string().min(1) }),
|
|
303
|
+
user: z.object({ login: z.string().min(1) }).catchall(z.unknown()),
|
|
152
304
|
created_at: z.iso.datetime(),
|
|
153
305
|
updated_at: z.iso.datetime(),
|
|
154
|
-
closed_at: z.iso.datetime().nullable()
|
|
306
|
+
closed_at: z.iso.datetime().nullable(),
|
|
307
|
+
pull_request: z.unknown().optional(),
|
|
308
|
+
active_lock_reason: z.unknown().optional(),
|
|
309
|
+
assignee: z.unknown().optional(),
|
|
310
|
+
author_association: z.string().optional(),
|
|
311
|
+
body: z.string().nullable().optional(),
|
|
312
|
+
closed_by: z.unknown().optional(),
|
|
313
|
+
comments: z.number().int().optional(),
|
|
314
|
+
comments_url: z.string().optional(),
|
|
315
|
+
draft: z.boolean().optional(),
|
|
316
|
+
events_url: z.string().optional(),
|
|
317
|
+
html_url: z.string().optional(),
|
|
318
|
+
id: z.number().int().optional(),
|
|
319
|
+
issue_field_values: z.unknown().optional(),
|
|
320
|
+
labels_url: z.string().optional(),
|
|
321
|
+
locked: z.boolean().optional(),
|
|
322
|
+
milestone: z.unknown().optional(),
|
|
323
|
+
node_id: z.string().optional(),
|
|
324
|
+
performed_via_github_app: z.unknown().optional(),
|
|
325
|
+
reactions: z.unknown().optional(),
|
|
326
|
+
repository_url: z.string().optional(),
|
|
327
|
+
state_reason: z.unknown().optional(),
|
|
328
|
+
timeline_url: z.string().optional(),
|
|
329
|
+
type: z.unknown().optional(),
|
|
330
|
+
url: z.string().optional()
|
|
155
331
|
})
|
|
156
332
|
);
|
|
157
333
|
var deploymentsSchema = z.array(
|
|
@@ -201,19 +377,63 @@ var repoStatsSchema = z.object({
|
|
|
201
377
|
forks_count: z.number().int(),
|
|
202
378
|
subscribers_count: z.number().int()
|
|
203
379
|
});
|
|
380
|
+
var githubResources = defineResources({
|
|
381
|
+
repo: {
|
|
382
|
+
shape: "entity",
|
|
383
|
+
description: "Top-level repository stats (stars, forks, and watchers) as a single entity.",
|
|
384
|
+
endpoint: "GET /repos/{owner}/{repo}",
|
|
385
|
+
responses: { repo: repoStatsSchema }
|
|
386
|
+
},
|
|
387
|
+
workflow_run: {
|
|
388
|
+
shape: "event",
|
|
389
|
+
description: "GitHub Actions CI pipeline executions.",
|
|
390
|
+
endpoint: "GET /repos/{owner}/{repo}/actions/runs",
|
|
391
|
+
responses: { workflow_runs: workflowRunsResponseSchema }
|
|
392
|
+
},
|
|
393
|
+
pull_request: {
|
|
394
|
+
shape: "entity",
|
|
395
|
+
description: "Open and closed pull requests, including draft state, author, and review state.",
|
|
396
|
+
endpoint: "GET /repos/{owner}/{repo}/pulls",
|
|
397
|
+
notes: "Review state is folded in from GET /repos/{owner}/{repo}/pulls/{number}/reviews per PR.",
|
|
398
|
+
responses: {
|
|
399
|
+
pull_requests: pullRequestsSchema,
|
|
400
|
+
pull_request_reviews: reviewsSchema
|
|
401
|
+
}
|
|
402
|
+
},
|
|
403
|
+
issue: {
|
|
404
|
+
shape: "entity",
|
|
405
|
+
description: "Open and closed issues with labels, assignees, and author (pull requests excluded).",
|
|
406
|
+
endpoint: "GET /repos/{owner}/{repo}/issues",
|
|
407
|
+
responses: { issues: issuesSchema }
|
|
408
|
+
},
|
|
409
|
+
deployment: {
|
|
410
|
+
shape: "entity",
|
|
411
|
+
description: "Deployments with their latest status, keyed by environment and ref.",
|
|
412
|
+
endpoint: "GET /repos/{owner}/{repo}/deployments",
|
|
413
|
+
notes: "The latest status is folded in from GET /repos/{owner}/{repo}/deployments/{id}/statuses.",
|
|
414
|
+
responses: {
|
|
415
|
+
deployments: deploymentsSchema,
|
|
416
|
+
deployment_statuses: deploymentStatusesSchema
|
|
417
|
+
}
|
|
418
|
+
},
|
|
419
|
+
release: {
|
|
420
|
+
shape: "entity",
|
|
421
|
+
description: "Published, draft, and prerelease GitHub releases.",
|
|
422
|
+
endpoint: "GET /repos/{owner}/{repo}/releases",
|
|
423
|
+
responses: { releases: releasesSchema }
|
|
424
|
+
},
|
|
425
|
+
contributor: {
|
|
426
|
+
shape: "entity",
|
|
427
|
+
description: "Per-author commit activity (commits, additions, deletions) for the repository.",
|
|
428
|
+
endpoint: "GET /repos/{owner}/{repo}/stats/contributors",
|
|
429
|
+
responses: { contributors: contributorsSchema }
|
|
430
|
+
}
|
|
431
|
+
});
|
|
432
|
+
var id = "github-actions";
|
|
204
433
|
var GitHubConnector = class _GitHubConnector extends BaseConnector {
|
|
205
|
-
static id =
|
|
206
|
-
static
|
|
207
|
-
|
|
208
|
-
workflow_runs: workflowRunsResponseSchema,
|
|
209
|
-
pull_requests: pullRequestsSchema,
|
|
210
|
-
pull_request_reviews: reviewsSchema,
|
|
211
|
-
issues: issuesSchema,
|
|
212
|
-
deployments: deploymentsSchema,
|
|
213
|
-
deployment_statuses: deploymentStatusesSchema,
|
|
214
|
-
releases: releasesSchema,
|
|
215
|
-
contributors: contributorsSchema
|
|
216
|
-
};
|
|
434
|
+
static id = id;
|
|
435
|
+
static resources = githubResources;
|
|
436
|
+
static schemas = schemasFromResources(githubResources);
|
|
217
437
|
static create(input, ctx) {
|
|
218
438
|
const parsed = configFields.parse(input);
|
|
219
439
|
return new _GitHubConnector(
|
|
@@ -222,14 +442,15 @@ var GitHubConnector = class _GitHubConnector extends BaseConnector {
|
|
|
222
442
|
ctx
|
|
223
443
|
);
|
|
224
444
|
}
|
|
225
|
-
id =
|
|
445
|
+
id = id;
|
|
226
446
|
credentials = githubCredentials;
|
|
227
447
|
seenWorkflowRunIds = /* @__PURE__ */ new Set();
|
|
448
|
+
preservedDeploymentStatus = /* @__PURE__ */ new Map();
|
|
228
449
|
buildHeaders() {
|
|
229
450
|
const headers = {
|
|
230
451
|
Accept: "application/vnd.github+json",
|
|
231
452
|
"X-GitHub-Api-Version": "2022-11-28",
|
|
232
|
-
"User-Agent": "
|
|
453
|
+
"User-Agent": connectorUserAgent("github")
|
|
233
454
|
};
|
|
234
455
|
if (this.creds.token) {
|
|
235
456
|
headers["Authorization"] = `Bearer ${this.creds.token}`;
|
|
@@ -263,22 +484,21 @@ var GitHubConnector = class _GitHubConnector extends BaseConnector {
|
|
|
263
484
|
}
|
|
264
485
|
}
|
|
265
486
|
sanitizePageUrl(phase, pageUrl) {
|
|
266
|
-
if (pageUrl === null) {
|
|
267
|
-
return null;
|
|
268
|
-
}
|
|
269
487
|
const allowedPath = this.allowedPageBasePath(phase);
|
|
270
488
|
if (allowedPath === null) {
|
|
271
489
|
return null;
|
|
272
490
|
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
491
|
+
return sanitizeAllowedUrl({
|
|
492
|
+
url: pageUrl,
|
|
493
|
+
host: "api.github.com",
|
|
494
|
+
pathname: allowedPath
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
isResourceAllowed(options, resource) {
|
|
498
|
+
if (!options.resources) {
|
|
499
|
+
return true;
|
|
281
500
|
}
|
|
501
|
+
return options.resources.has(resource);
|
|
282
502
|
}
|
|
283
503
|
resolveCursor(cursor) {
|
|
284
504
|
if (!isGitHubSyncCursor(cursor)) {
|
|
@@ -334,24 +554,30 @@ var GitHubConnector = class _GitHubConnector extends BaseConnector {
|
|
|
334
554
|
next: cutoffReached ? null : nextLink
|
|
335
555
|
};
|
|
336
556
|
}
|
|
337
|
-
async fetchPullRequests(page, signal) {
|
|
557
|
+
async fetchPullRequests(options, page, signal) {
|
|
338
558
|
const { owner, repo } = this.settings;
|
|
339
|
-
const url = page ?? `https://api.github.com/repos/${owner}/${repo}/pulls?state=all&per_page=100`;
|
|
559
|
+
const url = page ?? `https://api.github.com/repos/${owner}/${repo}/pulls?state=all&sort=updated&direction=desc&per_page=100`;
|
|
340
560
|
const res = await this.fetch(url, "pull_requests", signal);
|
|
341
561
|
const nextLink = parseLinkHeader(res.headers.get("link"))["next"] ?? null;
|
|
342
562
|
const prs = res.body;
|
|
563
|
+
const cutoff = options.since ? new Date(options.since).getTime() : null;
|
|
564
|
+
const filteredPrs = cutoff !== null ? prs.filter((pr) => new Date(pr.updated_at).getTime() >= cutoff) : prs;
|
|
565
|
+
const lastPr = prs.at(-1);
|
|
566
|
+
const cutoffReached = cutoff !== null && lastPr !== void 0 && new Date(lastPr.updated_at).getTime() < cutoff;
|
|
343
567
|
const reviewsByPR = /* @__PURE__ */ new Map();
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
568
|
+
if (this.isResourceAllowed(options, "pull_request_reviews")) {
|
|
569
|
+
for (const pr of filteredPrs) {
|
|
570
|
+
signal?.throwIfAborted();
|
|
571
|
+
const reviews = await this.fetch(
|
|
572
|
+
`https://api.github.com/repos/${owner}/${repo}/pulls/${pr.number}/reviews`,
|
|
573
|
+
"pull_request_reviews",
|
|
574
|
+
signal
|
|
575
|
+
);
|
|
576
|
+
reviewsByPR.set(pr.number, reviews.body);
|
|
577
|
+
}
|
|
352
578
|
}
|
|
353
|
-
const items = [{ prs, reviewsByPR }];
|
|
354
|
-
return { items, next: nextLink };
|
|
579
|
+
const items = [{ prs: filteredPrs, reviewsByPR }];
|
|
580
|
+
return { items, next: cutoffReached ? null : nextLink };
|
|
355
581
|
}
|
|
356
582
|
async fetchIssues(options, page, signal) {
|
|
357
583
|
const { owner, repo } = this.settings;
|
|
@@ -371,7 +597,7 @@ var GitHubConnector = class _GitHubConnector extends BaseConnector {
|
|
|
371
597
|
const nextLink = parseLinkHeader(res.headers.get("link"))["next"] ?? null;
|
|
372
598
|
return { items: res.body, next: nextLink };
|
|
373
599
|
}
|
|
374
|
-
async fetchDeployments(page, signal) {
|
|
600
|
+
async fetchDeployments(options, page, signal) {
|
|
375
601
|
const { owner, repo } = this.settings;
|
|
376
602
|
const url = page ?? `https://api.github.com/repos/${owner}/${repo}/deployments?per_page=100`;
|
|
377
603
|
const res = await this.fetch(
|
|
@@ -381,25 +607,41 @@ var GitHubConnector = class _GitHubConnector extends BaseConnector {
|
|
|
381
607
|
);
|
|
382
608
|
const nextLink = parseLinkHeader(res.headers.get("link"))["next"] ?? null;
|
|
383
609
|
const deployments = res.body;
|
|
610
|
+
const cutoff = options.since ? new Date(options.since).getTime() : null;
|
|
611
|
+
const filteredDeployments = cutoff !== null ? deployments.filter((d) => new Date(d.created_at).getTime() >= cutoff) : deployments;
|
|
612
|
+
const lastDeployment = deployments.at(-1);
|
|
613
|
+
const cutoffReached = cutoff !== null && lastDeployment !== void 0 && new Date(lastDeployment.created_at).getTime() < cutoff;
|
|
384
614
|
const latestStatusById = /* @__PURE__ */ new Map();
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
615
|
+
if (this.isResourceAllowed(options, "deployment_statuses")) {
|
|
616
|
+
for (const deployment of filteredDeployments) {
|
|
617
|
+
signal?.throwIfAborted();
|
|
618
|
+
const statusRes = await this.fetch(
|
|
619
|
+
`https://api.github.com/repos/${owner}/${repo}/deployments/${deployment.id}/statuses?per_page=1`,
|
|
620
|
+
"deployment_statuses",
|
|
621
|
+
signal
|
|
622
|
+
);
|
|
623
|
+
latestStatusById.set(deployment.id, statusRes.body[0] ?? null);
|
|
624
|
+
}
|
|
393
625
|
}
|
|
394
|
-
const items = [
|
|
395
|
-
|
|
626
|
+
const items = [
|
|
627
|
+
{ deployments: filteredDeployments, latestStatusById }
|
|
628
|
+
];
|
|
629
|
+
return { items, next: cutoffReached ? null : nextLink };
|
|
396
630
|
}
|
|
397
|
-
async fetchReleases(page, signal) {
|
|
631
|
+
async fetchReleases(options, page, signal) {
|
|
398
632
|
const { owner, repo } = this.settings;
|
|
399
633
|
const url = page ?? `https://api.github.com/repos/${owner}/${repo}/releases?per_page=100`;
|
|
400
634
|
const res = await this.fetch(url, "releases", signal);
|
|
401
635
|
const nextLink = parseLinkHeader(res.headers.get("link"))["next"] ?? null;
|
|
402
|
-
|
|
636
|
+
const releases = res.body;
|
|
637
|
+
const cutoff = options.since ? new Date(options.since).getTime() : null;
|
|
638
|
+
const filtered = cutoff !== null ? releases.filter((r) => {
|
|
639
|
+
const ts = new Date(r.published_at ?? r.created_at).getTime();
|
|
640
|
+
return ts >= cutoff;
|
|
641
|
+
}) : releases;
|
|
642
|
+
const lastRelease = releases.at(-1);
|
|
643
|
+
const cutoffReached = cutoff !== null && lastRelease !== void 0 && new Date(lastRelease.published_at ?? lastRelease.created_at).getTime() < cutoff;
|
|
644
|
+
return { items: filtered, next: cutoffReached ? null : nextLink };
|
|
403
645
|
}
|
|
404
646
|
async fetchContributors(signal) {
|
|
405
647
|
const { owner, repo } = this.settings;
|
|
@@ -513,10 +755,16 @@ var GitHubConnector = class _GitHubConnector extends BaseConnector {
|
|
|
513
755
|
});
|
|
514
756
|
}
|
|
515
757
|
}
|
|
516
|
-
async writePullRequests(storage, items, page) {
|
|
758
|
+
async writePullRequests(storage, items, page, options) {
|
|
759
|
+
const reviewsAllowed = this.isResourceAllowed(
|
|
760
|
+
options,
|
|
761
|
+
"pull_request_reviews"
|
|
762
|
+
);
|
|
517
763
|
if (page === null) {
|
|
518
764
|
await storage.entities([], { types: ["pull_request"] });
|
|
519
|
-
|
|
765
|
+
if (reviewsAllowed) {
|
|
766
|
+
await storage.edges([], { kinds: ["reviewed_by"] });
|
|
767
|
+
}
|
|
520
768
|
}
|
|
521
769
|
const pageItems = items;
|
|
522
770
|
for (const { prs: rawPrs, reviewsByPR } of pageItems) {
|
|
@@ -539,6 +787,9 @@ var GitHubConnector = class _GitHubConnector extends BaseConnector {
|
|
|
539
787
|
updated_at: new Date(pr.updated_at).getTime()
|
|
540
788
|
});
|
|
541
789
|
}
|
|
790
|
+
if (!reviewsAllowed) {
|
|
791
|
+
continue;
|
|
792
|
+
}
|
|
542
793
|
for (const pr of prs) {
|
|
543
794
|
const reviews = reviewsByPR.get(pr.number) ?? [];
|
|
544
795
|
for (const review of reviews) {
|
|
@@ -586,8 +837,21 @@ var GitHubConnector = class _GitHubConnector extends BaseConnector {
|
|
|
586
837
|
});
|
|
587
838
|
}
|
|
588
839
|
}
|
|
589
|
-
async writeDeployments(storage, items, page) {
|
|
840
|
+
async writeDeployments(storage, items, page, options) {
|
|
841
|
+
const statusesAllowed = this.isResourceAllowed(
|
|
842
|
+
options,
|
|
843
|
+
"deployment_statuses"
|
|
844
|
+
);
|
|
590
845
|
if (page === null) {
|
|
846
|
+
if (!statusesAllowed) {
|
|
847
|
+
const existing = await storage.queryEntities({ type: "deployment" });
|
|
848
|
+
for (const entity of existing) {
|
|
849
|
+
const prev = entity.attributes["latest_status"];
|
|
850
|
+
if (typeof prev === "string") {
|
|
851
|
+
this.preservedDeploymentStatus.set(entity.id, prev);
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
}
|
|
591
855
|
await storage.entities([], { types: ["deployment"] });
|
|
592
856
|
}
|
|
593
857
|
const pageItems = items;
|
|
@@ -598,9 +862,16 @@ var GitHubConnector = class _GitHubConnector extends BaseConnector {
|
|
|
598
862
|
"deployments"
|
|
599
863
|
);
|
|
600
864
|
for (const deployment of deployments) {
|
|
601
|
-
const status = latestStatusById.get(deployment.id) ?? null;
|
|
602
865
|
const createdMs = new Date(deployment.created_at).getTime();
|
|
603
|
-
|
|
866
|
+
let latestStatus;
|
|
867
|
+
let statusUpdatedMs = null;
|
|
868
|
+
if (statusesAllowed) {
|
|
869
|
+
const status = latestStatusById.get(deployment.id) ?? null;
|
|
870
|
+
latestStatus = status?.state ?? "unknown";
|
|
871
|
+
statusUpdatedMs = status?.updated_at ? new Date(status.updated_at).getTime() : null;
|
|
872
|
+
} else {
|
|
873
|
+
latestStatus = this.preservedDeploymentStatus.get(String(deployment.id)) ?? "unknown";
|
|
874
|
+
}
|
|
604
875
|
await storage.entity({
|
|
605
876
|
type: "deployment",
|
|
606
877
|
id: String(deployment.id),
|
|
@@ -610,7 +881,7 @@ var GitHubConnector = class _GitHubConnector extends BaseConnector {
|
|
|
610
881
|
sha: deployment.sha,
|
|
611
882
|
creator: deployment.creator?.login ?? "",
|
|
612
883
|
created_at: createdMs,
|
|
613
|
-
latest_status:
|
|
884
|
+
latest_status: latestStatus
|
|
614
885
|
},
|
|
615
886
|
updated_at: Math.max(createdMs, statusUpdatedMs ?? 0)
|
|
616
887
|
});
|
|
@@ -676,10 +947,12 @@ var GitHubConnector = class _GitHubConnector extends BaseConnector {
|
|
|
676
947
|
}
|
|
677
948
|
async sync(options, storage, signal) {
|
|
678
949
|
const cursor = this.resolveCursor(options.cursor);
|
|
950
|
+
const phases = selectPhases(options.resources);
|
|
679
951
|
return paginateChunked({
|
|
680
|
-
phases
|
|
952
|
+
phases,
|
|
681
953
|
cursor,
|
|
682
954
|
signal,
|
|
955
|
+
logger: this.logger,
|
|
683
956
|
fetchPage: async (phase, page, sig) => {
|
|
684
957
|
switch (phase) {
|
|
685
958
|
case "repo_stats":
|
|
@@ -687,13 +960,13 @@ var GitHubConnector = class _GitHubConnector extends BaseConnector {
|
|
|
687
960
|
case "workflow_runs":
|
|
688
961
|
return options.mode === "latest" ? this.fetchWorkflowRunsLatest(sig) : this.fetchWorkflowRunsFull(options, page, sig);
|
|
689
962
|
case "pull_requests":
|
|
690
|
-
return this.fetchPullRequests(page, sig);
|
|
963
|
+
return this.fetchPullRequests(options, page, sig);
|
|
691
964
|
case "issues":
|
|
692
965
|
return this.fetchIssues(options, page, sig);
|
|
693
966
|
case "deployments":
|
|
694
|
-
return this.fetchDeployments(page, sig);
|
|
967
|
+
return this.fetchDeployments(options, page, sig);
|
|
695
968
|
case "releases":
|
|
696
|
-
return this.fetchReleases(page, sig);
|
|
969
|
+
return this.fetchReleases(options, page, sig);
|
|
697
970
|
case "contributors":
|
|
698
971
|
return this.fetchContributors(sig);
|
|
699
972
|
}
|
|
@@ -705,11 +978,11 @@ var GitHubConnector = class _GitHubConnector extends BaseConnector {
|
|
|
705
978
|
case "workflow_runs":
|
|
706
979
|
return options.mode === "latest" ? this.writeWorkflowRunsLatest(storage, items) : this.writeWorkflowRunsFull(storage, items, page);
|
|
707
980
|
case "pull_requests":
|
|
708
|
-
return this.writePullRequests(storage, items, page);
|
|
981
|
+
return this.writePullRequests(storage, items, page, options);
|
|
709
982
|
case "issues":
|
|
710
983
|
return this.writeIssues(storage, items, page);
|
|
711
984
|
case "deployments":
|
|
712
|
-
return this.writeDeployments(storage, items, page);
|
|
985
|
+
return this.writeDeployments(storage, items, page, options);
|
|
713
986
|
case "releases":
|
|
714
987
|
return this.writeReleases(storage, items, page);
|
|
715
988
|
case "contributors":
|
|
@@ -725,6 +998,9 @@ var index_default = GitHubConnector;
|
|
|
725
998
|
export {
|
|
726
999
|
GitHubConnector,
|
|
727
1000
|
configFields,
|
|
728
|
-
index_default as default
|
|
1001
|
+
index_default as default,
|
|
1002
|
+
doc,
|
|
1003
|
+
id,
|
|
1004
|
+
githubResources as resources
|
|
729
1005
|
};
|
|
730
1006
|
//# sourceMappingURL=index.js.map
|