reposets 0.4.1 → 0.4.2
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 +15 -14
- package/bin/reposets.d.ts +1 -0
- package/bin/reposets.js +25 -507
- package/cli/commands/credentials.js +74 -0
- package/cli/commands/doctor.js +154 -0
- package/cli/commands/init.js +147 -0
- package/cli/commands/list.js +49 -0
- package/cli/commands/sync.js +70 -0
- package/cli/commands/validate.js +53 -0
- package/errors.js +12 -0
- package/index.d.ts +1750 -1840
- package/index.js +19 -2
- package/lib/crypto.js +27 -0
- package/package.json +61 -73
- package/schemas/common.js +130 -0
- package/schemas/config.js +469 -0
- package/schemas/credentials.js +78 -0
- package/schemas/environment.js +70 -0
- package/schemas/ruleset.js +545 -0
- package/services/ConfigFiles.js +119 -0
- package/services/CredentialResolver.js +40 -0
- package/services/GitHubClient.js +875 -0
- package/services/OnePasswordClient.js +30 -0
- package/services/SyncEngine.js +580 -0
- package/services/SyncLogger.js +106 -0
- package/tsdoc-metadata.json +11 -11
- package/500.js +0 -3354
|
@@ -0,0 +1,875 @@
|
|
|
1
|
+
import { GitHubApiError } from "../errors.js";
|
|
2
|
+
import { encryptSecret } from "../lib/crypto.js";
|
|
3
|
+
import { Context, Effect, Layer } from "effect";
|
|
4
|
+
import { Octokit } from "@octokit/rest";
|
|
5
|
+
|
|
6
|
+
//#region src/services/GitHubClient.ts
|
|
7
|
+
var GitHubClient = class extends Context.Tag("GitHubClient")() {};
|
|
8
|
+
/** Settings fields that are only valid for organization-owned repositories. */
|
|
9
|
+
const ORG_ONLY_SETTINGS = new Set(["allow_forking"]);
|
|
10
|
+
/**
|
|
11
|
+
* Fields in the user-facing security_and_analysis block that GitHub's API
|
|
12
|
+
* accepts as `{ status: "enabled" | "disabled" }`. The value as stored in
|
|
13
|
+
* config is the literal string; we wrap it before sending.
|
|
14
|
+
*/
|
|
15
|
+
const SAA_STATUS_FIELDS = new Set([
|
|
16
|
+
"advanced_security",
|
|
17
|
+
"code_security",
|
|
18
|
+
"secret_scanning",
|
|
19
|
+
"secret_scanning_push_protection",
|
|
20
|
+
"secret_scanning_ai_detection",
|
|
21
|
+
"secret_scanning_non_provider_patterns",
|
|
22
|
+
"secret_scanning_delegated_alert_dismissal",
|
|
23
|
+
"secret_scanning_delegated_bypass",
|
|
24
|
+
"dependabot_security_updates"
|
|
25
|
+
]);
|
|
26
|
+
/**
|
|
27
|
+
* Translate the user-facing security_and_analysis config block into the
|
|
28
|
+
* shape GitHub expects on `PATCH /repos/{o}/{r}`. Reviewer entries are
|
|
29
|
+
* expected to already have a numeric `reviewer_id` and `reviewer_type`
|
|
30
|
+
* (resolution from team slugs happens in the SyncEngine).
|
|
31
|
+
*/
|
|
32
|
+
function transformSecurityAndAnalysis(value) {
|
|
33
|
+
if (value === null || typeof value !== "object") return void 0;
|
|
34
|
+
const input = value;
|
|
35
|
+
const out = {};
|
|
36
|
+
for (const [key, raw] of Object.entries(input)) {
|
|
37
|
+
if (raw === void 0) continue;
|
|
38
|
+
if (SAA_STATUS_FIELDS.has(key) && (raw === "enabled" || raw === "disabled")) out[key] = { status: raw };
|
|
39
|
+
else if (key === "delegated_bypass_reviewers" && Array.isArray(raw) && raw.length > 0) out.secret_scanning_delegated_bypass_options = { reviewers: raw };
|
|
40
|
+
}
|
|
41
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Settings fields only available via the GraphQL `updateRepository` mutation.
|
|
45
|
+
* Maps snake_case config keys to camelCase GraphQL input field names.
|
|
46
|
+
*/
|
|
47
|
+
const GRAPHQL_SETTINGS = {
|
|
48
|
+
has_sponsorships: "hasSponsorshipsEnabled",
|
|
49
|
+
has_pull_requests: "hasPullRequestsEnabled"
|
|
50
|
+
};
|
|
51
|
+
/* v8 ignore start -- live Octokit API calls, tested via GitHubClientTest recorder */
|
|
52
|
+
function GitHubClientLive(token) {
|
|
53
|
+
return Layer.succeed(GitHubClient, (() => {
|
|
54
|
+
const octokit = new Octokit({ auth: token });
|
|
55
|
+
function wrapError(error) {
|
|
56
|
+
if (error instanceof Error) {
|
|
57
|
+
const asAny = error;
|
|
58
|
+
const status = typeof asAny.status === "number" ? asAny.status : void 0;
|
|
59
|
+
if (status !== void 0) return new GitHubApiError({
|
|
60
|
+
message: error.message,
|
|
61
|
+
status
|
|
62
|
+
});
|
|
63
|
+
return new GitHubApiError({ message: error.message });
|
|
64
|
+
}
|
|
65
|
+
return new GitHubApiError({ message: String(error) });
|
|
66
|
+
}
|
|
67
|
+
const ownerTypeCache = /* @__PURE__ */ new Map();
|
|
68
|
+
const teamIdCache = /* @__PURE__ */ new Map();
|
|
69
|
+
const roleIdCache = /* @__PURE__ */ new Map();
|
|
70
|
+
return {
|
|
71
|
+
getOwnerType(owner) {
|
|
72
|
+
return Effect.tryPromise({
|
|
73
|
+
try: async () => {
|
|
74
|
+
const cached = ownerTypeCache.get(owner);
|
|
75
|
+
if (cached) return cached;
|
|
76
|
+
const { data } = await octokit.users.getByUsername({ username: owner });
|
|
77
|
+
const ownerType = data.type === "Organization" ? "Organization" : "User";
|
|
78
|
+
ownerTypeCache.set(owner, ownerType);
|
|
79
|
+
return ownerType;
|
|
80
|
+
},
|
|
81
|
+
catch: wrapError
|
|
82
|
+
});
|
|
83
|
+
},
|
|
84
|
+
syncSecret(owner, repo, name, value, scope) {
|
|
85
|
+
return Effect.tryPromise({
|
|
86
|
+
try: async () => {
|
|
87
|
+
if (scope === "actions") {
|
|
88
|
+
const { data: publicKey } = await octokit.actions.getRepoPublicKey({
|
|
89
|
+
owner,
|
|
90
|
+
repo
|
|
91
|
+
});
|
|
92
|
+
const encryptedValue = encryptSecret(publicKey.key, value);
|
|
93
|
+
await octokit.actions.createOrUpdateRepoSecret({
|
|
94
|
+
owner,
|
|
95
|
+
repo,
|
|
96
|
+
secret_name: name,
|
|
97
|
+
encrypted_value: encryptedValue,
|
|
98
|
+
key_id: publicKey.key_id
|
|
99
|
+
});
|
|
100
|
+
} else if (scope === "dependabot") {
|
|
101
|
+
const { data: publicKey } = await octokit.dependabot.getRepoPublicKey({
|
|
102
|
+
owner,
|
|
103
|
+
repo
|
|
104
|
+
});
|
|
105
|
+
const encryptedValue = encryptSecret(publicKey.key, value);
|
|
106
|
+
await octokit.dependabot.createOrUpdateRepoSecret({
|
|
107
|
+
owner,
|
|
108
|
+
repo,
|
|
109
|
+
secret_name: name,
|
|
110
|
+
encrypted_value: encryptedValue,
|
|
111
|
+
key_id: publicKey.key_id
|
|
112
|
+
});
|
|
113
|
+
} else {
|
|
114
|
+
const { data: publicKey } = await octokit.codespaces.getRepoPublicKey({
|
|
115
|
+
owner,
|
|
116
|
+
repo
|
|
117
|
+
});
|
|
118
|
+
const encryptedValue = encryptSecret(publicKey.key, value);
|
|
119
|
+
await octokit.codespaces.createOrUpdateRepoSecret({
|
|
120
|
+
owner,
|
|
121
|
+
repo,
|
|
122
|
+
secret_name: name,
|
|
123
|
+
encrypted_value: encryptedValue,
|
|
124
|
+
key_id: publicKey.key_id
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
catch: wrapError
|
|
129
|
+
});
|
|
130
|
+
},
|
|
131
|
+
syncVariable(owner, repo, name, value) {
|
|
132
|
+
return Effect.tryPromise({
|
|
133
|
+
try: async () => {
|
|
134
|
+
const { data } = await octokit.actions.listRepoVariables({
|
|
135
|
+
owner,
|
|
136
|
+
repo
|
|
137
|
+
});
|
|
138
|
+
if (data.variables.some((v) => v.name === name)) await octokit.actions.updateRepoVariable({
|
|
139
|
+
owner,
|
|
140
|
+
repo,
|
|
141
|
+
name,
|
|
142
|
+
value
|
|
143
|
+
});
|
|
144
|
+
else await octokit.actions.createRepoVariable({
|
|
145
|
+
owner,
|
|
146
|
+
repo,
|
|
147
|
+
name,
|
|
148
|
+
value
|
|
149
|
+
});
|
|
150
|
+
},
|
|
151
|
+
catch: wrapError
|
|
152
|
+
});
|
|
153
|
+
},
|
|
154
|
+
syncSettings(owner, repo, settings) {
|
|
155
|
+
return Effect.tryPromise({
|
|
156
|
+
try: async () => {
|
|
157
|
+
const restSettings = {};
|
|
158
|
+
const graphqlInput = {};
|
|
159
|
+
for (const [key, value] of Object.entries(settings)) {
|
|
160
|
+
if (key === "security_and_analysis") {
|
|
161
|
+
const saa = transformSecurityAndAnalysis(value);
|
|
162
|
+
if (saa !== void 0) restSettings.security_and_analysis = saa;
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
const graphqlField = GRAPHQL_SETTINGS[key];
|
|
166
|
+
if (graphqlField !== void 0) graphqlInput[graphqlField] = value;
|
|
167
|
+
else restSettings[key] = value;
|
|
168
|
+
}
|
|
169
|
+
if (restSettings.allow_merge_commit === false) {
|
|
170
|
+
delete restSettings.merge_commit_title;
|
|
171
|
+
delete restSettings.merge_commit_message;
|
|
172
|
+
}
|
|
173
|
+
if (restSettings.allow_squash_merge === false) {
|
|
174
|
+
delete restSettings.squash_merge_commit_title;
|
|
175
|
+
delete restSettings.squash_merge_commit_message;
|
|
176
|
+
}
|
|
177
|
+
if (Object.keys(restSettings).length > 0) await octokit.repos.update({
|
|
178
|
+
owner,
|
|
179
|
+
repo,
|
|
180
|
+
...restSettings
|
|
181
|
+
});
|
|
182
|
+
if (Object.keys(graphqlInput).length > 0) {
|
|
183
|
+
const { data: repoData } = await octokit.repos.get({
|
|
184
|
+
owner,
|
|
185
|
+
repo
|
|
186
|
+
});
|
|
187
|
+
await octokit.graphql(`mutation UpdateRepository($input: UpdateRepositoryInput!) {
|
|
188
|
+
updateRepository(input: $input) {
|
|
189
|
+
repository { id }
|
|
190
|
+
}
|
|
191
|
+
}`, { input: {
|
|
192
|
+
repositoryId: repoData.node_id,
|
|
193
|
+
...graphqlInput
|
|
194
|
+
} });
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
catch: wrapError
|
|
198
|
+
});
|
|
199
|
+
},
|
|
200
|
+
syncRuleset(owner, repo, name, payload) {
|
|
201
|
+
return Effect.tryPromise({
|
|
202
|
+
try: async () => {
|
|
203
|
+
const { data: existing } = await octokit.repos.getRepoRulesets({
|
|
204
|
+
owner,
|
|
205
|
+
repo
|
|
206
|
+
});
|
|
207
|
+
const match = existing.find((r) => r.name === name);
|
|
208
|
+
const body = {
|
|
209
|
+
name: payload.name,
|
|
210
|
+
target: payload.target,
|
|
211
|
+
enforcement: payload.enforcement,
|
|
212
|
+
...payload.conditions !== void 0 ? { conditions: payload.conditions } : {},
|
|
213
|
+
...payload.rules !== void 0 ? { rules: payload.rules } : {},
|
|
214
|
+
...payload.bypass_actors !== void 0 ? { bypass_actors: payload.bypass_actors } : {}
|
|
215
|
+
};
|
|
216
|
+
if (match) await octokit.repos.updateRepoRuleset({
|
|
217
|
+
owner,
|
|
218
|
+
repo,
|
|
219
|
+
ruleset_id: match.id,
|
|
220
|
+
...body
|
|
221
|
+
});
|
|
222
|
+
else await octokit.repos.createRepoRuleset({
|
|
223
|
+
owner,
|
|
224
|
+
repo,
|
|
225
|
+
...body
|
|
226
|
+
});
|
|
227
|
+
},
|
|
228
|
+
catch: wrapError
|
|
229
|
+
});
|
|
230
|
+
},
|
|
231
|
+
listSecrets(owner, repo, scope) {
|
|
232
|
+
return Effect.tryPromise({
|
|
233
|
+
try: async () => {
|
|
234
|
+
if (scope === "actions") {
|
|
235
|
+
const { data } = await octokit.actions.listRepoSecrets({
|
|
236
|
+
owner,
|
|
237
|
+
repo
|
|
238
|
+
});
|
|
239
|
+
return data.secrets.map((s) => ({ name: s.name }));
|
|
240
|
+
} else if (scope === "dependabot") {
|
|
241
|
+
const { data } = await octokit.dependabot.listRepoSecrets({
|
|
242
|
+
owner,
|
|
243
|
+
repo
|
|
244
|
+
});
|
|
245
|
+
return data.secrets.map((s) => ({ name: s.name }));
|
|
246
|
+
} else {
|
|
247
|
+
const { data } = await octokit.codespaces.listRepoSecrets({
|
|
248
|
+
owner,
|
|
249
|
+
repo
|
|
250
|
+
});
|
|
251
|
+
return data.secrets.map((s) => ({ name: s.name }));
|
|
252
|
+
}
|
|
253
|
+
},
|
|
254
|
+
catch: wrapError
|
|
255
|
+
});
|
|
256
|
+
},
|
|
257
|
+
listVariables(owner, repo) {
|
|
258
|
+
return Effect.tryPromise({
|
|
259
|
+
try: async () => {
|
|
260
|
+
const { data } = await octokit.actions.listRepoVariables({
|
|
261
|
+
owner,
|
|
262
|
+
repo
|
|
263
|
+
});
|
|
264
|
+
return data.variables.map((v) => ({ name: v.name }));
|
|
265
|
+
},
|
|
266
|
+
catch: wrapError
|
|
267
|
+
});
|
|
268
|
+
},
|
|
269
|
+
listRulesets(owner, repo) {
|
|
270
|
+
return Effect.tryPromise({
|
|
271
|
+
try: async () => {
|
|
272
|
+
const { data } = await octokit.repos.getRepoRulesets({
|
|
273
|
+
owner,
|
|
274
|
+
repo
|
|
275
|
+
});
|
|
276
|
+
return data.map((r) => ({
|
|
277
|
+
name: r.name,
|
|
278
|
+
id: r.id,
|
|
279
|
+
source_type: r.source_type
|
|
280
|
+
}));
|
|
281
|
+
},
|
|
282
|
+
catch: wrapError
|
|
283
|
+
});
|
|
284
|
+
},
|
|
285
|
+
deleteSecret(owner, repo, name, scope) {
|
|
286
|
+
return Effect.tryPromise({
|
|
287
|
+
try: async () => {
|
|
288
|
+
if (scope === "actions") await octokit.actions.deleteRepoSecret({
|
|
289
|
+
owner,
|
|
290
|
+
repo,
|
|
291
|
+
secret_name: name
|
|
292
|
+
});
|
|
293
|
+
else if (scope === "dependabot") await octokit.dependabot.deleteRepoSecret({
|
|
294
|
+
owner,
|
|
295
|
+
repo,
|
|
296
|
+
secret_name: name
|
|
297
|
+
});
|
|
298
|
+
else await octokit.codespaces.deleteRepoSecret({
|
|
299
|
+
owner,
|
|
300
|
+
repo,
|
|
301
|
+
secret_name: name
|
|
302
|
+
});
|
|
303
|
+
},
|
|
304
|
+
catch: wrapError
|
|
305
|
+
});
|
|
306
|
+
},
|
|
307
|
+
deleteVariable(owner, repo, name) {
|
|
308
|
+
return Effect.tryPromise({
|
|
309
|
+
try: async () => {
|
|
310
|
+
await octokit.actions.deleteRepoVariable({
|
|
311
|
+
owner,
|
|
312
|
+
repo,
|
|
313
|
+
name
|
|
314
|
+
});
|
|
315
|
+
},
|
|
316
|
+
catch: wrapError
|
|
317
|
+
});
|
|
318
|
+
},
|
|
319
|
+
deleteRuleset(owner, repo, rulesetId) {
|
|
320
|
+
return Effect.tryPromise({
|
|
321
|
+
try: async () => {
|
|
322
|
+
await octokit.repos.deleteRepoRuleset({
|
|
323
|
+
owner,
|
|
324
|
+
repo,
|
|
325
|
+
ruleset_id: rulesetId
|
|
326
|
+
});
|
|
327
|
+
},
|
|
328
|
+
catch: wrapError
|
|
329
|
+
});
|
|
330
|
+
},
|
|
331
|
+
syncEnvironment(owner, repo, name, config) {
|
|
332
|
+
return Effect.tryPromise({
|
|
333
|
+
try: async () => {
|
|
334
|
+
await octokit.repos.createOrUpdateEnvironment({
|
|
335
|
+
owner,
|
|
336
|
+
repo,
|
|
337
|
+
environment_name: name,
|
|
338
|
+
...config
|
|
339
|
+
});
|
|
340
|
+
},
|
|
341
|
+
catch: wrapError
|
|
342
|
+
});
|
|
343
|
+
},
|
|
344
|
+
syncEnvironmentSecret(owner, repo, envName, name, value) {
|
|
345
|
+
return Effect.tryPromise({
|
|
346
|
+
try: async () => {
|
|
347
|
+
const { data: publicKey } = await octokit.actions.getEnvironmentPublicKey({
|
|
348
|
+
owner,
|
|
349
|
+
repo,
|
|
350
|
+
environment_name: envName
|
|
351
|
+
});
|
|
352
|
+
const encrypted_value = encryptSecret(publicKey.key, value);
|
|
353
|
+
await octokit.actions.createOrUpdateEnvironmentSecret({
|
|
354
|
+
owner,
|
|
355
|
+
repo,
|
|
356
|
+
environment_name: envName,
|
|
357
|
+
secret_name: name,
|
|
358
|
+
encrypted_value,
|
|
359
|
+
key_id: publicKey.key_id
|
|
360
|
+
});
|
|
361
|
+
},
|
|
362
|
+
catch: wrapError
|
|
363
|
+
});
|
|
364
|
+
},
|
|
365
|
+
syncEnvironmentVariable(owner, repo, envName, name, value) {
|
|
366
|
+
return Effect.tryPromise({
|
|
367
|
+
try: async () => {
|
|
368
|
+
const { data } = await octokit.actions.listEnvironmentVariables({
|
|
369
|
+
owner,
|
|
370
|
+
repo,
|
|
371
|
+
environment_name: envName
|
|
372
|
+
});
|
|
373
|
+
if (data.variables.some((v) => v.name === name)) await octokit.actions.updateEnvironmentVariable({
|
|
374
|
+
owner,
|
|
375
|
+
repo,
|
|
376
|
+
environment_name: envName,
|
|
377
|
+
name,
|
|
378
|
+
value
|
|
379
|
+
});
|
|
380
|
+
else await octokit.actions.createEnvironmentVariable({
|
|
381
|
+
owner,
|
|
382
|
+
repo,
|
|
383
|
+
environment_name: envName,
|
|
384
|
+
name,
|
|
385
|
+
value
|
|
386
|
+
});
|
|
387
|
+
},
|
|
388
|
+
catch: wrapError
|
|
389
|
+
});
|
|
390
|
+
},
|
|
391
|
+
listEnvironments(owner, repo) {
|
|
392
|
+
return Effect.tryPromise({
|
|
393
|
+
try: async () => {
|
|
394
|
+
const { data } = await octokit.repos.getAllEnvironments({
|
|
395
|
+
owner,
|
|
396
|
+
repo
|
|
397
|
+
});
|
|
398
|
+
return (data.environments ?? []).map((e) => ({ name: e.name }));
|
|
399
|
+
},
|
|
400
|
+
catch: wrapError
|
|
401
|
+
});
|
|
402
|
+
},
|
|
403
|
+
listEnvironmentSecrets(owner, repo, envName) {
|
|
404
|
+
return Effect.tryPromise({
|
|
405
|
+
try: async () => {
|
|
406
|
+
const { data } = await octokit.actions.listEnvironmentSecrets({
|
|
407
|
+
owner,
|
|
408
|
+
repo,
|
|
409
|
+
environment_name: envName
|
|
410
|
+
});
|
|
411
|
+
return data.secrets.map((s) => ({ name: s.name }));
|
|
412
|
+
},
|
|
413
|
+
catch: wrapError
|
|
414
|
+
});
|
|
415
|
+
},
|
|
416
|
+
listEnvironmentVariables(owner, repo, envName) {
|
|
417
|
+
return Effect.tryPromise({
|
|
418
|
+
try: async () => {
|
|
419
|
+
const { data } = await octokit.actions.listEnvironmentVariables({
|
|
420
|
+
owner,
|
|
421
|
+
repo,
|
|
422
|
+
environment_name: envName
|
|
423
|
+
});
|
|
424
|
+
return data.variables.map((v) => ({ name: v.name }));
|
|
425
|
+
},
|
|
426
|
+
catch: wrapError
|
|
427
|
+
});
|
|
428
|
+
},
|
|
429
|
+
deleteEnvironment(owner, repo, name) {
|
|
430
|
+
return Effect.tryPromise({
|
|
431
|
+
try: async () => {
|
|
432
|
+
await octokit.repos.deleteAnEnvironment({
|
|
433
|
+
owner,
|
|
434
|
+
repo,
|
|
435
|
+
environment_name: name
|
|
436
|
+
});
|
|
437
|
+
},
|
|
438
|
+
catch: wrapError
|
|
439
|
+
});
|
|
440
|
+
},
|
|
441
|
+
deleteEnvironmentSecret(owner, repo, envName, name) {
|
|
442
|
+
return Effect.tryPromise({
|
|
443
|
+
try: async () => {
|
|
444
|
+
await octokit.actions.deleteEnvironmentSecret({
|
|
445
|
+
owner,
|
|
446
|
+
repo,
|
|
447
|
+
environment_name: envName,
|
|
448
|
+
secret_name: name
|
|
449
|
+
});
|
|
450
|
+
},
|
|
451
|
+
catch: wrapError
|
|
452
|
+
});
|
|
453
|
+
},
|
|
454
|
+
deleteEnvironmentVariable(owner, repo, envName, name) {
|
|
455
|
+
return Effect.tryPromise({
|
|
456
|
+
try: async () => {
|
|
457
|
+
await octokit.actions.deleteEnvironmentVariable({
|
|
458
|
+
owner,
|
|
459
|
+
repo,
|
|
460
|
+
environment_name: envName,
|
|
461
|
+
name
|
|
462
|
+
});
|
|
463
|
+
},
|
|
464
|
+
catch: wrapError
|
|
465
|
+
});
|
|
466
|
+
},
|
|
467
|
+
getVulnerabilityAlerts(owner, repo) {
|
|
468
|
+
return Effect.tryPromise({
|
|
469
|
+
try: async () => {
|
|
470
|
+
try {
|
|
471
|
+
await octokit.request("GET /repos/{owner}/{repo}/vulnerability-alerts", {
|
|
472
|
+
owner,
|
|
473
|
+
repo
|
|
474
|
+
});
|
|
475
|
+
return true;
|
|
476
|
+
} catch (error) {
|
|
477
|
+
if (error.status === 404) return false;
|
|
478
|
+
throw error;
|
|
479
|
+
}
|
|
480
|
+
},
|
|
481
|
+
catch: wrapError
|
|
482
|
+
});
|
|
483
|
+
},
|
|
484
|
+
setVulnerabilityAlerts(owner, repo, enabled) {
|
|
485
|
+
return Effect.tryPromise({
|
|
486
|
+
try: async () => {
|
|
487
|
+
if (enabled) await octokit.request("PUT /repos/{owner}/{repo}/vulnerability-alerts", {
|
|
488
|
+
owner,
|
|
489
|
+
repo
|
|
490
|
+
});
|
|
491
|
+
else await octokit.request("DELETE /repos/{owner}/{repo}/vulnerability-alerts", {
|
|
492
|
+
owner,
|
|
493
|
+
repo
|
|
494
|
+
});
|
|
495
|
+
},
|
|
496
|
+
catch: wrapError
|
|
497
|
+
});
|
|
498
|
+
},
|
|
499
|
+
getAutomatedSecurityFixes(owner, repo) {
|
|
500
|
+
return Effect.tryPromise({
|
|
501
|
+
try: async () => {
|
|
502
|
+
const { data } = await octokit.request("GET /repos/{owner}/{repo}/automated-security-fixes", {
|
|
503
|
+
owner,
|
|
504
|
+
repo
|
|
505
|
+
});
|
|
506
|
+
return Boolean(data.enabled);
|
|
507
|
+
},
|
|
508
|
+
catch: wrapError
|
|
509
|
+
});
|
|
510
|
+
},
|
|
511
|
+
setAutomatedSecurityFixes(owner, repo, enabled) {
|
|
512
|
+
return Effect.tryPromise({
|
|
513
|
+
try: async () => {
|
|
514
|
+
if (enabled) await octokit.request("PUT /repos/{owner}/{repo}/automated-security-fixes", {
|
|
515
|
+
owner,
|
|
516
|
+
repo
|
|
517
|
+
});
|
|
518
|
+
else await octokit.request("DELETE /repos/{owner}/{repo}/automated-security-fixes", {
|
|
519
|
+
owner,
|
|
520
|
+
repo
|
|
521
|
+
});
|
|
522
|
+
},
|
|
523
|
+
catch: wrapError
|
|
524
|
+
});
|
|
525
|
+
},
|
|
526
|
+
getPrivateVulnerabilityReporting(owner, repo) {
|
|
527
|
+
return Effect.tryPromise({
|
|
528
|
+
try: async () => {
|
|
529
|
+
const { data } = await octokit.request("GET /repos/{owner}/{repo}/private-vulnerability-reporting", {
|
|
530
|
+
owner,
|
|
531
|
+
repo
|
|
532
|
+
});
|
|
533
|
+
return Boolean(data.enabled);
|
|
534
|
+
},
|
|
535
|
+
catch: wrapError
|
|
536
|
+
});
|
|
537
|
+
},
|
|
538
|
+
setPrivateVulnerabilityReporting(owner, repo, enabled) {
|
|
539
|
+
return Effect.tryPromise({
|
|
540
|
+
try: async () => {
|
|
541
|
+
if (enabled) await octokit.request("PUT /repos/{owner}/{repo}/private-vulnerability-reporting", {
|
|
542
|
+
owner,
|
|
543
|
+
repo
|
|
544
|
+
});
|
|
545
|
+
else await octokit.request("DELETE /repos/{owner}/{repo}/private-vulnerability-reporting", {
|
|
546
|
+
owner,
|
|
547
|
+
repo
|
|
548
|
+
});
|
|
549
|
+
},
|
|
550
|
+
catch: wrapError
|
|
551
|
+
});
|
|
552
|
+
},
|
|
553
|
+
updateCodeScanningDefaultSetup(owner, repo, config) {
|
|
554
|
+
return Effect.tryPromise({
|
|
555
|
+
try: async () => {
|
|
556
|
+
const body = {};
|
|
557
|
+
if (config.state !== void 0) body.state = config.state;
|
|
558
|
+
if (config.languages !== void 0) body.languages = [...config.languages];
|
|
559
|
+
if (config.query_suite !== void 0) body.query_suite = config.query_suite;
|
|
560
|
+
if (config.threat_model !== void 0) body.threat_model = config.threat_model;
|
|
561
|
+
if (config.runner_type !== void 0) body.runner_type = config.runner_type;
|
|
562
|
+
if (config.runner_label !== void 0) body.runner_label = config.runner_label;
|
|
563
|
+
await octokit.request("PATCH /repos/{owner}/{repo}/code-scanning/default-setup", {
|
|
564
|
+
owner,
|
|
565
|
+
repo,
|
|
566
|
+
...body
|
|
567
|
+
});
|
|
568
|
+
},
|
|
569
|
+
catch: wrapError
|
|
570
|
+
});
|
|
571
|
+
},
|
|
572
|
+
listRepoLanguages(owner, repo) {
|
|
573
|
+
return Effect.tryPromise({
|
|
574
|
+
try: async () => {
|
|
575
|
+
const { data } = await octokit.repos.listLanguages({
|
|
576
|
+
owner,
|
|
577
|
+
repo
|
|
578
|
+
});
|
|
579
|
+
return Object.keys(data);
|
|
580
|
+
},
|
|
581
|
+
catch: wrapError
|
|
582
|
+
});
|
|
583
|
+
},
|
|
584
|
+
resolveTeamId(org, slug) {
|
|
585
|
+
return Effect.tryPromise({
|
|
586
|
+
try: async () => {
|
|
587
|
+
const cacheKey = `${org}:${slug}`;
|
|
588
|
+
const cached = teamIdCache.get(cacheKey);
|
|
589
|
+
if (cached !== void 0) return cached;
|
|
590
|
+
const { data } = await octokit.teams.getByName({
|
|
591
|
+
org,
|
|
592
|
+
team_slug: slug
|
|
593
|
+
});
|
|
594
|
+
teamIdCache.set(cacheKey, data.id);
|
|
595
|
+
return data.id;
|
|
596
|
+
},
|
|
597
|
+
catch: wrapError
|
|
598
|
+
});
|
|
599
|
+
},
|
|
600
|
+
resolveRoleId(org, name) {
|
|
601
|
+
return Effect.tryPromise({
|
|
602
|
+
try: async () => {
|
|
603
|
+
const cacheKey = `${org}:${name}`;
|
|
604
|
+
const cached = roleIdCache.get(cacheKey);
|
|
605
|
+
if (cached !== void 0) return cached;
|
|
606
|
+
const { data } = await octokit.request("GET /orgs/{org}/organization-roles", { org });
|
|
607
|
+
const roles = data.roles ?? [];
|
|
608
|
+
const role = roles.find((r) => r.name === name);
|
|
609
|
+
if (!role) throw new Error(`organization role '${name}' not found in '${org}' (available: ${roles.map((r) => r.name).join(", ") || "none"})`);
|
|
610
|
+
roleIdCache.set(cacheKey, role.id);
|
|
611
|
+
return role.id;
|
|
612
|
+
},
|
|
613
|
+
catch: wrapError
|
|
614
|
+
});
|
|
615
|
+
}
|
|
616
|
+
};
|
|
617
|
+
})());
|
|
618
|
+
}
|
|
619
|
+
function GitHubClientTest() {
|
|
620
|
+
const recorded = [];
|
|
621
|
+
return {
|
|
622
|
+
layer: Layer.succeed(GitHubClient, {
|
|
623
|
+
getOwnerType(_owner) {
|
|
624
|
+
return Effect.succeed("User");
|
|
625
|
+
},
|
|
626
|
+
syncSecret(owner, repo, name, _value, scope) {
|
|
627
|
+
recorded.push({
|
|
628
|
+
method: "syncSecret",
|
|
629
|
+
args: {
|
|
630
|
+
owner,
|
|
631
|
+
repo,
|
|
632
|
+
name,
|
|
633
|
+
scope
|
|
634
|
+
}
|
|
635
|
+
});
|
|
636
|
+
return Effect.void;
|
|
637
|
+
},
|
|
638
|
+
syncVariable(owner, repo, name, _value) {
|
|
639
|
+
recorded.push({
|
|
640
|
+
method: "syncVariable",
|
|
641
|
+
args: {
|
|
642
|
+
owner,
|
|
643
|
+
repo,
|
|
644
|
+
name
|
|
645
|
+
}
|
|
646
|
+
});
|
|
647
|
+
return Effect.void;
|
|
648
|
+
},
|
|
649
|
+
syncSettings(owner, repo, settings) {
|
|
650
|
+
recorded.push({
|
|
651
|
+
method: "syncSettings",
|
|
652
|
+
args: {
|
|
653
|
+
owner,
|
|
654
|
+
repo,
|
|
655
|
+
settings
|
|
656
|
+
}
|
|
657
|
+
});
|
|
658
|
+
return Effect.void;
|
|
659
|
+
},
|
|
660
|
+
syncRuleset(owner, repo, name, _payload) {
|
|
661
|
+
recorded.push({
|
|
662
|
+
method: "syncRuleset",
|
|
663
|
+
args: {
|
|
664
|
+
owner,
|
|
665
|
+
repo,
|
|
666
|
+
name
|
|
667
|
+
}
|
|
668
|
+
});
|
|
669
|
+
return Effect.void;
|
|
670
|
+
},
|
|
671
|
+
listSecrets(_owner, _repo, _scope) {
|
|
672
|
+
return Effect.succeed([]);
|
|
673
|
+
},
|
|
674
|
+
listVariables(_owner, _repo) {
|
|
675
|
+
return Effect.succeed([]);
|
|
676
|
+
},
|
|
677
|
+
listRulesets(_owner, _repo) {
|
|
678
|
+
return Effect.succeed([]);
|
|
679
|
+
},
|
|
680
|
+
deleteSecret(owner, repo, name, scope) {
|
|
681
|
+
recorded.push({
|
|
682
|
+
method: "deleteSecret",
|
|
683
|
+
args: {
|
|
684
|
+
owner,
|
|
685
|
+
repo,
|
|
686
|
+
name,
|
|
687
|
+
scope
|
|
688
|
+
}
|
|
689
|
+
});
|
|
690
|
+
return Effect.void;
|
|
691
|
+
},
|
|
692
|
+
deleteVariable(owner, repo, name) {
|
|
693
|
+
recorded.push({
|
|
694
|
+
method: "deleteVariable",
|
|
695
|
+
args: {
|
|
696
|
+
owner,
|
|
697
|
+
repo,
|
|
698
|
+
name
|
|
699
|
+
}
|
|
700
|
+
});
|
|
701
|
+
return Effect.void;
|
|
702
|
+
},
|
|
703
|
+
deleteRuleset(owner, repo, rulesetId) {
|
|
704
|
+
recorded.push({
|
|
705
|
+
method: "deleteRuleset",
|
|
706
|
+
args: {
|
|
707
|
+
owner,
|
|
708
|
+
repo,
|
|
709
|
+
rulesetId
|
|
710
|
+
}
|
|
711
|
+
});
|
|
712
|
+
return Effect.void;
|
|
713
|
+
},
|
|
714
|
+
syncEnvironment(owner, repo, name, _config) {
|
|
715
|
+
recorded.push({
|
|
716
|
+
method: "syncEnvironment",
|
|
717
|
+
args: {
|
|
718
|
+
owner,
|
|
719
|
+
repo,
|
|
720
|
+
name
|
|
721
|
+
}
|
|
722
|
+
});
|
|
723
|
+
return Effect.void;
|
|
724
|
+
},
|
|
725
|
+
syncEnvironmentSecret(owner, repo, envName, name, _value) {
|
|
726
|
+
recorded.push({
|
|
727
|
+
method: "syncEnvironmentSecret",
|
|
728
|
+
args: {
|
|
729
|
+
owner,
|
|
730
|
+
repo,
|
|
731
|
+
envName,
|
|
732
|
+
name
|
|
733
|
+
}
|
|
734
|
+
});
|
|
735
|
+
return Effect.void;
|
|
736
|
+
},
|
|
737
|
+
syncEnvironmentVariable(owner, repo, envName, name, _value) {
|
|
738
|
+
recorded.push({
|
|
739
|
+
method: "syncEnvironmentVariable",
|
|
740
|
+
args: {
|
|
741
|
+
owner,
|
|
742
|
+
repo,
|
|
743
|
+
envName,
|
|
744
|
+
name
|
|
745
|
+
}
|
|
746
|
+
});
|
|
747
|
+
return Effect.void;
|
|
748
|
+
},
|
|
749
|
+
listEnvironments(_owner, _repo) {
|
|
750
|
+
return Effect.succeed([]);
|
|
751
|
+
},
|
|
752
|
+
listEnvironmentSecrets(_owner, _repo, _envName) {
|
|
753
|
+
return Effect.succeed([]);
|
|
754
|
+
},
|
|
755
|
+
listEnvironmentVariables(_owner, _repo, _envName) {
|
|
756
|
+
return Effect.succeed([]);
|
|
757
|
+
},
|
|
758
|
+
deleteEnvironment(owner, repo, name) {
|
|
759
|
+
recorded.push({
|
|
760
|
+
method: "deleteEnvironment",
|
|
761
|
+
args: {
|
|
762
|
+
owner,
|
|
763
|
+
repo,
|
|
764
|
+
name
|
|
765
|
+
}
|
|
766
|
+
});
|
|
767
|
+
return Effect.void;
|
|
768
|
+
},
|
|
769
|
+
deleteEnvironmentSecret(owner, repo, envName, name) {
|
|
770
|
+
recorded.push({
|
|
771
|
+
method: "deleteEnvironmentSecret",
|
|
772
|
+
args: {
|
|
773
|
+
owner,
|
|
774
|
+
repo,
|
|
775
|
+
envName,
|
|
776
|
+
name
|
|
777
|
+
}
|
|
778
|
+
});
|
|
779
|
+
return Effect.void;
|
|
780
|
+
},
|
|
781
|
+
deleteEnvironmentVariable(owner, repo, envName, name) {
|
|
782
|
+
recorded.push({
|
|
783
|
+
method: "deleteEnvironmentVariable",
|
|
784
|
+
args: {
|
|
785
|
+
owner,
|
|
786
|
+
repo,
|
|
787
|
+
envName,
|
|
788
|
+
name
|
|
789
|
+
}
|
|
790
|
+
});
|
|
791
|
+
return Effect.void;
|
|
792
|
+
},
|
|
793
|
+
getVulnerabilityAlerts(_owner, _repo) {
|
|
794
|
+
return Effect.succeed(false);
|
|
795
|
+
},
|
|
796
|
+
setVulnerabilityAlerts(owner, repo, enabled) {
|
|
797
|
+
recorded.push({
|
|
798
|
+
method: "setVulnerabilityAlerts",
|
|
799
|
+
args: {
|
|
800
|
+
owner,
|
|
801
|
+
repo,
|
|
802
|
+
enabled
|
|
803
|
+
}
|
|
804
|
+
});
|
|
805
|
+
return Effect.void;
|
|
806
|
+
},
|
|
807
|
+
getAutomatedSecurityFixes(_owner, _repo) {
|
|
808
|
+
return Effect.succeed(false);
|
|
809
|
+
},
|
|
810
|
+
setAutomatedSecurityFixes(owner, repo, enabled) {
|
|
811
|
+
recorded.push({
|
|
812
|
+
method: "setAutomatedSecurityFixes",
|
|
813
|
+
args: {
|
|
814
|
+
owner,
|
|
815
|
+
repo,
|
|
816
|
+
enabled
|
|
817
|
+
}
|
|
818
|
+
});
|
|
819
|
+
return Effect.void;
|
|
820
|
+
},
|
|
821
|
+
getPrivateVulnerabilityReporting(_owner, _repo) {
|
|
822
|
+
return Effect.succeed(false);
|
|
823
|
+
},
|
|
824
|
+
setPrivateVulnerabilityReporting(owner, repo, enabled) {
|
|
825
|
+
recorded.push({
|
|
826
|
+
method: "setPrivateVulnerabilityReporting",
|
|
827
|
+
args: {
|
|
828
|
+
owner,
|
|
829
|
+
repo,
|
|
830
|
+
enabled
|
|
831
|
+
}
|
|
832
|
+
});
|
|
833
|
+
return Effect.void;
|
|
834
|
+
},
|
|
835
|
+
updateCodeScanningDefaultSetup(owner, repo, config) {
|
|
836
|
+
recorded.push({
|
|
837
|
+
method: "updateCodeScanningDefaultSetup",
|
|
838
|
+
args: {
|
|
839
|
+
owner,
|
|
840
|
+
repo,
|
|
841
|
+
config
|
|
842
|
+
}
|
|
843
|
+
});
|
|
844
|
+
return Effect.void;
|
|
845
|
+
},
|
|
846
|
+
listRepoLanguages(_owner, _repo) {
|
|
847
|
+
return Effect.succeed([]);
|
|
848
|
+
},
|
|
849
|
+
resolveTeamId(org, slug) {
|
|
850
|
+
recorded.push({
|
|
851
|
+
method: "resolveTeamId",
|
|
852
|
+
args: {
|
|
853
|
+
org,
|
|
854
|
+
slug
|
|
855
|
+
}
|
|
856
|
+
});
|
|
857
|
+
return Effect.succeed(0);
|
|
858
|
+
},
|
|
859
|
+
resolveRoleId(org, name) {
|
|
860
|
+
recorded.push({
|
|
861
|
+
method: "resolveRoleId",
|
|
862
|
+
args: {
|
|
863
|
+
org,
|
|
864
|
+
name
|
|
865
|
+
}
|
|
866
|
+
});
|
|
867
|
+
return Effect.succeed(0);
|
|
868
|
+
}
|
|
869
|
+
}),
|
|
870
|
+
calls: () => [...recorded]
|
|
871
|
+
};
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
//#endregion
|
|
875
|
+
export { GitHubClient, GitHubClientLive, GitHubClientTest, ORG_ONLY_SETTINGS };
|