@bakers-build/addon-forge-github 0.9.1-next.7 → 0.10.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/dist/src/index.js +6 -63
- package/options.schema.json +18 -5
- package/package.json +5 -5
package/dist/src/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// shortly before it expires; or, with `auth: 'gh'`, the conductor's own `gh`
|
|
4
4
|
// login. The repository comes from config, never from a clone's `origin`.
|
|
5
5
|
import { createSign } from 'node:crypto';
|
|
6
|
-
import { cliToken, requestJson,
|
|
6
|
+
import { cliToken, requestJson, } from '@bakers-build/addon-forge/backend';
|
|
7
7
|
const API = 'https://api.github.com';
|
|
8
8
|
const REFRESH_MS = 60_000;
|
|
9
9
|
function settingsOf(raw) {
|
|
@@ -64,7 +64,6 @@ function stateStatus(state) {
|
|
|
64
64
|
return 'success';
|
|
65
65
|
return state === 'pending' ? 'pending' : 'failure';
|
|
66
66
|
}
|
|
67
|
-
const NO_RUN = { run_id: null, job_id: null, run_started_at: null };
|
|
68
67
|
const basic = (token) => Buffer.from(`x-access-token:${token}`).toString('base64');
|
|
69
68
|
const factory = async (raw, context) => {
|
|
70
69
|
const s = settingsOf(raw);
|
|
@@ -102,7 +101,7 @@ const factory = async (raw, context) => {
|
|
|
102
101
|
};
|
|
103
102
|
return out.token;
|
|
104
103
|
};
|
|
105
|
-
const api = async (path, init = {}) =>
|
|
104
|
+
const api = async (path, init = {}) => requestJson(doFetch, `${repo}${path}`, {
|
|
106
105
|
method: init.method ?? 'GET',
|
|
107
106
|
headers: {
|
|
108
107
|
authorization: `Bearer ${await accessToken()}`,
|
|
@@ -136,11 +135,11 @@ const factory = async (raw, context) => {
|
|
|
136
135
|
openPrsWithBase: async (base) => (await api(`/pulls?state=open&base=${encodeURIComponent(base)}&per_page=100`))
|
|
137
136
|
.filter((p) => p.base.ref === base)
|
|
138
137
|
.map(prOf),
|
|
139
|
-
checks: async (ref, base
|
|
138
|
+
checks: async (ref, base) => {
|
|
140
139
|
const at = encodeURIComponent(ref);
|
|
141
140
|
const b = encodeURIComponent(base);
|
|
142
141
|
const [runs, status, rules, branch] = await Promise.all([
|
|
143
|
-
api(`/commits/${at}/check-runs?
|
|
142
|
+
api(`/commits/${at}/check-runs?per_page=100`),
|
|
144
143
|
api(`/commits/${at}/status`),
|
|
145
144
|
api(`/rules/branches/${b}`, { allow404: true }),
|
|
146
145
|
api(`/branches/${b}`, { allow404: true }),
|
|
@@ -152,67 +151,21 @@ const factory = async (raw, context) => {
|
|
|
152
151
|
.map((c) => c.context),
|
|
153
152
|
...(branch?.protection?.required_status_checks?.contexts ?? []),
|
|
154
153
|
]);
|
|
155
|
-
// shortcut: one page only; a partial list could hide a newer attempt, so refuse it; paginate when this trips.
|
|
156
|
-
if ((runs.total_count ?? 0) > runs.check_runs.length)
|
|
157
|
-
throw new Error(`${ref} has ${runs.total_count} check runs, more than the ${runs.check_runs.length} one page holds; the result would be partial`);
|
|
158
|
-
// `ref` may be a branch: the runs lookup needs the commit the check runs report.
|
|
159
|
-
const sha = runs.check_runs[0]?.head_sha;
|
|
160
|
-
const workflowRuns = sha
|
|
161
|
-
? (await api(`/actions/runs?head_sha=${encodeURIComponent(sha)}&per_page=100`)).workflow_runs
|
|
162
|
-
: [];
|
|
163
|
-
const bySuite = new Map(workflowRuns.map((w) => [w.check_suite_id, w]));
|
|
164
|
-
const newest = new Map();
|
|
165
|
-
for (const w of workflowRuns) {
|
|
166
|
-
const kept = newest.get(w.workflow_id);
|
|
167
|
-
if (!kept ||
|
|
168
|
-
w.run_started_at > kept.run_started_at ||
|
|
169
|
-
(w.run_started_at === kept.run_started_at && w.id > kept.id))
|
|
170
|
-
newest.set(w.workflow_id, w);
|
|
171
|
-
}
|
|
172
|
-
let jobs = runs.check_runs.map((r) => ({
|
|
173
|
-
r,
|
|
174
|
-
w: r.check_suite ? bySuite.get(r.check_suite.id) : undefined,
|
|
175
|
-
}));
|
|
176
|
-
if (!opts.allRuns) {
|
|
177
|
-
// An Actions check run without a listed run belongs to an earlier attempt.
|
|
178
|
-
jobs = jobs.filter(({ r, w }) => w
|
|
179
|
-
? newest.get(w.workflow_id) === w
|
|
180
|
-
: r.app?.slug !== 'github-actions');
|
|
181
|
-
// A re-run attempt is a newer check run of the same name in the same run.
|
|
182
|
-
const latest = new Map();
|
|
183
|
-
const key = (j) => `${j.w?.id ?? ''}\0${j.r.name}`;
|
|
184
|
-
for (const j of jobs)
|
|
185
|
-
latest.set(key(j), Math.max(latest.get(key(j)) ?? 0, j.r.id));
|
|
186
|
-
jobs = jobs.filter((j) => latest.get(key(j)) === j.r.id);
|
|
187
|
-
}
|
|
188
154
|
const rows = [
|
|
189
|
-
...
|
|
155
|
+
...runs.check_runs.map((r) => ({
|
|
190
156
|
name: r.name,
|
|
191
157
|
status: runStatus(r),
|
|
192
158
|
required: required.has(r.name),
|
|
193
|
-
conclusion: r.conclusion,
|
|
194
|
-
run_id: w?.id ?? null,
|
|
195
|
-
// An Actions job's id is its check run's id.
|
|
196
|
-
job_id: w ? r.id : null,
|
|
197
|
-
run_started_at: w?.run_started_at ?? null,
|
|
198
159
|
})),
|
|
199
160
|
...status.statuses.map((st) => ({
|
|
200
161
|
name: st.context,
|
|
201
162
|
status: stateStatus(st.state),
|
|
202
163
|
required: required.has(st.context),
|
|
203
|
-
conclusion: st.state,
|
|
204
|
-
...NO_RUN,
|
|
205
164
|
})),
|
|
206
165
|
];
|
|
207
166
|
for (const name of required)
|
|
208
167
|
if (!rows.some((r) => r.name === name))
|
|
209
|
-
rows.push({
|
|
210
|
-
name,
|
|
211
|
-
status: 'missing',
|
|
212
|
-
required: true,
|
|
213
|
-
conclusion: null,
|
|
214
|
-
...NO_RUN,
|
|
215
|
-
});
|
|
168
|
+
rows.push({ name, status: 'missing', required: true });
|
|
216
169
|
return rows;
|
|
217
170
|
},
|
|
218
171
|
mergeBlocker: async (pr) => {
|
|
@@ -237,16 +190,6 @@ const factory = async (raw, context) => {
|
|
|
237
190
|
});
|
|
238
191
|
},
|
|
239
192
|
isMerged: async (pr) => (await pull(pr.number)).merged === true,
|
|
240
|
-
// The API redirects to a signed download URL; fetch follows it.
|
|
241
|
-
jobLog: async (jobId, maxBytes) => {
|
|
242
|
-
const log = (await api(`/actions/jobs/${jobId}/logs`, {
|
|
243
|
-
allow404: true,
|
|
244
|
-
text: true,
|
|
245
|
-
}));
|
|
246
|
-
if (log === undefined)
|
|
247
|
-
throw new Error(`job ${jobId} is not a job of ${s.repository}`);
|
|
248
|
-
return tail(log, maxBytes);
|
|
249
|
-
},
|
|
250
193
|
};
|
|
251
194
|
return backend;
|
|
252
195
|
};
|
package/options.schema.json
CHANGED
|
@@ -5,7 +5,9 @@
|
|
|
5
5
|
"description": "The forge entry's with.backend.with: a GitHub App, or the conductor's gh login with auth 'gh'.",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"additionalProperties": false,
|
|
8
|
-
"required": [
|
|
8
|
+
"required": [
|
|
9
|
+
"repository"
|
|
10
|
+
],
|
|
9
11
|
"properties": {
|
|
10
12
|
"repository": {
|
|
11
13
|
"type": "string",
|
|
@@ -17,12 +19,18 @@
|
|
|
17
19
|
"description": "Use the conductor's own gh login instead of a GitHub App."
|
|
18
20
|
},
|
|
19
21
|
"app_id": {
|
|
20
|
-
"type": [
|
|
22
|
+
"type": [
|
|
23
|
+
"integer",
|
|
24
|
+
"string"
|
|
25
|
+
],
|
|
21
26
|
"minLength": 1,
|
|
22
27
|
"description": "The GitHub App's ID; required unless `auth` is set."
|
|
23
28
|
},
|
|
24
29
|
"installation_id": {
|
|
25
|
-
"type": [
|
|
30
|
+
"type": [
|
|
31
|
+
"integer",
|
|
32
|
+
"string"
|
|
33
|
+
],
|
|
26
34
|
"minLength": 1,
|
|
27
35
|
"description": "The App's installation on the repository; required unless `auth` is set."
|
|
28
36
|
},
|
|
@@ -38,9 +46,14 @@
|
|
|
38
46
|
}
|
|
39
47
|
},
|
|
40
48
|
"if": {
|
|
41
|
-
"required": [
|
|
49
|
+
"required": [
|
|
50
|
+
"auth"
|
|
51
|
+
]
|
|
42
52
|
},
|
|
43
53
|
"else": {
|
|
44
|
-
"required": [
|
|
54
|
+
"required": [
|
|
55
|
+
"app_id",
|
|
56
|
+
"installation_id"
|
|
57
|
+
]
|
|
45
58
|
}
|
|
46
59
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bakers-build/addon-forge-github",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "bakers.build forge backend for GitHub: a GitHub App installation token scoped to one repository. Implements @bakers-build/addon-forge's ForgeBackend contract.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -21,11 +21,11 @@
|
|
|
21
21
|
"directory": "bakers-cli/addons/forge-github"
|
|
22
22
|
},
|
|
23
23
|
"peerDependencies": {
|
|
24
|
-
"@bakers-build/addon-forge": "^0.
|
|
25
|
-
"@bakers-build/core": "^0.
|
|
24
|
+
"@bakers-build/addon-forge": "^0.10.0",
|
|
25
|
+
"@bakers-build/core": "^0.10.0"
|
|
26
26
|
},
|
|
27
27
|
"bakersBuild": {
|
|
28
|
-
"commit": "
|
|
29
|
-
"channel": "
|
|
28
|
+
"commit": "5857b00008bc0926fd52fdc8d9366d6d52444e35",
|
|
29
|
+
"channel": "latest"
|
|
30
30
|
}
|
|
31
31
|
}
|