@argos-ci/core 5.1.2 → 5.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/dist/index.cjs +3 -3
- package/dist/index.d.mts +325 -0
- package/dist/index.mjs +1389 -0
- package/package.json +12 -12
- package/dist/index.d.ts +0 -702
- package/dist/index.js +0 -1519
package/dist/index.js
DELETED
|
@@ -1,1519 +0,0 @@
|
|
|
1
|
-
// src/config.ts
|
|
2
|
-
import convict from "convict";
|
|
3
|
-
|
|
4
|
-
// src/ci-environment/git.ts
|
|
5
|
-
import { execSync } from "child_process";
|
|
6
|
-
|
|
7
|
-
// src/debug.ts
|
|
8
|
-
import createDebug from "debug";
|
|
9
|
-
var KEY = "@argos-ci/core";
|
|
10
|
-
var debug = createDebug(KEY);
|
|
11
|
-
var isDebugEnabled = createDebug.enabled(KEY);
|
|
12
|
-
var debugTime = (arg) => {
|
|
13
|
-
if (isDebugEnabled) {
|
|
14
|
-
console.time(arg);
|
|
15
|
-
}
|
|
16
|
-
};
|
|
17
|
-
var debugTimeEnd = (arg) => {
|
|
18
|
-
if (isDebugEnabled) {
|
|
19
|
-
console.timeEnd(arg);
|
|
20
|
-
}
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
// src/ci-environment/git.ts
|
|
24
|
-
function checkIsGitRepository() {
|
|
25
|
-
try {
|
|
26
|
-
return execSync("git rev-parse --is-inside-work-tree").toString().trim() === "true";
|
|
27
|
-
} catch {
|
|
28
|
-
return false;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
function head() {
|
|
32
|
-
try {
|
|
33
|
-
return execSync("git rev-parse HEAD").toString().trim();
|
|
34
|
-
} catch {
|
|
35
|
-
return null;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
function branch() {
|
|
39
|
-
try {
|
|
40
|
-
const headRef = execSync("git rev-parse --abbrev-ref HEAD").toString().trim();
|
|
41
|
-
if (headRef === "HEAD") {
|
|
42
|
-
return null;
|
|
43
|
-
}
|
|
44
|
-
return headRef;
|
|
45
|
-
} catch {
|
|
46
|
-
return null;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
function getRepositoryURL() {
|
|
50
|
-
try {
|
|
51
|
-
const url = execSync("git config --get remote.origin.url").toString().trim();
|
|
52
|
-
return url;
|
|
53
|
-
} catch {
|
|
54
|
-
return null;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
function gitMergeBase(input) {
|
|
58
|
-
try {
|
|
59
|
-
return execSync(`git merge-base ${input.head} ${input.base}`).toString().trim();
|
|
60
|
-
} catch (error) {
|
|
61
|
-
if (checkIsExecError(error) && error.status === 1 && error.stderr.toString() === "") {
|
|
62
|
-
return null;
|
|
63
|
-
}
|
|
64
|
-
throw error;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
function gitFetch(input) {
|
|
68
|
-
execSync(
|
|
69
|
-
`git fetch --force --update-head-ok --depth ${input.depth} origin ${input.ref}:${input.target}`
|
|
70
|
-
);
|
|
71
|
-
}
|
|
72
|
-
function checkIsExecError(error) {
|
|
73
|
-
return error instanceof Error && "status" in error && typeof error.status === "number" && "stderr" in error && Buffer.isBuffer(error.stderr);
|
|
74
|
-
}
|
|
75
|
-
function getMergeBaseCommitSha(input) {
|
|
76
|
-
let depth = 200;
|
|
77
|
-
const argosBaseRef = `argos/${input.base}`;
|
|
78
|
-
const argosHeadRef = `argos/${input.head}`;
|
|
79
|
-
while (depth < 1e3) {
|
|
80
|
-
gitFetch({ ref: input.head, depth, target: argosHeadRef });
|
|
81
|
-
gitFetch({ ref: input.base, depth, target: argosBaseRef });
|
|
82
|
-
const mergeBase = gitMergeBase({
|
|
83
|
-
base: argosBaseRef,
|
|
84
|
-
head: argosHeadRef
|
|
85
|
-
});
|
|
86
|
-
if (mergeBase) {
|
|
87
|
-
return mergeBase;
|
|
88
|
-
}
|
|
89
|
-
depth += 200;
|
|
90
|
-
}
|
|
91
|
-
if (isDebugEnabled) {
|
|
92
|
-
const headShas = listShas(argosHeadRef);
|
|
93
|
-
const baseShas = listShas(argosBaseRef);
|
|
94
|
-
debug(
|
|
95
|
-
`No merge base found for ${input.head} and ${input.base} with depth ${depth}`
|
|
96
|
-
);
|
|
97
|
-
debug(
|
|
98
|
-
`Found ${headShas.length} commits in ${input.head}: ${headShas.join(", ")}`
|
|
99
|
-
);
|
|
100
|
-
debug(
|
|
101
|
-
`Found ${baseShas.length} commits in ${input.base}: ${baseShas.join(", ")}`
|
|
102
|
-
);
|
|
103
|
-
}
|
|
104
|
-
return null;
|
|
105
|
-
}
|
|
106
|
-
function listShas(path, maxCount) {
|
|
107
|
-
const maxCountArg = maxCount ? `--max-count=${maxCount}` : "";
|
|
108
|
-
const raw = execSync(`git log --format="%H" ${maxCountArg} ${path}`.trim());
|
|
109
|
-
const shas = raw.toString().trim().split("\n");
|
|
110
|
-
return shas;
|
|
111
|
-
}
|
|
112
|
-
function listParentCommits(input) {
|
|
113
|
-
const limit = 200;
|
|
114
|
-
try {
|
|
115
|
-
execSync(`git fetch --depth=${limit} origin ${input.sha}`);
|
|
116
|
-
} catch (error) {
|
|
117
|
-
if (error instanceof Error && error.message.includes("not our ref")) {
|
|
118
|
-
return [];
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
return listShas(input.sha, limit);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// src/ci-environment/services/bitrise.ts
|
|
125
|
-
function getPrNumber(context) {
|
|
126
|
-
const { env } = context;
|
|
127
|
-
return env.BITRISE_PULL_REQUEST ? Number(env.BITRISE_PULL_REQUEST) : null;
|
|
128
|
-
}
|
|
129
|
-
function getRepository(context) {
|
|
130
|
-
const { env } = context;
|
|
131
|
-
if (env.BITRISEIO_GIT_REPOSITORY_OWNER && env.BITRISEIO_GIT_REPOSITORY_SLUG) {
|
|
132
|
-
return `${env.BITRISEIO_GIT_REPOSITORY_OWNER}/${env.BITRISEIO_GIT_REPOSITORY_SLUG}`;
|
|
133
|
-
}
|
|
134
|
-
return null;
|
|
135
|
-
}
|
|
136
|
-
var service = {
|
|
137
|
-
name: "Bitrise",
|
|
138
|
-
key: "bitrise",
|
|
139
|
-
detect: ({ env }) => Boolean(env.BITRISE_IO),
|
|
140
|
-
config: (context) => {
|
|
141
|
-
const { env } = context;
|
|
142
|
-
const repository = getRepository(context);
|
|
143
|
-
return {
|
|
144
|
-
commit: env.BITRISE_GIT_COMMIT || null,
|
|
145
|
-
branch: env.BITRISE_GIT_BRANCH || null,
|
|
146
|
-
repository,
|
|
147
|
-
originalRepository: repository,
|
|
148
|
-
jobId: null,
|
|
149
|
-
runId: null,
|
|
150
|
-
runAttempt: null,
|
|
151
|
-
prNumber: getPrNumber({ env }),
|
|
152
|
-
prHeadCommit: null,
|
|
153
|
-
prBaseBranch: null,
|
|
154
|
-
nonce: env.BITRISEIO_PIPELINE_ID || null,
|
|
155
|
-
mergeQueue: false
|
|
156
|
-
};
|
|
157
|
-
},
|
|
158
|
-
getMergeBaseCommitSha,
|
|
159
|
-
listParentCommits
|
|
160
|
-
};
|
|
161
|
-
var bitrise_default = service;
|
|
162
|
-
|
|
163
|
-
// src/util/url.ts
|
|
164
|
-
function getRepositoryNameFromURL(url) {
|
|
165
|
-
const sshMatch = url.match(/^git@[^:]+:([^/]+)\/(.+?)(?:\.git)?$/);
|
|
166
|
-
if (sshMatch && sshMatch[1] && sshMatch[2]) {
|
|
167
|
-
return `${sshMatch[1]}/${sshMatch[2]}`;
|
|
168
|
-
}
|
|
169
|
-
const httpsMatch = url.match(
|
|
170
|
-
/^(?:https?|git):\/\/[^/]+\/([^/]+)\/(.+?)(?:\.git)?$/
|
|
171
|
-
);
|
|
172
|
-
if (httpsMatch && httpsMatch[1] && httpsMatch[2]) {
|
|
173
|
-
return `${httpsMatch[1]}/${httpsMatch[2]}`;
|
|
174
|
-
}
|
|
175
|
-
return null;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// src/ci-environment/services/buildkite.ts
|
|
179
|
-
function getRepository2(context) {
|
|
180
|
-
const { env } = context;
|
|
181
|
-
if (env.BUILDKITE_REPO) {
|
|
182
|
-
return getRepositoryNameFromURL(env.BUILDKITE_REPO);
|
|
183
|
-
}
|
|
184
|
-
return null;
|
|
185
|
-
}
|
|
186
|
-
var service2 = {
|
|
187
|
-
name: "Buildkite",
|
|
188
|
-
key: "buildkite",
|
|
189
|
-
detect: ({ env }) => Boolean(env.BUILDKITE),
|
|
190
|
-
config: (context) => {
|
|
191
|
-
const { env } = context;
|
|
192
|
-
const repository = getRepository2(context);
|
|
193
|
-
return {
|
|
194
|
-
// Buildkite doesn't work well so we fallback to git to ensure we have commit and branch
|
|
195
|
-
commit: env.BUILDKITE_COMMIT || head() || null,
|
|
196
|
-
branch: env.BUILDKITE_BRANCH || branch() || null,
|
|
197
|
-
repository,
|
|
198
|
-
originalRepository: repository,
|
|
199
|
-
jobId: null,
|
|
200
|
-
runId: null,
|
|
201
|
-
runAttempt: null,
|
|
202
|
-
prNumber: env.BUILDKITE_PULL_REQUEST ? Number(env.BUILDKITE_PULL_REQUEST) : null,
|
|
203
|
-
prHeadCommit: null,
|
|
204
|
-
prBaseBranch: null,
|
|
205
|
-
nonce: env.BUILDKITE_BUILD_ID || null,
|
|
206
|
-
mergeQueue: false
|
|
207
|
-
};
|
|
208
|
-
},
|
|
209
|
-
getMergeBaseCommitSha,
|
|
210
|
-
listParentCommits
|
|
211
|
-
};
|
|
212
|
-
var buildkite_default = service2;
|
|
213
|
-
|
|
214
|
-
// src/ci-environment/services/heroku.ts
|
|
215
|
-
var service3 = {
|
|
216
|
-
name: "Heroku",
|
|
217
|
-
key: "heroku",
|
|
218
|
-
detect: ({ env }) => Boolean(env.HEROKU_TEST_RUN_ID),
|
|
219
|
-
config: ({ env }) => ({
|
|
220
|
-
commit: env.HEROKU_TEST_RUN_COMMIT_VERSION || null,
|
|
221
|
-
branch: env.HEROKU_TEST_RUN_BRANCH || null,
|
|
222
|
-
owner: null,
|
|
223
|
-
repository: null,
|
|
224
|
-
originalRepository: null,
|
|
225
|
-
jobId: null,
|
|
226
|
-
runId: null,
|
|
227
|
-
runAttempt: null,
|
|
228
|
-
prNumber: null,
|
|
229
|
-
prHeadCommit: null,
|
|
230
|
-
prBaseBranch: null,
|
|
231
|
-
nonce: env.HEROKU_TEST_RUN_ID || null,
|
|
232
|
-
mergeQueue: false
|
|
233
|
-
}),
|
|
234
|
-
getMergeBaseCommitSha,
|
|
235
|
-
listParentCommits
|
|
236
|
-
};
|
|
237
|
-
var heroku_default = service3;
|
|
238
|
-
|
|
239
|
-
// src/ci-environment/services/github-actions.ts
|
|
240
|
-
import { existsSync, readFileSync } from "fs";
|
|
241
|
-
|
|
242
|
-
// src/ci-environment/github.ts
|
|
243
|
-
function getGitHubRepository(ctx) {
|
|
244
|
-
return ctx.env.GITHUB_REPOSITORY || null;
|
|
245
|
-
}
|
|
246
|
-
function assertGitHubRepository(ctx) {
|
|
247
|
-
const repo = getGitHubRepository(ctx);
|
|
248
|
-
if (!repo) {
|
|
249
|
-
throw new Error("GITHUB_REPOSITORY is missing");
|
|
250
|
-
}
|
|
251
|
-
return repo;
|
|
252
|
-
}
|
|
253
|
-
function getGitHubToken({ env }) {
|
|
254
|
-
if (!env.GITHUB_TOKEN) {
|
|
255
|
-
if (!env.DISABLE_GITHUB_TOKEN_WARNING) {
|
|
256
|
-
console.log(
|
|
257
|
-
`
|
|
258
|
-
Argos couldn\u2019t find a relevant pull request in the current environment.
|
|
259
|
-
To resolve this, Argos requires a GITHUB_TOKEN to fetch the pull request associated with the head SHA. Please ensure the following environment variable is added:
|
|
260
|
-
|
|
261
|
-
GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }}
|
|
262
|
-
|
|
263
|
-
For more details, check out the documentation: Read more at https://argos-ci.com/docs/run-on-preview-deployment
|
|
264
|
-
|
|
265
|
-
If you want to disable this warning, you can set the following environment variable:
|
|
266
|
-
|
|
267
|
-
DISABLE_GITHUB_TOKEN_WARNING: true
|
|
268
|
-
`.trim()
|
|
269
|
-
);
|
|
270
|
-
}
|
|
271
|
-
return null;
|
|
272
|
-
}
|
|
273
|
-
return env.GITHUB_TOKEN;
|
|
274
|
-
}
|
|
275
|
-
async function fetchGitHubAPI(ctx, url) {
|
|
276
|
-
const githubToken = getGitHubToken(ctx);
|
|
277
|
-
if (!githubToken) {
|
|
278
|
-
return null;
|
|
279
|
-
}
|
|
280
|
-
const response = await fetch(url, {
|
|
281
|
-
headers: {
|
|
282
|
-
Accept: "application/vnd.github+json",
|
|
283
|
-
Authorization: `Bearer ${githubToken}`,
|
|
284
|
-
"X-GitHub-Api-Version": "2022-11-28"
|
|
285
|
-
},
|
|
286
|
-
signal: AbortSignal.timeout(1e4)
|
|
287
|
-
});
|
|
288
|
-
return response;
|
|
289
|
-
}
|
|
290
|
-
var GITHUB_API_BASE_URL = "https://api.github.com";
|
|
291
|
-
async function getPullRequestFromHeadSha(ctx, sha) {
|
|
292
|
-
debug(`Fetching pull request details from head sha: ${sha}`);
|
|
293
|
-
const githubRepository = assertGitHubRepository(ctx);
|
|
294
|
-
const url = new URL(`/repos/${githubRepository}/pulls`, GITHUB_API_BASE_URL);
|
|
295
|
-
url.search = new URLSearchParams({
|
|
296
|
-
state: "open",
|
|
297
|
-
sort: "updated",
|
|
298
|
-
per_page: "30",
|
|
299
|
-
page: "1"
|
|
300
|
-
}).toString();
|
|
301
|
-
const response = await fetchGitHubAPI(ctx, url);
|
|
302
|
-
if (!response) {
|
|
303
|
-
return null;
|
|
304
|
-
}
|
|
305
|
-
if (!response.ok) {
|
|
306
|
-
throw new Error(
|
|
307
|
-
`Non-OK response (status: ${response.status}) while fetching pull request details from head sha (${sha})`
|
|
308
|
-
);
|
|
309
|
-
}
|
|
310
|
-
const result = await response.json();
|
|
311
|
-
if (result.length === 0) {
|
|
312
|
-
debug("No results, no pull request found");
|
|
313
|
-
return null;
|
|
314
|
-
}
|
|
315
|
-
const matchingPr = result.find((pr) => pr.head.sha === sha);
|
|
316
|
-
if (matchingPr) {
|
|
317
|
-
debug("Pull request found", matchingPr);
|
|
318
|
-
return matchingPr;
|
|
319
|
-
}
|
|
320
|
-
debug("No matching pull request found");
|
|
321
|
-
return null;
|
|
322
|
-
}
|
|
323
|
-
async function getPullRequestFromPrNumber(ctx, prNumber) {
|
|
324
|
-
debug(`Fetching pull request #${prNumber}`);
|
|
325
|
-
const githubRepository = assertGitHubRepository(ctx);
|
|
326
|
-
const response = await fetchGitHubAPI(
|
|
327
|
-
ctx,
|
|
328
|
-
new URL(
|
|
329
|
-
`/repos/${githubRepository}/pulls/${prNumber}`,
|
|
330
|
-
GITHUB_API_BASE_URL
|
|
331
|
-
)
|
|
332
|
-
);
|
|
333
|
-
if (!response) {
|
|
334
|
-
return null;
|
|
335
|
-
}
|
|
336
|
-
if (response.status === 404) {
|
|
337
|
-
debug(
|
|
338
|
-
"No pull request found, pr detection from branch was probably a mistake"
|
|
339
|
-
);
|
|
340
|
-
return null;
|
|
341
|
-
}
|
|
342
|
-
if (!response.ok) {
|
|
343
|
-
throw new Error(
|
|
344
|
-
`Non-OK response (status: ${response.status}) while fetching pull request #${prNumber}`
|
|
345
|
-
);
|
|
346
|
-
}
|
|
347
|
-
const result = await response.json();
|
|
348
|
-
return result;
|
|
349
|
-
}
|
|
350
|
-
function getPRNumberFromMergeGroupBranch(branch2) {
|
|
351
|
-
const prMatch = /queue\/[^/]*\/pr-(\d+)-/.exec(branch2);
|
|
352
|
-
if (prMatch) {
|
|
353
|
-
const prNumber = Number(prMatch[1]);
|
|
354
|
-
return prNumber;
|
|
355
|
-
}
|
|
356
|
-
return null;
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
// src/ci-environment/services/github-actions.ts
|
|
360
|
-
function readEventPayload({ env }) {
|
|
361
|
-
if (!env.GITHUB_EVENT_PATH) {
|
|
362
|
-
return null;
|
|
363
|
-
}
|
|
364
|
-
if (!existsSync(env.GITHUB_EVENT_PATH)) {
|
|
365
|
-
return null;
|
|
366
|
-
}
|
|
367
|
-
return JSON.parse(readFileSync(env.GITHUB_EVENT_PATH, "utf-8"));
|
|
368
|
-
}
|
|
369
|
-
function getVercelDeploymentPayload(payload) {
|
|
370
|
-
if (process.env.GITHUB_EVENT_NAME === "repository_dispatch" && payload && "action" in payload && payload.action === "vercel.deployment.success") {
|
|
371
|
-
return payload;
|
|
372
|
-
}
|
|
373
|
-
return null;
|
|
374
|
-
}
|
|
375
|
-
function getMergeGroupPayload(payload) {
|
|
376
|
-
if (payload && process.env.GITHUB_EVENT_NAME === "merge_group" && "action" in payload && payload.action === "checks_requested") {
|
|
377
|
-
return payload;
|
|
378
|
-
}
|
|
379
|
-
return null;
|
|
380
|
-
}
|
|
381
|
-
function getBranchFromContext(context) {
|
|
382
|
-
const { env } = context;
|
|
383
|
-
if (env.GITHUB_HEAD_REF) {
|
|
384
|
-
return env.GITHUB_HEAD_REF;
|
|
385
|
-
}
|
|
386
|
-
if (!env.GITHUB_REF) {
|
|
387
|
-
return null;
|
|
388
|
-
}
|
|
389
|
-
const branchRegex = /refs\/heads\/(.*)/;
|
|
390
|
-
const matches = branchRegex.exec(env.GITHUB_REF);
|
|
391
|
-
return matches?.[1] ?? null;
|
|
392
|
-
}
|
|
393
|
-
function getBranchFromPayload(payload) {
|
|
394
|
-
if ("workflow_run" in payload && payload.workflow_run) {
|
|
395
|
-
return payload.workflow_run.head_branch;
|
|
396
|
-
}
|
|
397
|
-
if ("deployment" in payload && payload.deployment) {
|
|
398
|
-
return payload.deployment.environment;
|
|
399
|
-
}
|
|
400
|
-
return null;
|
|
401
|
-
}
|
|
402
|
-
function getBranch(args) {
|
|
403
|
-
const { payload, mergeGroupPayload, vercelPayload, pullRequest, context } = args;
|
|
404
|
-
if (mergeGroupPayload && pullRequest?.head.ref) {
|
|
405
|
-
return pullRequest.head.ref;
|
|
406
|
-
}
|
|
407
|
-
if (vercelPayload) {
|
|
408
|
-
return vercelPayload.client_payload.git.ref;
|
|
409
|
-
}
|
|
410
|
-
if (payload) {
|
|
411
|
-
const fromPayload = getBranchFromPayload(payload);
|
|
412
|
-
if (fromPayload) {
|
|
413
|
-
return fromPayload;
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
const fromContext = getBranchFromContext(context);
|
|
417
|
-
if (fromContext) {
|
|
418
|
-
return fromContext;
|
|
419
|
-
}
|
|
420
|
-
if (pullRequest) {
|
|
421
|
-
return pullRequest.head.ref;
|
|
422
|
-
}
|
|
423
|
-
return null;
|
|
424
|
-
}
|
|
425
|
-
function getRepository3(context, payload) {
|
|
426
|
-
if (payload && "pull_request" in payload && payload.pull_request) {
|
|
427
|
-
const pr = payload.pull_request;
|
|
428
|
-
if (pr.head && pr.head.repo && pr.head.repo.full_name) {
|
|
429
|
-
return pr.head.repo.full_name;
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
return getGitHubRepository(context);
|
|
433
|
-
}
|
|
434
|
-
function getSha(context, vercelPayload, payload) {
|
|
435
|
-
if (context.env.GITHUB_EVENT_NAME === "pull_request_target") {
|
|
436
|
-
if (!payload) {
|
|
437
|
-
throw new Error('Payload is missing in "pull_request_target" event');
|
|
438
|
-
}
|
|
439
|
-
const pullRequest = getPullRequestFromPayload(payload);
|
|
440
|
-
if (!pullRequest) {
|
|
441
|
-
throw new Error('Pull request missing in "pull_request_target" event');
|
|
442
|
-
}
|
|
443
|
-
return pullRequest.head.sha;
|
|
444
|
-
}
|
|
445
|
-
if (vercelPayload) {
|
|
446
|
-
return vercelPayload.client_payload.git.sha;
|
|
447
|
-
}
|
|
448
|
-
if (!context.env.GITHUB_SHA) {
|
|
449
|
-
throw new Error("GITHUB_SHA is missing");
|
|
450
|
-
}
|
|
451
|
-
return context.env.GITHUB_SHA;
|
|
452
|
-
}
|
|
453
|
-
function getPullRequestFromPayload(payload) {
|
|
454
|
-
if ("pull_request" in payload && payload.pull_request && payload.pull_request) {
|
|
455
|
-
return payload.pull_request;
|
|
456
|
-
}
|
|
457
|
-
if ("workflow_run" in payload && payload.workflow_run && payload.workflow_run.pull_requests[0]) {
|
|
458
|
-
return payload.workflow_run.pull_requests[0];
|
|
459
|
-
}
|
|
460
|
-
if ("check_run" in payload && payload.check_run && "pull_requests" in payload.check_run && payload.check_run.pull_requests[0]) {
|
|
461
|
-
return payload.check_run.pull_requests[0];
|
|
462
|
-
}
|
|
463
|
-
return null;
|
|
464
|
-
}
|
|
465
|
-
async function getPullRequest(args) {
|
|
466
|
-
const { payload, vercelPayload, mergeGroupPayload, context, sha } = args;
|
|
467
|
-
if (vercelPayload || !payload) {
|
|
468
|
-
return getPullRequestFromHeadSha(context, sha);
|
|
469
|
-
}
|
|
470
|
-
if (mergeGroupPayload) {
|
|
471
|
-
const prNumber = getPRNumberFromMergeGroupBranch(
|
|
472
|
-
mergeGroupPayload.merge_group.head_ref
|
|
473
|
-
);
|
|
474
|
-
if (!prNumber) {
|
|
475
|
-
debug(
|
|
476
|
-
`No PR found from merge group head ref: ${mergeGroupPayload.merge_group.head_ref}`
|
|
477
|
-
);
|
|
478
|
-
return null;
|
|
479
|
-
}
|
|
480
|
-
debug(
|
|
481
|
-
`PR #${prNumber} found from merge group head ref (${mergeGroupPayload.merge_group.head_ref})`
|
|
482
|
-
);
|
|
483
|
-
return getPullRequestFromPrNumber(context, prNumber);
|
|
484
|
-
}
|
|
485
|
-
return getPullRequestFromPayload(payload);
|
|
486
|
-
}
|
|
487
|
-
var service4 = {
|
|
488
|
-
name: "GitHub Actions",
|
|
489
|
-
key: "github-actions",
|
|
490
|
-
detect: (context) => Boolean(context.env.GITHUB_ACTIONS),
|
|
491
|
-
config: async (context) => {
|
|
492
|
-
const { env } = context;
|
|
493
|
-
const payload = readEventPayload(context);
|
|
494
|
-
const vercelPayload = getVercelDeploymentPayload(payload);
|
|
495
|
-
const mergeGroupPayload = getMergeGroupPayload(payload);
|
|
496
|
-
const sha = getSha(context, vercelPayload, payload);
|
|
497
|
-
const pullRequest = await getPullRequest({
|
|
498
|
-
payload,
|
|
499
|
-
vercelPayload,
|
|
500
|
-
mergeGroupPayload,
|
|
501
|
-
sha,
|
|
502
|
-
context
|
|
503
|
-
});
|
|
504
|
-
const branch2 = getBranch({
|
|
505
|
-
payload,
|
|
506
|
-
vercelPayload,
|
|
507
|
-
mergeGroupPayload,
|
|
508
|
-
context,
|
|
509
|
-
pullRequest
|
|
510
|
-
});
|
|
511
|
-
return {
|
|
512
|
-
commit: sha,
|
|
513
|
-
repository: getRepository3(context, payload),
|
|
514
|
-
originalRepository: getGitHubRepository(context),
|
|
515
|
-
jobId: env.GITHUB_JOB || null,
|
|
516
|
-
runId: env.GITHUB_RUN_ID || null,
|
|
517
|
-
runAttempt: env.GITHUB_RUN_ATTEMPT ? Number(env.GITHUB_RUN_ATTEMPT) : null,
|
|
518
|
-
nonce: `${env.GITHUB_RUN_ID}-${env.GITHUB_RUN_ATTEMPT}`,
|
|
519
|
-
branch: branch2,
|
|
520
|
-
prNumber: pullRequest?.number || null,
|
|
521
|
-
prHeadCommit: pullRequest?.head.sha ?? null,
|
|
522
|
-
prBaseBranch: pullRequest?.base.ref ?? null,
|
|
523
|
-
mergeQueue: Boolean(mergeGroupPayload)
|
|
524
|
-
};
|
|
525
|
-
},
|
|
526
|
-
getMergeBaseCommitSha,
|
|
527
|
-
listParentCommits
|
|
528
|
-
};
|
|
529
|
-
var github_actions_default = service4;
|
|
530
|
-
|
|
531
|
-
// src/ci-environment/services/circleci.ts
|
|
532
|
-
function getPrNumber2(context) {
|
|
533
|
-
const { env } = context;
|
|
534
|
-
const matches = /pull\/(\d+)/.exec(env.CIRCLE_PULL_REQUEST || "");
|
|
535
|
-
if (matches) {
|
|
536
|
-
return Number(matches[1]);
|
|
537
|
-
}
|
|
538
|
-
return null;
|
|
539
|
-
}
|
|
540
|
-
function getRepository4(context) {
|
|
541
|
-
const { env } = context;
|
|
542
|
-
if (env.CIRCLE_PR_REPONAME && env.CIRCLE_PR_USERNAME) {
|
|
543
|
-
return `${env.CIRCLE_PR_USERNAME}/${env.CIRCLE_PR_REPONAME}`;
|
|
544
|
-
}
|
|
545
|
-
return getOriginalRepository(context);
|
|
546
|
-
}
|
|
547
|
-
function getOriginalRepository(context) {
|
|
548
|
-
const { env } = context;
|
|
549
|
-
if (env.CIRCLE_PROJECT_USERNAME && env.CIRCLE_PROJECT_REPONAME) {
|
|
550
|
-
return `${env.CIRCLE_PROJECT_USERNAME}/${env.CIRCLE_PROJECT_REPONAME}`;
|
|
551
|
-
}
|
|
552
|
-
return null;
|
|
553
|
-
}
|
|
554
|
-
var service5 = {
|
|
555
|
-
name: "CircleCI",
|
|
556
|
-
key: "circleci",
|
|
557
|
-
detect: ({ env }) => Boolean(env.CIRCLECI),
|
|
558
|
-
config: (context) => {
|
|
559
|
-
const { env } = context;
|
|
560
|
-
return {
|
|
561
|
-
commit: env.CIRCLE_SHA1 || null,
|
|
562
|
-
branch: env.CIRCLE_BRANCH || null,
|
|
563
|
-
repository: getRepository4(context),
|
|
564
|
-
originalRepository: getOriginalRepository(context),
|
|
565
|
-
jobId: null,
|
|
566
|
-
runId: null,
|
|
567
|
-
runAttempt: null,
|
|
568
|
-
prNumber: getPrNumber2({ env }),
|
|
569
|
-
prHeadCommit: null,
|
|
570
|
-
prBaseBranch: null,
|
|
571
|
-
nonce: env.CIRCLE_WORKFLOW_ID || env.CIRCLE_BUILD_NUM || null,
|
|
572
|
-
mergeQueue: false
|
|
573
|
-
};
|
|
574
|
-
},
|
|
575
|
-
getMergeBaseCommitSha,
|
|
576
|
-
listParentCommits
|
|
577
|
-
};
|
|
578
|
-
var circleci_default = service5;
|
|
579
|
-
|
|
580
|
-
// src/ci-environment/services/travis.ts
|
|
581
|
-
function getRepository5(context) {
|
|
582
|
-
const { env } = context;
|
|
583
|
-
if (env.TRAVIS_PULL_REQUEST_SLUG) {
|
|
584
|
-
return env.TRAVIS_PULL_REQUEST_SLUG;
|
|
585
|
-
}
|
|
586
|
-
return getOriginalRepository2(context);
|
|
587
|
-
}
|
|
588
|
-
function getOriginalRepository2(context) {
|
|
589
|
-
const { env } = context;
|
|
590
|
-
return env.TRAVIS_REPO_SLUG || null;
|
|
591
|
-
}
|
|
592
|
-
function getPrNumber3(context) {
|
|
593
|
-
const { env } = context;
|
|
594
|
-
if (env.TRAVIS_PULL_REQUEST) {
|
|
595
|
-
return Number(env.TRAVIS_PULL_REQUEST);
|
|
596
|
-
}
|
|
597
|
-
return null;
|
|
598
|
-
}
|
|
599
|
-
var service6 = {
|
|
600
|
-
name: "Travis CI",
|
|
601
|
-
key: "travis",
|
|
602
|
-
detect: ({ env }) => Boolean(env.TRAVIS),
|
|
603
|
-
config: (ctx) => {
|
|
604
|
-
const { env } = ctx;
|
|
605
|
-
return {
|
|
606
|
-
commit: env.TRAVIS_COMMIT || null,
|
|
607
|
-
branch: env.TRAVIS_BRANCH || null,
|
|
608
|
-
repository: getRepository5(ctx),
|
|
609
|
-
originalRepository: getOriginalRepository2(ctx),
|
|
610
|
-
jobId: null,
|
|
611
|
-
runId: null,
|
|
612
|
-
runAttempt: null,
|
|
613
|
-
prNumber: getPrNumber3(ctx),
|
|
614
|
-
prHeadCommit: null,
|
|
615
|
-
prBaseBranch: null,
|
|
616
|
-
nonce: env.TRAVIS_BUILD_ID || null,
|
|
617
|
-
mergeQueue: false
|
|
618
|
-
};
|
|
619
|
-
},
|
|
620
|
-
getMergeBaseCommitSha,
|
|
621
|
-
listParentCommits
|
|
622
|
-
};
|
|
623
|
-
var travis_default = service6;
|
|
624
|
-
|
|
625
|
-
// src/ci-environment/services/gitlab.ts
|
|
626
|
-
function getRepository6(context) {
|
|
627
|
-
const { env } = context;
|
|
628
|
-
if (env.CI_MERGE_REQUEST_PROJECT_PATH) {
|
|
629
|
-
return env.CI_MERGE_REQUEST_PROJECT_PATH;
|
|
630
|
-
}
|
|
631
|
-
return getOriginalRepository3(context);
|
|
632
|
-
}
|
|
633
|
-
function getOriginalRepository3(context) {
|
|
634
|
-
const { env } = context;
|
|
635
|
-
return env.CI_PROJECT_PATH || null;
|
|
636
|
-
}
|
|
637
|
-
var service7 = {
|
|
638
|
-
name: "GitLab",
|
|
639
|
-
key: "gitlab",
|
|
640
|
-
detect: ({ env }) => env.GITLAB_CI === "true",
|
|
641
|
-
config: (context) => {
|
|
642
|
-
const { env } = context;
|
|
643
|
-
return {
|
|
644
|
-
commit: env.CI_COMMIT_SHA || null,
|
|
645
|
-
branch: env.CI_COMMIT_REF_NAME || null,
|
|
646
|
-
repository: getRepository6(context),
|
|
647
|
-
originalRepository: getOriginalRepository3(context),
|
|
648
|
-
jobId: null,
|
|
649
|
-
runId: null,
|
|
650
|
-
runAttempt: null,
|
|
651
|
-
prNumber: null,
|
|
652
|
-
prHeadCommit: null,
|
|
653
|
-
prBaseBranch: null,
|
|
654
|
-
nonce: env.CI_PIPELINE_ID || null,
|
|
655
|
-
mergeQueue: false
|
|
656
|
-
};
|
|
657
|
-
},
|
|
658
|
-
getMergeBaseCommitSha,
|
|
659
|
-
listParentCommits
|
|
660
|
-
};
|
|
661
|
-
var gitlab_default = service7;
|
|
662
|
-
|
|
663
|
-
// src/ci-environment/services/git.ts
|
|
664
|
-
function getRepository7() {
|
|
665
|
-
const repositoryURL = getRepositoryURL();
|
|
666
|
-
if (!repositoryURL) {
|
|
667
|
-
return null;
|
|
668
|
-
}
|
|
669
|
-
return getRepositoryNameFromURL(repositoryURL);
|
|
670
|
-
}
|
|
671
|
-
var service8 = {
|
|
672
|
-
name: "Git",
|
|
673
|
-
key: "git",
|
|
674
|
-
detect: () => checkIsGitRepository(),
|
|
675
|
-
config: () => {
|
|
676
|
-
const repository = getRepository7();
|
|
677
|
-
return {
|
|
678
|
-
commit: head() || null,
|
|
679
|
-
branch: branch() || null,
|
|
680
|
-
repository,
|
|
681
|
-
originalRepository: repository,
|
|
682
|
-
jobId: null,
|
|
683
|
-
runId: null,
|
|
684
|
-
runAttempt: null,
|
|
685
|
-
prNumber: null,
|
|
686
|
-
prHeadCommit: null,
|
|
687
|
-
prBaseBranch: null,
|
|
688
|
-
nonce: null,
|
|
689
|
-
mergeQueue: false
|
|
690
|
-
};
|
|
691
|
-
},
|
|
692
|
-
getMergeBaseCommitSha,
|
|
693
|
-
listParentCommits
|
|
694
|
-
};
|
|
695
|
-
var git_default = service8;
|
|
696
|
-
|
|
697
|
-
// src/ci-environment/index.ts
|
|
698
|
-
var services = [
|
|
699
|
-
heroku_default,
|
|
700
|
-
github_actions_default,
|
|
701
|
-
circleci_default,
|
|
702
|
-
travis_default,
|
|
703
|
-
buildkite_default,
|
|
704
|
-
gitlab_default,
|
|
705
|
-
bitrise_default,
|
|
706
|
-
git_default
|
|
707
|
-
];
|
|
708
|
-
function createContext() {
|
|
709
|
-
return { env: process.env };
|
|
710
|
-
}
|
|
711
|
-
function getCiService(context) {
|
|
712
|
-
return services.find((service9) => service9.detect(context));
|
|
713
|
-
}
|
|
714
|
-
function getMergeBaseCommitSha2(input) {
|
|
715
|
-
const context = createContext();
|
|
716
|
-
const service9 = getCiService(context);
|
|
717
|
-
if (!service9) {
|
|
718
|
-
return null;
|
|
719
|
-
}
|
|
720
|
-
return service9.getMergeBaseCommitSha(input, context);
|
|
721
|
-
}
|
|
722
|
-
function listParentCommits2(input) {
|
|
723
|
-
const context = createContext();
|
|
724
|
-
const service9 = getCiService(context);
|
|
725
|
-
if (!service9) {
|
|
726
|
-
return null;
|
|
727
|
-
}
|
|
728
|
-
return service9.listParentCommits(input, context);
|
|
729
|
-
}
|
|
730
|
-
async function getCiEnvironment() {
|
|
731
|
-
const context = createContext();
|
|
732
|
-
debug("Detecting CI environment", context);
|
|
733
|
-
const service9 = getCiService(context);
|
|
734
|
-
if (service9) {
|
|
735
|
-
debug("Internal service matched", service9.name);
|
|
736
|
-
const variables = await service9.config(context);
|
|
737
|
-
const ciEnvironment = {
|
|
738
|
-
name: service9.name,
|
|
739
|
-
key: service9.key,
|
|
740
|
-
...variables
|
|
741
|
-
};
|
|
742
|
-
debug("CI environment", ciEnvironment);
|
|
743
|
-
return ciEnvironment;
|
|
744
|
-
}
|
|
745
|
-
return null;
|
|
746
|
-
}
|
|
747
|
-
|
|
748
|
-
// src/config.ts
|
|
749
|
-
var mustBeApiBaseUrl = (value) => {
|
|
750
|
-
const URL_REGEX = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/;
|
|
751
|
-
if (!URL_REGEX.test(value)) {
|
|
752
|
-
throw new Error("Invalid Argos API base URL");
|
|
753
|
-
}
|
|
754
|
-
};
|
|
755
|
-
var mustBeCommit = (value) => {
|
|
756
|
-
const SHA1_REGEX = /^[0-9a-f]{40}$/;
|
|
757
|
-
if (!SHA1_REGEX.test(value)) {
|
|
758
|
-
const SHA1_SHORT_REGEX = /^[0-9a-f]{7}$/;
|
|
759
|
-
if (SHA1_SHORT_REGEX.test(value)) {
|
|
760
|
-
throw new Error("Short SHA1 is not allowed");
|
|
761
|
-
}
|
|
762
|
-
throw new Error("Invalid commit");
|
|
763
|
-
}
|
|
764
|
-
};
|
|
765
|
-
var mustBeArgosToken = (value) => {
|
|
766
|
-
if (value && value.length !== 40) {
|
|
767
|
-
throw new Error("Invalid Argos repository token (must be 40 characters)");
|
|
768
|
-
}
|
|
769
|
-
};
|
|
770
|
-
var minInteger = (min) => (value) => {
|
|
771
|
-
if (!Number.isInteger(value)) {
|
|
772
|
-
throw new Error("must be an integer");
|
|
773
|
-
}
|
|
774
|
-
if (value < min) {
|
|
775
|
-
throw new Error(`must be at least ${min}`);
|
|
776
|
-
}
|
|
777
|
-
};
|
|
778
|
-
var toInt = (value) => {
|
|
779
|
-
if (value === "") {
|
|
780
|
-
return null;
|
|
781
|
-
}
|
|
782
|
-
const num = Number(value);
|
|
783
|
-
if (!Number.isInteger(num) || Number.isNaN(num)) {
|
|
784
|
-
return num;
|
|
785
|
-
}
|
|
786
|
-
return num;
|
|
787
|
-
};
|
|
788
|
-
var toFloat = (value) => parseFloat(value);
|
|
789
|
-
convict.addFormat({
|
|
790
|
-
name: "parallel-total",
|
|
791
|
-
validate: minInteger(-1),
|
|
792
|
-
coerce: toInt
|
|
793
|
-
});
|
|
794
|
-
convict.addFormat({
|
|
795
|
-
name: "parallel-index",
|
|
796
|
-
validate: minInteger(1),
|
|
797
|
-
coerce: toInt
|
|
798
|
-
});
|
|
799
|
-
convict.addFormat({
|
|
800
|
-
name: "float-percent",
|
|
801
|
-
validate: (val) => {
|
|
802
|
-
if (val !== 0 && (!val || val > 1 || val < 0)) {
|
|
803
|
-
throw new Error("Must be a float between 0 and 1, inclusive.");
|
|
804
|
-
}
|
|
805
|
-
},
|
|
806
|
-
coerce: toFloat
|
|
807
|
-
});
|
|
808
|
-
var schema = {
|
|
809
|
-
apiBaseUrl: {
|
|
810
|
-
env: "ARGOS_API_BASE_URL",
|
|
811
|
-
default: "https://api.argos-ci.com/v2/",
|
|
812
|
-
format: mustBeApiBaseUrl
|
|
813
|
-
},
|
|
814
|
-
commit: {
|
|
815
|
-
env: "ARGOS_COMMIT",
|
|
816
|
-
default: null,
|
|
817
|
-
format: mustBeCommit
|
|
818
|
-
},
|
|
819
|
-
branch: {
|
|
820
|
-
env: "ARGOS_BRANCH",
|
|
821
|
-
default: null,
|
|
822
|
-
format: String
|
|
823
|
-
},
|
|
824
|
-
token: {
|
|
825
|
-
env: "ARGOS_TOKEN",
|
|
826
|
-
default: null,
|
|
827
|
-
format: mustBeArgosToken
|
|
828
|
-
},
|
|
829
|
-
buildName: {
|
|
830
|
-
env: "ARGOS_BUILD_NAME",
|
|
831
|
-
default: null,
|
|
832
|
-
format: String,
|
|
833
|
-
nullable: true
|
|
834
|
-
},
|
|
835
|
-
mode: {
|
|
836
|
-
env: "ARGOS_MODE",
|
|
837
|
-
format: ["ci", "monitoring"],
|
|
838
|
-
default: null,
|
|
839
|
-
nullable: true
|
|
840
|
-
},
|
|
841
|
-
prNumber: {
|
|
842
|
-
env: "ARGOS_PR_NUMBER",
|
|
843
|
-
format: Number,
|
|
844
|
-
default: null,
|
|
845
|
-
nullable: true
|
|
846
|
-
},
|
|
847
|
-
prHeadCommit: {
|
|
848
|
-
env: "ARGOS_PR_HEAD_COMMIT",
|
|
849
|
-
format: String,
|
|
850
|
-
default: null,
|
|
851
|
-
nullable: true
|
|
852
|
-
},
|
|
853
|
-
prBaseBranch: {
|
|
854
|
-
env: "ARGOS_PR_BASE_BRANCH",
|
|
855
|
-
format: String,
|
|
856
|
-
default: null,
|
|
857
|
-
nullable: true
|
|
858
|
-
},
|
|
859
|
-
parallel: {
|
|
860
|
-
env: "ARGOS_PARALLEL",
|
|
861
|
-
default: false,
|
|
862
|
-
format: Boolean
|
|
863
|
-
},
|
|
864
|
-
parallelNonce: {
|
|
865
|
-
env: "ARGOS_PARALLEL_NONCE",
|
|
866
|
-
format: String,
|
|
867
|
-
default: null,
|
|
868
|
-
nullable: true
|
|
869
|
-
},
|
|
870
|
-
parallelIndex: {
|
|
871
|
-
env: "ARGOS_PARALLEL_INDEX",
|
|
872
|
-
format: "parallel-index",
|
|
873
|
-
default: null,
|
|
874
|
-
nullable: true
|
|
875
|
-
},
|
|
876
|
-
parallelTotal: {
|
|
877
|
-
env: "ARGOS_PARALLEL_TOTAL",
|
|
878
|
-
format: "parallel-total",
|
|
879
|
-
default: null,
|
|
880
|
-
nullable: true
|
|
881
|
-
},
|
|
882
|
-
referenceBranch: {
|
|
883
|
-
env: "ARGOS_REFERENCE_BRANCH",
|
|
884
|
-
format: String,
|
|
885
|
-
default: null,
|
|
886
|
-
nullable: true
|
|
887
|
-
},
|
|
888
|
-
referenceCommit: {
|
|
889
|
-
env: "ARGOS_REFERENCE_COMMIT",
|
|
890
|
-
format: String,
|
|
891
|
-
default: null,
|
|
892
|
-
nullable: true
|
|
893
|
-
},
|
|
894
|
-
jobId: {
|
|
895
|
-
format: String,
|
|
896
|
-
default: null,
|
|
897
|
-
nullable: true
|
|
898
|
-
},
|
|
899
|
-
runId: {
|
|
900
|
-
format: String,
|
|
901
|
-
default: null,
|
|
902
|
-
nullable: true
|
|
903
|
-
},
|
|
904
|
-
runAttempt: {
|
|
905
|
-
format: "nat",
|
|
906
|
-
default: null,
|
|
907
|
-
nullable: true
|
|
908
|
-
},
|
|
909
|
-
repository: {
|
|
910
|
-
format: String,
|
|
911
|
-
default: null,
|
|
912
|
-
nullable: true
|
|
913
|
-
},
|
|
914
|
-
originalRepository: {
|
|
915
|
-
format: String,
|
|
916
|
-
default: null,
|
|
917
|
-
nullable: true
|
|
918
|
-
},
|
|
919
|
-
ciProvider: {
|
|
920
|
-
format: String,
|
|
921
|
-
default: null,
|
|
922
|
-
nullable: true
|
|
923
|
-
},
|
|
924
|
-
threshold: {
|
|
925
|
-
env: "ARGOS_THRESHOLD",
|
|
926
|
-
format: "float-percent",
|
|
927
|
-
default: null,
|
|
928
|
-
nullable: true
|
|
929
|
-
},
|
|
930
|
-
previewBaseUrl: {
|
|
931
|
-
env: "ARGOS_PREVIEW_BASE_URL",
|
|
932
|
-
format: String,
|
|
933
|
-
default: null,
|
|
934
|
-
nullable: true
|
|
935
|
-
},
|
|
936
|
-
skipped: {
|
|
937
|
-
env: "ARGOS_SKIPPED",
|
|
938
|
-
format: Boolean,
|
|
939
|
-
default: false
|
|
940
|
-
},
|
|
941
|
-
mergeQueue: {
|
|
942
|
-
format: Boolean,
|
|
943
|
-
default: false
|
|
944
|
-
},
|
|
945
|
-
subset: {
|
|
946
|
-
env: "ARGOS_SUBSET",
|
|
947
|
-
format: Boolean,
|
|
948
|
-
default: false
|
|
949
|
-
}
|
|
950
|
-
};
|
|
951
|
-
function createConfig() {
|
|
952
|
-
return convict(schema, { args: [], env: {} });
|
|
953
|
-
}
|
|
954
|
-
function getDefaultConfig() {
|
|
955
|
-
return Object.entries(schema).reduce(
|
|
956
|
-
(cfg, [key, entry]) => {
|
|
957
|
-
cfg[key] = "env" in entry && entry.env && process.env[entry.env] ? process.env[entry.env] : entry.default;
|
|
958
|
-
return cfg;
|
|
959
|
-
},
|
|
960
|
-
{}
|
|
961
|
-
);
|
|
962
|
-
}
|
|
963
|
-
async function readConfig(options = {}) {
|
|
964
|
-
const config = createConfig();
|
|
965
|
-
const ciEnv = await getCiEnvironment();
|
|
966
|
-
const defaultConfig = getDefaultConfig();
|
|
967
|
-
config.load({
|
|
968
|
-
apiBaseUrl: options.apiBaseUrl || defaultConfig.apiBaseUrl,
|
|
969
|
-
commit: options.commit || defaultConfig.commit || ciEnv?.commit || null,
|
|
970
|
-
branch: options.branch || defaultConfig.branch || ciEnv?.branch || null,
|
|
971
|
-
token: options.token || defaultConfig.token || null,
|
|
972
|
-
buildName: options.buildName || defaultConfig.buildName || null,
|
|
973
|
-
prNumber: options.prNumber || defaultConfig.prNumber || ciEnv?.prNumber || null,
|
|
974
|
-
prHeadCommit: defaultConfig.prHeadCommit || ciEnv?.prHeadCommit || null,
|
|
975
|
-
prBaseBranch: defaultConfig.prBaseBranch || ciEnv?.prBaseBranch || null,
|
|
976
|
-
referenceBranch: options.referenceBranch || defaultConfig.referenceBranch || null,
|
|
977
|
-
referenceCommit: options.referenceCommit || defaultConfig.referenceCommit || null,
|
|
978
|
-
repository: ciEnv?.repository || null,
|
|
979
|
-
originalRepository: ciEnv?.originalRepository || null,
|
|
980
|
-
jobId: ciEnv?.jobId || null,
|
|
981
|
-
runId: ciEnv?.runId || null,
|
|
982
|
-
runAttempt: ciEnv?.runAttempt || null,
|
|
983
|
-
parallel: options.parallel ?? defaultConfig.parallel ?? false,
|
|
984
|
-
parallelNonce: options.parallelNonce || defaultConfig.parallelNonce || ciEnv?.nonce || null,
|
|
985
|
-
parallelTotal: options.parallelTotal ?? defaultConfig.parallelTotal ?? null,
|
|
986
|
-
parallelIndex: options.parallelIndex ?? defaultConfig.parallelIndex ?? null,
|
|
987
|
-
mode: options.mode || defaultConfig.mode || null,
|
|
988
|
-
ciProvider: ciEnv?.key || null,
|
|
989
|
-
previewBaseUrl: defaultConfig.previewBaseUrl || null,
|
|
990
|
-
skipped: options.skipped ?? defaultConfig.skipped ?? false,
|
|
991
|
-
subset: options.subset ?? defaultConfig.subset ?? false,
|
|
992
|
-
mergeQueue: ciEnv?.mergeQueue ?? false
|
|
993
|
-
});
|
|
994
|
-
if (!config.get("branch") || !config.get("commit")) {
|
|
995
|
-
throw new Error(
|
|
996
|
-
"Argos requires a branch and a commit to be set. If you are running in a non-git environment consider setting ARGOS_BRANCH and ARGOS_COMMIT environment variables."
|
|
997
|
-
);
|
|
998
|
-
}
|
|
999
|
-
config.validate();
|
|
1000
|
-
return config.get();
|
|
1001
|
-
}
|
|
1002
|
-
async function getConfigFromOptions({
|
|
1003
|
-
parallel,
|
|
1004
|
-
...options
|
|
1005
|
-
}) {
|
|
1006
|
-
return readConfig({
|
|
1007
|
-
...options,
|
|
1008
|
-
parallel: parallel !== void 0 ? Boolean(parallel) : void 0,
|
|
1009
|
-
parallelNonce: parallel ? parallel.nonce : void 0,
|
|
1010
|
-
parallelTotal: parallel ? parallel.total : void 0,
|
|
1011
|
-
parallelIndex: parallel ? parallel.index : void 0
|
|
1012
|
-
});
|
|
1013
|
-
}
|
|
1014
|
-
|
|
1015
|
-
// src/finalize.ts
|
|
1016
|
-
import { createClient, throwAPIError } from "@argos-ci/api-client";
|
|
1017
|
-
|
|
1018
|
-
// src/auth.ts
|
|
1019
|
-
var base64Encode = (obj) => Buffer.from(JSON.stringify(obj), "utf8").toString("base64");
|
|
1020
|
-
function getAuthToken(args) {
|
|
1021
|
-
const {
|
|
1022
|
-
token,
|
|
1023
|
-
ciProvider,
|
|
1024
|
-
originalRepository: repository,
|
|
1025
|
-
jobId,
|
|
1026
|
-
runId,
|
|
1027
|
-
prNumber
|
|
1028
|
-
} = args;
|
|
1029
|
-
if (token) {
|
|
1030
|
-
return token;
|
|
1031
|
-
}
|
|
1032
|
-
switch (ciProvider) {
|
|
1033
|
-
case "github-actions": {
|
|
1034
|
-
if (!repository || !jobId || !runId) {
|
|
1035
|
-
throw new Error(
|
|
1036
|
-
`Automatic GitHub Actions variables detection failed. Please add the 'ARGOS_TOKEN'`
|
|
1037
|
-
);
|
|
1038
|
-
}
|
|
1039
|
-
const [owner, repo] = repository.split("/");
|
|
1040
|
-
return `tokenless-github-${base64Encode({
|
|
1041
|
-
owner,
|
|
1042
|
-
repository: repo,
|
|
1043
|
-
jobId,
|
|
1044
|
-
runId,
|
|
1045
|
-
prNumber: prNumber ?? void 0
|
|
1046
|
-
})}`;
|
|
1047
|
-
}
|
|
1048
|
-
default:
|
|
1049
|
-
throw new Error("Missing Argos repository token 'ARGOS_TOKEN'");
|
|
1050
|
-
}
|
|
1051
|
-
}
|
|
1052
|
-
|
|
1053
|
-
// src/finalize.ts
|
|
1054
|
-
async function finalize(params) {
|
|
1055
|
-
const config = await readConfig({
|
|
1056
|
-
parallelNonce: params.parallel?.nonce
|
|
1057
|
-
});
|
|
1058
|
-
const authToken = getAuthToken(config);
|
|
1059
|
-
const apiClient = createClient({
|
|
1060
|
-
baseUrl: config.apiBaseUrl,
|
|
1061
|
-
authToken
|
|
1062
|
-
});
|
|
1063
|
-
if (!config.parallelNonce) {
|
|
1064
|
-
throw new Error("parallel.nonce is required to finalize the build");
|
|
1065
|
-
}
|
|
1066
|
-
const finalizeBuildsResult = await apiClient.POST("/builds/finalize", {
|
|
1067
|
-
body: {
|
|
1068
|
-
parallelNonce: config.parallelNonce
|
|
1069
|
-
}
|
|
1070
|
-
});
|
|
1071
|
-
if (finalizeBuildsResult.error) {
|
|
1072
|
-
throwAPIError(finalizeBuildsResult.error);
|
|
1073
|
-
}
|
|
1074
|
-
return finalizeBuildsResult.data;
|
|
1075
|
-
}
|
|
1076
|
-
|
|
1077
|
-
// src/upload.ts
|
|
1078
|
-
import { createClient as createClient3, throwAPIError as throwAPIError3 } from "@argos-ci/api-client";
|
|
1079
|
-
|
|
1080
|
-
// src/discovery.ts
|
|
1081
|
-
import { extname, resolve } from "path";
|
|
1082
|
-
import glob from "fast-glob";
|
|
1083
|
-
async function discoverSnapshots(patterns, { root = process.cwd(), ignore } = {}) {
|
|
1084
|
-
debug(
|
|
1085
|
-
`Discovering snapshots with patterns: ${Array.isArray(patterns) ? patterns.join(", ") : patterns} in ${root}`
|
|
1086
|
-
);
|
|
1087
|
-
const matches = await glob(patterns, { onlyFiles: true, ignore, cwd: root });
|
|
1088
|
-
return matches.map((match) => {
|
|
1089
|
-
debug(`Found screenshot: ${match}`);
|
|
1090
|
-
const path = resolve(root, match);
|
|
1091
|
-
return {
|
|
1092
|
-
name: match,
|
|
1093
|
-
path
|
|
1094
|
-
};
|
|
1095
|
-
});
|
|
1096
|
-
}
|
|
1097
|
-
function checkIsValidImageFile(filename) {
|
|
1098
|
-
const lowerFilename = extname(filename).toLowerCase();
|
|
1099
|
-
return lowerFilename === ".png" || lowerFilename === ".jpg" || lowerFilename === ".jpeg";
|
|
1100
|
-
}
|
|
1101
|
-
|
|
1102
|
-
// src/optimize.ts
|
|
1103
|
-
import { promisify } from "util";
|
|
1104
|
-
import { basename } from "path";
|
|
1105
|
-
import sharp from "sharp";
|
|
1106
|
-
import tmp from "tmp";
|
|
1107
|
-
var tmpFile = promisify(tmp.file);
|
|
1108
|
-
var MAX_PIXELS = 8e7;
|
|
1109
|
-
var DEFAULT_MAX_WIDTH = 2048;
|
|
1110
|
-
async function optimizeScreenshot(filepath) {
|
|
1111
|
-
if (!checkIsValidImageFile(filepath)) {
|
|
1112
|
-
return filepath;
|
|
1113
|
-
}
|
|
1114
|
-
try {
|
|
1115
|
-
const [resultFilePath, metadata] = await Promise.all([
|
|
1116
|
-
tmpFile(),
|
|
1117
|
-
sharp(filepath).metadata()
|
|
1118
|
-
]);
|
|
1119
|
-
const { width, height } = metadata;
|
|
1120
|
-
const maxDimensions = (() => {
|
|
1121
|
-
if (!width || !height) {
|
|
1122
|
-
return {
|
|
1123
|
-
width: DEFAULT_MAX_WIDTH,
|
|
1124
|
-
height: Math.floor(MAX_PIXELS / DEFAULT_MAX_WIDTH)
|
|
1125
|
-
};
|
|
1126
|
-
}
|
|
1127
|
-
const nbPixels = width * height;
|
|
1128
|
-
if (nbPixels <= MAX_PIXELS) {
|
|
1129
|
-
return null;
|
|
1130
|
-
}
|
|
1131
|
-
if (width < height) {
|
|
1132
|
-
return {
|
|
1133
|
-
width: DEFAULT_MAX_WIDTH,
|
|
1134
|
-
height: Math.floor(MAX_PIXELS / DEFAULT_MAX_WIDTH)
|
|
1135
|
-
};
|
|
1136
|
-
}
|
|
1137
|
-
const scaleFactor = Math.sqrt(MAX_PIXELS / nbPixels);
|
|
1138
|
-
return {
|
|
1139
|
-
width: Math.floor(width * scaleFactor),
|
|
1140
|
-
height: Math.floor(height * scaleFactor)
|
|
1141
|
-
};
|
|
1142
|
-
})();
|
|
1143
|
-
let operation = sharp(filepath);
|
|
1144
|
-
if (maxDimensions) {
|
|
1145
|
-
operation = operation.resize(maxDimensions.width, maxDimensions.height, {
|
|
1146
|
-
fit: "inside",
|
|
1147
|
-
withoutEnlargement: true
|
|
1148
|
-
});
|
|
1149
|
-
}
|
|
1150
|
-
await operation.png({ force: true }).toFile(resultFilePath);
|
|
1151
|
-
if (width && height && maxDimensions) {
|
|
1152
|
-
const { width: maxWidth, height: maxHeight } = maxDimensions;
|
|
1153
|
-
const widthRatio = maxWidth / width;
|
|
1154
|
-
const heightRatio = maxHeight / height;
|
|
1155
|
-
const scaleFactor = Math.min(widthRatio, heightRatio);
|
|
1156
|
-
const newWidth = Math.floor(width * scaleFactor);
|
|
1157
|
-
const newHeight = Math.floor(height * scaleFactor);
|
|
1158
|
-
console.warn(
|
|
1159
|
-
`Image ${basename(filepath)} resized from ${width}x${height} to ${newWidth}x${newHeight}.`
|
|
1160
|
-
);
|
|
1161
|
-
}
|
|
1162
|
-
return resultFilePath;
|
|
1163
|
-
} catch (error) {
|
|
1164
|
-
const message = error instanceof Error ? error.message : "Unknown Error";
|
|
1165
|
-
throw new Error(`Error while processing image (${filepath}): ${message}`, {
|
|
1166
|
-
cause: error
|
|
1167
|
-
});
|
|
1168
|
-
}
|
|
1169
|
-
}
|
|
1170
|
-
|
|
1171
|
-
// src/hashing.ts
|
|
1172
|
-
import { createReadStream } from "fs";
|
|
1173
|
-
import { createHash } from "crypto";
|
|
1174
|
-
var hashFile = async (filepath) => {
|
|
1175
|
-
const fileStream = createReadStream(filepath);
|
|
1176
|
-
const hash = createHash("sha256");
|
|
1177
|
-
await new Promise((resolve2, reject) => {
|
|
1178
|
-
fileStream.on("error", reject);
|
|
1179
|
-
hash.on("error", reject);
|
|
1180
|
-
hash.on("finish", resolve2);
|
|
1181
|
-
fileStream.pipe(hash);
|
|
1182
|
-
});
|
|
1183
|
-
return hash.digest("hex");
|
|
1184
|
-
};
|
|
1185
|
-
|
|
1186
|
-
// src/s3.ts
|
|
1187
|
-
import { readFile } from "fs/promises";
|
|
1188
|
-
async function uploadFile(input) {
|
|
1189
|
-
const file = await readFile(input.path);
|
|
1190
|
-
const response = await fetch(input.url, {
|
|
1191
|
-
method: "PUT",
|
|
1192
|
-
headers: {
|
|
1193
|
-
"Content-Type": input.contentType,
|
|
1194
|
-
"Content-Length": file.length.toString()
|
|
1195
|
-
},
|
|
1196
|
-
signal: AbortSignal.timeout(3e4),
|
|
1197
|
-
body: new Uint8Array(file)
|
|
1198
|
-
});
|
|
1199
|
-
if (!response.ok) {
|
|
1200
|
-
throw new Error(
|
|
1201
|
-
`Failed to upload file to ${input.url}: ${response.status} ${response.statusText}`
|
|
1202
|
-
);
|
|
1203
|
-
}
|
|
1204
|
-
}
|
|
1205
|
-
|
|
1206
|
-
// src/util/chunk.ts
|
|
1207
|
-
var chunk = (collection, size) => {
|
|
1208
|
-
const result = [];
|
|
1209
|
-
for (let x = 0; x < Math.ceil(collection.length / size); x++) {
|
|
1210
|
-
const start = x * size;
|
|
1211
|
-
const end = start + size;
|
|
1212
|
-
result.push(collection.slice(start, end));
|
|
1213
|
-
}
|
|
1214
|
-
return result;
|
|
1215
|
-
};
|
|
1216
|
-
|
|
1217
|
-
// src/upload.ts
|
|
1218
|
-
import {
|
|
1219
|
-
getPlaywrightTracePath,
|
|
1220
|
-
readMetadata
|
|
1221
|
-
} from "@argos-ci/util";
|
|
1222
|
-
|
|
1223
|
-
// src/version.ts
|
|
1224
|
-
import { readVersionFromPackage } from "@argos-ci/util";
|
|
1225
|
-
import { createRequire } from "module";
|
|
1226
|
-
var require2 = createRequire(import.meta.url);
|
|
1227
|
-
async function getArgosCoreSDKIdentifier() {
|
|
1228
|
-
const pkgPath = require2.resolve("@argos-ci/core/package.json");
|
|
1229
|
-
const version = await readVersionFromPackage(pkgPath);
|
|
1230
|
-
return `@argos-ci/core@${version}`;
|
|
1231
|
-
}
|
|
1232
|
-
|
|
1233
|
-
// src/mime-type.ts
|
|
1234
|
-
import mime from "mime-types";
|
|
1235
|
-
function getSnapshotMimeType(filepath) {
|
|
1236
|
-
const type = mime.lookup(filepath);
|
|
1237
|
-
if (!type) {
|
|
1238
|
-
throw new Error(`Unable to determine snapshot file type for: ${filepath}`);
|
|
1239
|
-
}
|
|
1240
|
-
return type;
|
|
1241
|
-
}
|
|
1242
|
-
|
|
1243
|
-
// src/skip.ts
|
|
1244
|
-
import { createClient as createClient2, throwAPIError as throwAPIError2 } from "@argos-ci/api-client";
|
|
1245
|
-
async function skip(params) {
|
|
1246
|
-
const [config, argosSdk] = await Promise.all([
|
|
1247
|
-
getConfigFromOptions(params),
|
|
1248
|
-
getArgosCoreSDKIdentifier()
|
|
1249
|
-
]);
|
|
1250
|
-
const authToken = getAuthToken(config);
|
|
1251
|
-
const apiClient = createClient2({
|
|
1252
|
-
baseUrl: config.apiBaseUrl,
|
|
1253
|
-
authToken
|
|
1254
|
-
});
|
|
1255
|
-
const createBuildResponse = await apiClient.POST("/builds", {
|
|
1256
|
-
body: {
|
|
1257
|
-
commit: config.commit,
|
|
1258
|
-
branch: config.branch,
|
|
1259
|
-
name: config.buildName,
|
|
1260
|
-
mode: config.mode,
|
|
1261
|
-
prNumber: config.prNumber,
|
|
1262
|
-
prHeadCommit: config.prHeadCommit,
|
|
1263
|
-
referenceBranch: config.referenceBranch,
|
|
1264
|
-
referenceCommit: config.referenceCommit,
|
|
1265
|
-
argosSdk,
|
|
1266
|
-
ciProvider: config.ciProvider,
|
|
1267
|
-
runId: config.runId,
|
|
1268
|
-
runAttempt: config.runAttempt,
|
|
1269
|
-
skipped: true,
|
|
1270
|
-
screenshotKeys: [],
|
|
1271
|
-
pwTraceKeys: [],
|
|
1272
|
-
parentCommits: []
|
|
1273
|
-
}
|
|
1274
|
-
});
|
|
1275
|
-
if (createBuildResponse.error) {
|
|
1276
|
-
throwAPIError2(createBuildResponse.error);
|
|
1277
|
-
}
|
|
1278
|
-
return { build: createBuildResponse.data.build };
|
|
1279
|
-
}
|
|
1280
|
-
|
|
1281
|
-
// src/upload.ts
|
|
1282
|
-
var CHUNK_SIZE = 10;
|
|
1283
|
-
async function upload(params) {
|
|
1284
|
-
debug("Starting upload with params", params);
|
|
1285
|
-
const [config, argosSdk] = await Promise.all([
|
|
1286
|
-
getConfigFromOptions(params),
|
|
1287
|
-
getArgosCoreSDKIdentifier()
|
|
1288
|
-
]);
|
|
1289
|
-
const authToken = getAuthToken(config);
|
|
1290
|
-
const apiClient = createClient3({
|
|
1291
|
-
baseUrl: config.apiBaseUrl,
|
|
1292
|
-
authToken
|
|
1293
|
-
});
|
|
1294
|
-
if (config.skipped) {
|
|
1295
|
-
const { build } = await skip(params);
|
|
1296
|
-
return { build, screenshots: [] };
|
|
1297
|
-
}
|
|
1298
|
-
const previewUrlFormatter = params.previewUrl ?? (config.previewBaseUrl ? { baseUrl: config.previewBaseUrl } : void 0);
|
|
1299
|
-
const globs = params.files ?? ["**/*.{png,jpg,jpeg}"];
|
|
1300
|
-
debug("Using config and files", config, globs);
|
|
1301
|
-
const files = await discoverSnapshots(globs, {
|
|
1302
|
-
root: params.root,
|
|
1303
|
-
ignore: params.ignore
|
|
1304
|
-
});
|
|
1305
|
-
debug("Found snapshots", files);
|
|
1306
|
-
const snapshots = await Promise.all(
|
|
1307
|
-
files.map(async (snapshot) => {
|
|
1308
|
-
const contentType = getSnapshotMimeType(snapshot.path);
|
|
1309
|
-
const [metadata, pwTracePath, optimizedPath] = await Promise.all([
|
|
1310
|
-
readMetadata(snapshot.path),
|
|
1311
|
-
getPlaywrightTracePath(snapshot.path),
|
|
1312
|
-
contentType.startsWith("image/") ? optimizeScreenshot(snapshot.path) : snapshot.path
|
|
1313
|
-
]);
|
|
1314
|
-
const [hash, pwTraceHash] = await Promise.all([
|
|
1315
|
-
hashFile(optimizedPath),
|
|
1316
|
-
pwTracePath ? hashFile(pwTracePath) : null
|
|
1317
|
-
]);
|
|
1318
|
-
const threshold = metadata?.transient?.threshold ?? null;
|
|
1319
|
-
const baseName = metadata?.transient?.baseName ?? null;
|
|
1320
|
-
const parentName = metadata?.transient?.parentName ?? null;
|
|
1321
|
-
if (metadata) {
|
|
1322
|
-
delete metadata.transient;
|
|
1323
|
-
if (metadata.url && previewUrlFormatter) {
|
|
1324
|
-
metadata.previewUrl = formatPreviewUrl(
|
|
1325
|
-
metadata.url,
|
|
1326
|
-
previewUrlFormatter
|
|
1327
|
-
);
|
|
1328
|
-
}
|
|
1329
|
-
}
|
|
1330
|
-
return {
|
|
1331
|
-
...snapshot,
|
|
1332
|
-
hash,
|
|
1333
|
-
optimizedPath,
|
|
1334
|
-
metadata,
|
|
1335
|
-
threshold,
|
|
1336
|
-
baseName,
|
|
1337
|
-
parentName,
|
|
1338
|
-
pwTrace: pwTracePath && pwTraceHash ? { path: pwTracePath, hash: pwTraceHash } : null,
|
|
1339
|
-
contentType
|
|
1340
|
-
};
|
|
1341
|
-
})
|
|
1342
|
-
);
|
|
1343
|
-
debug("Fetch project");
|
|
1344
|
-
const projectResponse = await apiClient.GET("/project");
|
|
1345
|
-
if (projectResponse.error) {
|
|
1346
|
-
throwAPIError3(projectResponse.error);
|
|
1347
|
-
}
|
|
1348
|
-
debug("Project fetched", projectResponse.data);
|
|
1349
|
-
const { defaultBaseBranch, hasRemoteContentAccess } = projectResponse.data;
|
|
1350
|
-
const referenceCommit = (() => {
|
|
1351
|
-
if (config.referenceCommit) {
|
|
1352
|
-
debug("Found reference commit in config", config.referenceCommit);
|
|
1353
|
-
return config.referenceCommit;
|
|
1354
|
-
}
|
|
1355
|
-
if (hasRemoteContentAccess) {
|
|
1356
|
-
return null;
|
|
1357
|
-
}
|
|
1358
|
-
const base = config.referenceBranch || config.prBaseBranch || defaultBaseBranch;
|
|
1359
|
-
const sha = getMergeBaseCommitSha2({ base, head: config.branch });
|
|
1360
|
-
if (sha) {
|
|
1361
|
-
debug("Found merge base", sha);
|
|
1362
|
-
} else {
|
|
1363
|
-
debug("No merge base found");
|
|
1364
|
-
}
|
|
1365
|
-
return sha;
|
|
1366
|
-
})();
|
|
1367
|
-
const parentCommits = (() => {
|
|
1368
|
-
if (hasRemoteContentAccess) {
|
|
1369
|
-
return null;
|
|
1370
|
-
}
|
|
1371
|
-
if (referenceCommit) {
|
|
1372
|
-
const commits = listParentCommits2({ sha: referenceCommit });
|
|
1373
|
-
if (commits) {
|
|
1374
|
-
debug("Found parent commits", commits);
|
|
1375
|
-
} else {
|
|
1376
|
-
debug("No parent commits found");
|
|
1377
|
-
}
|
|
1378
|
-
return commits;
|
|
1379
|
-
}
|
|
1380
|
-
return null;
|
|
1381
|
-
})();
|
|
1382
|
-
debug("Creating build");
|
|
1383
|
-
const [pwTraceKeys, snapshotKeys] = snapshots.reduce(
|
|
1384
|
-
([pwTraceKeys2, snapshotKeys2], snapshot) => {
|
|
1385
|
-
if (snapshot.pwTrace && !pwTraceKeys2.includes(snapshot.pwTrace.hash)) {
|
|
1386
|
-
pwTraceKeys2.push(snapshot.pwTrace.hash);
|
|
1387
|
-
}
|
|
1388
|
-
if (!snapshotKeys2.includes(snapshot.hash)) {
|
|
1389
|
-
snapshotKeys2.push(snapshot.hash);
|
|
1390
|
-
}
|
|
1391
|
-
return [pwTraceKeys2, snapshotKeys2];
|
|
1392
|
-
},
|
|
1393
|
-
[[], []]
|
|
1394
|
-
);
|
|
1395
|
-
const createBuildResponse = await apiClient.POST("/builds", {
|
|
1396
|
-
body: {
|
|
1397
|
-
commit: config.commit,
|
|
1398
|
-
branch: config.branch,
|
|
1399
|
-
name: config.buildName,
|
|
1400
|
-
mode: config.mode,
|
|
1401
|
-
parallel: config.parallel,
|
|
1402
|
-
parallelNonce: config.parallelNonce,
|
|
1403
|
-
screenshotKeys: snapshotKeys,
|
|
1404
|
-
pwTraceKeys,
|
|
1405
|
-
prNumber: config.prNumber,
|
|
1406
|
-
prHeadCommit: config.prHeadCommit,
|
|
1407
|
-
referenceBranch: config.referenceBranch,
|
|
1408
|
-
referenceCommit,
|
|
1409
|
-
parentCommits,
|
|
1410
|
-
argosSdk,
|
|
1411
|
-
ciProvider: config.ciProvider,
|
|
1412
|
-
runId: config.runId,
|
|
1413
|
-
runAttempt: config.runAttempt,
|
|
1414
|
-
mergeQueue: config.mergeQueue,
|
|
1415
|
-
subset: config.subset
|
|
1416
|
-
}
|
|
1417
|
-
});
|
|
1418
|
-
if (createBuildResponse.error) {
|
|
1419
|
-
throwAPIError3(createBuildResponse.error);
|
|
1420
|
-
}
|
|
1421
|
-
const result = createBuildResponse.data;
|
|
1422
|
-
debug("Got uploads url", result);
|
|
1423
|
-
const uploadFiles = [
|
|
1424
|
-
...result.screenshots.map(({ key, putUrl }) => {
|
|
1425
|
-
const snapshot = snapshots.find((s) => s.hash === key);
|
|
1426
|
-
if (!snapshot) {
|
|
1427
|
-
throw new Error(`Invariant: snapshot with hash ${key} not found`);
|
|
1428
|
-
}
|
|
1429
|
-
return {
|
|
1430
|
-
url: putUrl,
|
|
1431
|
-
path: snapshot.optimizedPath,
|
|
1432
|
-
contentType: snapshot.contentType
|
|
1433
|
-
};
|
|
1434
|
-
}),
|
|
1435
|
-
...result.pwTraces?.map(({ key, putUrl }) => {
|
|
1436
|
-
const snapshot = snapshots.find(
|
|
1437
|
-
(s) => s.pwTrace && s.pwTrace.hash === key
|
|
1438
|
-
);
|
|
1439
|
-
if (!snapshot || !snapshot.pwTrace) {
|
|
1440
|
-
throw new Error(`Invariant: trace with ${key} not found`);
|
|
1441
|
-
}
|
|
1442
|
-
return {
|
|
1443
|
-
url: putUrl,
|
|
1444
|
-
path: snapshot.pwTrace.path,
|
|
1445
|
-
contentType: "application/json"
|
|
1446
|
-
};
|
|
1447
|
-
}) ?? []
|
|
1448
|
-
];
|
|
1449
|
-
await uploadFilesToS3(uploadFiles);
|
|
1450
|
-
debug("Updating build");
|
|
1451
|
-
const uploadBuildResponse = await apiClient.PUT("/builds/{buildId}", {
|
|
1452
|
-
params: {
|
|
1453
|
-
path: {
|
|
1454
|
-
buildId: result.build.id
|
|
1455
|
-
}
|
|
1456
|
-
},
|
|
1457
|
-
body: {
|
|
1458
|
-
screenshots: snapshots.map((snapshot) => ({
|
|
1459
|
-
key: snapshot.hash,
|
|
1460
|
-
name: snapshot.name,
|
|
1461
|
-
metadata: snapshot.metadata,
|
|
1462
|
-
pwTraceKey: snapshot.pwTrace?.hash ?? null,
|
|
1463
|
-
threshold: snapshot.threshold ?? config?.threshold ?? null,
|
|
1464
|
-
baseName: snapshot.baseName,
|
|
1465
|
-
parentName: snapshot.parentName,
|
|
1466
|
-
contentType: snapshot.contentType
|
|
1467
|
-
})),
|
|
1468
|
-
parallel: config.parallel,
|
|
1469
|
-
parallelTotal: config.parallelTotal,
|
|
1470
|
-
parallelIndex: config.parallelIndex,
|
|
1471
|
-
metadata: params.metadata
|
|
1472
|
-
}
|
|
1473
|
-
});
|
|
1474
|
-
if (uploadBuildResponse.error) {
|
|
1475
|
-
throwAPIError3(uploadBuildResponse.error);
|
|
1476
|
-
}
|
|
1477
|
-
return { build: uploadBuildResponse.data.build, screenshots: snapshots };
|
|
1478
|
-
}
|
|
1479
|
-
async function uploadFilesToS3(files) {
|
|
1480
|
-
debug(`Split files in chunks of ${CHUNK_SIZE}`);
|
|
1481
|
-
const chunks = chunk(files, CHUNK_SIZE);
|
|
1482
|
-
debug(`Starting upload of ${chunks.length} chunks`);
|
|
1483
|
-
for (let i = 0; i < chunks.length; i++) {
|
|
1484
|
-
debug(`Uploading chunk ${i + 1}/${chunks.length}`);
|
|
1485
|
-
const timeLabel = `Chunk ${i + 1}/${chunks.length}`;
|
|
1486
|
-
debugTime(timeLabel);
|
|
1487
|
-
const chunk2 = chunks[i];
|
|
1488
|
-
if (!chunk2) {
|
|
1489
|
-
throw new Error(`Invariant: chunk ${i} is empty`);
|
|
1490
|
-
}
|
|
1491
|
-
await Promise.all(
|
|
1492
|
-
chunk2.map(async ({ url, path, contentType }) => {
|
|
1493
|
-
await uploadFile({
|
|
1494
|
-
url,
|
|
1495
|
-
path,
|
|
1496
|
-
contentType
|
|
1497
|
-
});
|
|
1498
|
-
})
|
|
1499
|
-
);
|
|
1500
|
-
debugTimeEnd(timeLabel);
|
|
1501
|
-
}
|
|
1502
|
-
}
|
|
1503
|
-
function formatPreviewUrl(url, formatter) {
|
|
1504
|
-
if (typeof formatter === "function") {
|
|
1505
|
-
return formatter(url);
|
|
1506
|
-
}
|
|
1507
|
-
const urlObj = new URL(url);
|
|
1508
|
-
return new URL(
|
|
1509
|
-
urlObj.pathname + urlObj.search + urlObj.hash,
|
|
1510
|
-
formatter.baseUrl
|
|
1511
|
-
).href;
|
|
1512
|
-
}
|
|
1513
|
-
export {
|
|
1514
|
-
finalize,
|
|
1515
|
-
getConfigFromOptions,
|
|
1516
|
-
readConfig,
|
|
1517
|
-
skip,
|
|
1518
|
-
upload
|
|
1519
|
-
};
|