@argos-ci/core 2.9.2 → 2.11.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 +11 -12
- package/dist/index.d.ts +140 -23
- package/dist/index.js +1032 -0
- package/package.json +11 -12
- package/dist/index.mjs +0 -1039
package/dist/index.js
ADDED
|
@@ -0,0 +1,1032 @@
|
|
|
1
|
+
// src/upload.ts
|
|
2
|
+
import {
|
|
3
|
+
createClient,
|
|
4
|
+
throwAPIError
|
|
5
|
+
} from "@argos-ci/api-client";
|
|
6
|
+
|
|
7
|
+
// src/config.ts
|
|
8
|
+
import convict from "convict";
|
|
9
|
+
|
|
10
|
+
// src/ci-environment/git.ts
|
|
11
|
+
import { execSync } from "node:child_process";
|
|
12
|
+
function checkIsGitRepository() {
|
|
13
|
+
try {
|
|
14
|
+
return execSync("git rev-parse --is-inside-work-tree").toString().trim() === "true";
|
|
15
|
+
} catch {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function head() {
|
|
20
|
+
try {
|
|
21
|
+
return execSync("git rev-parse HEAD").toString().trim();
|
|
22
|
+
} catch {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function branch() {
|
|
27
|
+
try {
|
|
28
|
+
const headRef = execSync("git rev-parse --abbrev-ref HEAD").toString().trim();
|
|
29
|
+
if (headRef === "HEAD") {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
return headRef;
|
|
33
|
+
} catch {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function getMergeBaseCommitShaWithDepth(input) {
|
|
38
|
+
try {
|
|
39
|
+
execSync(
|
|
40
|
+
`git fetch --update-head-ok --depth ${input.depth} origin ${input.head}:${input.head}`
|
|
41
|
+
);
|
|
42
|
+
execSync(
|
|
43
|
+
`git fetch --update-head-ok --depth ${input.depth} origin ${input.base}:${input.base}`
|
|
44
|
+
);
|
|
45
|
+
const mergeBase = execSync(`git merge-base ${input.head} ${input.base}`).toString().trim();
|
|
46
|
+
return mergeBase || null;
|
|
47
|
+
} catch {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function getMergeBaseCommitSha(input) {
|
|
52
|
+
let depth = 200;
|
|
53
|
+
while (depth < 1e3) {
|
|
54
|
+
const mergeBase = getMergeBaseCommitShaWithDepth({
|
|
55
|
+
depth,
|
|
56
|
+
...input
|
|
57
|
+
});
|
|
58
|
+
if (mergeBase) {
|
|
59
|
+
return mergeBase;
|
|
60
|
+
}
|
|
61
|
+
depth += 200;
|
|
62
|
+
}
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
function listParentCommits(input) {
|
|
66
|
+
try {
|
|
67
|
+
execSync(`git fetch --depth=200 origin ${input.sha}`);
|
|
68
|
+
const raw = execSync(`git log --format="%H" --max-count=200 ${input.sha}`);
|
|
69
|
+
const shas = raw.toString().trim().split("\n");
|
|
70
|
+
return shas;
|
|
71
|
+
} catch {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// src/ci-environment/services/bitrise.ts
|
|
77
|
+
var getPrNumber = ({ env }) => {
|
|
78
|
+
return env.BITRISE_PULL_REQUEST ? Number(env.BITRISE_PULL_REQUEST) : null;
|
|
79
|
+
};
|
|
80
|
+
var service = {
|
|
81
|
+
name: "Bitrise",
|
|
82
|
+
key: "bitrise",
|
|
83
|
+
detect: ({ env }) => Boolean(env.BITRISE_IO),
|
|
84
|
+
config: ({ env }) => {
|
|
85
|
+
return {
|
|
86
|
+
commit: env.BITRISE_GIT_COMMIT || null,
|
|
87
|
+
branch: env.BITRISE_GIT_BRANCH || null,
|
|
88
|
+
owner: env.BITRISEIO_GIT_REPOSITORY_OWNER || null,
|
|
89
|
+
repository: env.BITRISEIO_GIT_REPOSITORY_SLUG || null,
|
|
90
|
+
jobId: null,
|
|
91
|
+
runId: null,
|
|
92
|
+
runAttempt: null,
|
|
93
|
+
prNumber: getPrNumber({ env }),
|
|
94
|
+
prHeadCommit: null,
|
|
95
|
+
prBaseBranch: null,
|
|
96
|
+
nonce: env.BITRISEIO_PIPELINE_ID || null
|
|
97
|
+
};
|
|
98
|
+
},
|
|
99
|
+
getMergeBaseCommitSha,
|
|
100
|
+
listParentCommits
|
|
101
|
+
};
|
|
102
|
+
var bitrise_default = service;
|
|
103
|
+
|
|
104
|
+
// src/ci-environment/services/buildkite.ts
|
|
105
|
+
var service2 = {
|
|
106
|
+
name: "Buildkite",
|
|
107
|
+
key: "buildkite",
|
|
108
|
+
detect: ({ env }) => Boolean(env.BUILDKITE),
|
|
109
|
+
config: ({ env }) => {
|
|
110
|
+
return {
|
|
111
|
+
// Buildkite doesn't work well so we fallback to git to ensure we have commit and branch
|
|
112
|
+
commit: env.BUILDKITE_COMMIT || head() || null,
|
|
113
|
+
branch: env.BUILDKITE_BRANCH || branch() || null,
|
|
114
|
+
owner: env.BUILDKITE_ORGANIZATION_SLUG || null,
|
|
115
|
+
repository: env.BUILDKITE_PROJECT_SLUG || null,
|
|
116
|
+
jobId: null,
|
|
117
|
+
runId: null,
|
|
118
|
+
runAttempt: null,
|
|
119
|
+
prNumber: env.BUILDKITE_PULL_REQUEST ? Number(env.BUILDKITE_PULL_REQUEST) : null,
|
|
120
|
+
prHeadCommit: null,
|
|
121
|
+
prBaseBranch: null,
|
|
122
|
+
nonce: env.BUILDKITE_BUILD_ID || null
|
|
123
|
+
};
|
|
124
|
+
},
|
|
125
|
+
getMergeBaseCommitSha,
|
|
126
|
+
listParentCommits
|
|
127
|
+
};
|
|
128
|
+
var buildkite_default = service2;
|
|
129
|
+
|
|
130
|
+
// src/ci-environment/services/heroku.ts
|
|
131
|
+
var service3 = {
|
|
132
|
+
name: "Heroku",
|
|
133
|
+
key: "heroku",
|
|
134
|
+
detect: ({ env }) => Boolean(env.HEROKU_TEST_RUN_ID),
|
|
135
|
+
config: ({ env }) => ({
|
|
136
|
+
commit: env.HEROKU_TEST_RUN_COMMIT_VERSION || null,
|
|
137
|
+
branch: env.HEROKU_TEST_RUN_BRANCH || null,
|
|
138
|
+
owner: null,
|
|
139
|
+
repository: null,
|
|
140
|
+
jobId: null,
|
|
141
|
+
runId: null,
|
|
142
|
+
runAttempt: null,
|
|
143
|
+
prNumber: null,
|
|
144
|
+
prHeadCommit: null,
|
|
145
|
+
prBaseBranch: null,
|
|
146
|
+
nonce: env.HEROKU_TEST_RUN_ID || null
|
|
147
|
+
}),
|
|
148
|
+
getMergeBaseCommitSha,
|
|
149
|
+
listParentCommits
|
|
150
|
+
};
|
|
151
|
+
var heroku_default = service3;
|
|
152
|
+
|
|
153
|
+
// src/ci-environment/services/github-actions.ts
|
|
154
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
155
|
+
import axios from "axios";
|
|
156
|
+
|
|
157
|
+
// src/debug.ts
|
|
158
|
+
import createDebug from "debug";
|
|
159
|
+
var KEY = "@argos-ci/core";
|
|
160
|
+
var debug = createDebug(KEY);
|
|
161
|
+
var debugTime = (arg) => {
|
|
162
|
+
const enabled = createDebug.enabled(KEY);
|
|
163
|
+
if (enabled) {
|
|
164
|
+
console.time(arg);
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
var debugTimeEnd = (arg) => {
|
|
168
|
+
const enabled = createDebug.enabled(KEY);
|
|
169
|
+
if (enabled) {
|
|
170
|
+
console.timeEnd(arg);
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
// src/ci-environment/services/github-actions.ts
|
|
175
|
+
async function getPullRequestFromHeadSha({ env }, sha) {
|
|
176
|
+
debug("Fetching pull request number from head sha", sha);
|
|
177
|
+
if (!env.GITHUB_REPOSITORY) {
|
|
178
|
+
throw new Error("GITHUB_REPOSITORY is missing");
|
|
179
|
+
}
|
|
180
|
+
if (!env.GITHUB_TOKEN) {
|
|
181
|
+
if (!env.DISABLE_GITHUB_TOKEN_WARNING) {
|
|
182
|
+
console.log(
|
|
183
|
+
`
|
|
184
|
+
Running argos from a "deployment_status" event requires a GITHUB_TOKEN.
|
|
185
|
+
Please add \`GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }}\` as environment variable.
|
|
186
|
+
|
|
187
|
+
Read more at https://argos-ci.com/docs/run-on-preview-deployment
|
|
188
|
+
|
|
189
|
+
To disable this warning, add \`DISABLE_GITHUB_TOKEN_WARNING: true\` as environment variable.
|
|
190
|
+
`.trim()
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
try {
|
|
196
|
+
const result = await axios.get(
|
|
197
|
+
`https://api.github.com/repos/${env.GITHUB_REPOSITORY}/pulls`,
|
|
198
|
+
{
|
|
199
|
+
params: {
|
|
200
|
+
state: "open",
|
|
201
|
+
sort: "updated",
|
|
202
|
+
per_page: 30,
|
|
203
|
+
page: 1
|
|
204
|
+
},
|
|
205
|
+
headers: {
|
|
206
|
+
Accept: "application/vnd.github+json",
|
|
207
|
+
Authorization: `Bearer ${process.env.GITHUB_TOKEN}`,
|
|
208
|
+
"X-GitHub-Api-Version": "2022-11-28"
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
);
|
|
212
|
+
if (result.data.length === 0) {
|
|
213
|
+
debug("Aborting because no pull request found");
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
const matchingPr = result.data.find((pr) => pr.head.sha === sha);
|
|
217
|
+
if (matchingPr) {
|
|
218
|
+
debug("Pull request found", matchingPr);
|
|
219
|
+
return matchingPr;
|
|
220
|
+
}
|
|
221
|
+
debug("Aborting because no pull request found");
|
|
222
|
+
return null;
|
|
223
|
+
} catch (error) {
|
|
224
|
+
debug("Error while fetching pull request from head sha", error);
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
function getBranch(context, eventPayload) {
|
|
229
|
+
if (eventPayload?.pull_request?.head.ref) {
|
|
230
|
+
return eventPayload.pull_request.head.ref;
|
|
231
|
+
}
|
|
232
|
+
const { env } = context;
|
|
233
|
+
if (env.GITHUB_HEAD_REF) {
|
|
234
|
+
return env.GITHUB_HEAD_REF;
|
|
235
|
+
}
|
|
236
|
+
if (!env.GITHUB_REF) {
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
const branchRegex = /refs\/heads\/(.*)/;
|
|
240
|
+
const matches = branchRegex.exec(env.GITHUB_REF);
|
|
241
|
+
return matches?.[1] ?? null;
|
|
242
|
+
}
|
|
243
|
+
function getRepository({ env }) {
|
|
244
|
+
if (!env.GITHUB_REPOSITORY) return null;
|
|
245
|
+
return env.GITHUB_REPOSITORY.split("/")[1] || null;
|
|
246
|
+
}
|
|
247
|
+
function readEventPayload({ env }) {
|
|
248
|
+
if (!env.GITHUB_EVENT_PATH) return null;
|
|
249
|
+
if (!existsSync(env.GITHUB_EVENT_PATH)) return null;
|
|
250
|
+
return JSON.parse(readFileSync(env.GITHUB_EVENT_PATH, "utf-8"));
|
|
251
|
+
}
|
|
252
|
+
var service4 = {
|
|
253
|
+
name: "GitHub Actions",
|
|
254
|
+
key: "github-actions",
|
|
255
|
+
detect: (context) => Boolean(context.env.GITHUB_ACTIONS),
|
|
256
|
+
config: async (context) => {
|
|
257
|
+
const { env } = context;
|
|
258
|
+
const payload = readEventPayload(context);
|
|
259
|
+
const sha = process.env.GITHUB_SHA || null;
|
|
260
|
+
if (!sha) {
|
|
261
|
+
throw new Error(`GITHUB_SHA is missing`);
|
|
262
|
+
}
|
|
263
|
+
const commonConfig = {
|
|
264
|
+
commit: sha,
|
|
265
|
+
owner: env.GITHUB_REPOSITORY_OWNER || null,
|
|
266
|
+
repository: getRepository(context),
|
|
267
|
+
jobId: env.GITHUB_JOB || null,
|
|
268
|
+
runId: env.GITHUB_RUN_ID || null,
|
|
269
|
+
runAttempt: env.GITHUB_RUN_ATTEMPT ? Number(env.GITHUB_RUN_ATTEMPT) : null,
|
|
270
|
+
nonce: `${env.GITHUB_RUN_ID}-${env.GITHUB_RUN_ATTEMPT}` || null
|
|
271
|
+
};
|
|
272
|
+
if (payload?.deployment) {
|
|
273
|
+
debug("Deployment event detected");
|
|
274
|
+
const pullRequest = await getPullRequestFromHeadSha(context, sha);
|
|
275
|
+
return {
|
|
276
|
+
...commonConfig,
|
|
277
|
+
// If no pull request is found, we fallback to the deployment environment as branch name
|
|
278
|
+
// Branch name is required to create a build but has no real impact on the build.
|
|
279
|
+
branch: pullRequest?.head.ref || payload.deployment.environment || null,
|
|
280
|
+
prNumber: pullRequest?.number || null,
|
|
281
|
+
prHeadCommit: pullRequest?.head.sha || null,
|
|
282
|
+
prBaseBranch: null
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
return {
|
|
286
|
+
...commonConfig,
|
|
287
|
+
branch: payload?.pull_request?.head.ref || getBranch(context, payload) || null,
|
|
288
|
+
prNumber: payload?.pull_request?.number || null,
|
|
289
|
+
prHeadCommit: payload?.pull_request?.head.sha ?? null,
|
|
290
|
+
prBaseBranch: payload?.pull_request?.base.ref ?? null
|
|
291
|
+
};
|
|
292
|
+
},
|
|
293
|
+
getMergeBaseCommitSha,
|
|
294
|
+
listParentCommits
|
|
295
|
+
};
|
|
296
|
+
var github_actions_default = service4;
|
|
297
|
+
|
|
298
|
+
// src/ci-environment/services/circleci.ts
|
|
299
|
+
var getPrNumber2 = ({ env }) => {
|
|
300
|
+
const branchRegex = /pull\/(\d+)/;
|
|
301
|
+
const matches = branchRegex.exec(env.CIRCLE_PULL_REQUEST || "");
|
|
302
|
+
if (matches) {
|
|
303
|
+
return Number(matches[1]);
|
|
304
|
+
}
|
|
305
|
+
return null;
|
|
306
|
+
};
|
|
307
|
+
var service5 = {
|
|
308
|
+
name: "CircleCI",
|
|
309
|
+
key: "circleci",
|
|
310
|
+
detect: ({ env }) => Boolean(env.CIRCLECI),
|
|
311
|
+
config: ({ env }) => {
|
|
312
|
+
return {
|
|
313
|
+
commit: env.CIRCLE_SHA1 || null,
|
|
314
|
+
branch: env.CIRCLE_BRANCH || null,
|
|
315
|
+
owner: env.CIRCLE_PROJECT_USERNAME || null,
|
|
316
|
+
repository: env.CIRCLE_PROJECT_REPONAME || null,
|
|
317
|
+
jobId: null,
|
|
318
|
+
runId: null,
|
|
319
|
+
runAttempt: null,
|
|
320
|
+
prNumber: getPrNumber2({ env }),
|
|
321
|
+
prHeadCommit: null,
|
|
322
|
+
prBaseBranch: null,
|
|
323
|
+
nonce: env.CIRCLE_WORKFLOW_ID || env.CIRCLE_BUILD_NUM || null
|
|
324
|
+
};
|
|
325
|
+
},
|
|
326
|
+
getMergeBaseCommitSha,
|
|
327
|
+
listParentCommits
|
|
328
|
+
};
|
|
329
|
+
var circleci_default = service5;
|
|
330
|
+
|
|
331
|
+
// src/ci-environment/services/travis.ts
|
|
332
|
+
var getOwner = ({ env }) => {
|
|
333
|
+
if (!env.TRAVIS_REPO_SLUG) return null;
|
|
334
|
+
return env.TRAVIS_REPO_SLUG.split("/")[0] || null;
|
|
335
|
+
};
|
|
336
|
+
var getRepository2 = ({ env }) => {
|
|
337
|
+
if (!env.TRAVIS_REPO_SLUG) return null;
|
|
338
|
+
return env.TRAVIS_REPO_SLUG.split("/")[1] || null;
|
|
339
|
+
};
|
|
340
|
+
var getPrNumber3 = ({ env }) => {
|
|
341
|
+
if (env.TRAVIS_PULL_REQUEST) return Number(env.TRAVIS_PULL_REQUEST);
|
|
342
|
+
return null;
|
|
343
|
+
};
|
|
344
|
+
var service6 = {
|
|
345
|
+
name: "Travis CI",
|
|
346
|
+
key: "travis",
|
|
347
|
+
detect: ({ env }) => Boolean(env.TRAVIS),
|
|
348
|
+
config: (ctx) => {
|
|
349
|
+
const { env } = ctx;
|
|
350
|
+
return {
|
|
351
|
+
commit: env.TRAVIS_COMMIT || null,
|
|
352
|
+
branch: env.TRAVIS_BRANCH || null,
|
|
353
|
+
owner: getOwner(ctx),
|
|
354
|
+
repository: getRepository2(ctx),
|
|
355
|
+
jobId: null,
|
|
356
|
+
runId: null,
|
|
357
|
+
runAttempt: null,
|
|
358
|
+
prNumber: getPrNumber3(ctx),
|
|
359
|
+
prHeadCommit: null,
|
|
360
|
+
prBaseBranch: null,
|
|
361
|
+
nonce: env.TRAVIS_BUILD_ID || null
|
|
362
|
+
};
|
|
363
|
+
},
|
|
364
|
+
getMergeBaseCommitSha,
|
|
365
|
+
listParentCommits
|
|
366
|
+
};
|
|
367
|
+
var travis_default = service6;
|
|
368
|
+
|
|
369
|
+
// src/ci-environment/services/gitlab.ts
|
|
370
|
+
var service7 = {
|
|
371
|
+
name: "GitLab",
|
|
372
|
+
key: "gitlab",
|
|
373
|
+
detect: ({ env }) => env.GITLAB_CI === "true",
|
|
374
|
+
config: ({ env }) => {
|
|
375
|
+
return {
|
|
376
|
+
commit: env.CI_COMMIT_SHA || null,
|
|
377
|
+
branch: env.CI_COMMIT_REF_NAME || null,
|
|
378
|
+
owner: null,
|
|
379
|
+
repository: null,
|
|
380
|
+
jobId: null,
|
|
381
|
+
runId: null,
|
|
382
|
+
runAttempt: null,
|
|
383
|
+
prNumber: null,
|
|
384
|
+
prHeadCommit: null,
|
|
385
|
+
prBaseBranch: null,
|
|
386
|
+
nonce: env.CI_PIPELINE_ID || null
|
|
387
|
+
};
|
|
388
|
+
},
|
|
389
|
+
getMergeBaseCommitSha,
|
|
390
|
+
listParentCommits
|
|
391
|
+
};
|
|
392
|
+
var gitlab_default = service7;
|
|
393
|
+
|
|
394
|
+
// src/ci-environment/services/git.ts
|
|
395
|
+
var service8 = {
|
|
396
|
+
name: "Git",
|
|
397
|
+
key: "git",
|
|
398
|
+
detect: () => checkIsGitRepository(),
|
|
399
|
+
config: () => {
|
|
400
|
+
return {
|
|
401
|
+
commit: head() || null,
|
|
402
|
+
branch: branch() || null,
|
|
403
|
+
owner: null,
|
|
404
|
+
repository: null,
|
|
405
|
+
jobId: null,
|
|
406
|
+
runId: null,
|
|
407
|
+
runAttempt: null,
|
|
408
|
+
prNumber: null,
|
|
409
|
+
prHeadCommit: null,
|
|
410
|
+
prBaseBranch: null,
|
|
411
|
+
nonce: null
|
|
412
|
+
};
|
|
413
|
+
},
|
|
414
|
+
getMergeBaseCommitSha,
|
|
415
|
+
listParentCommits
|
|
416
|
+
};
|
|
417
|
+
var git_default = service8;
|
|
418
|
+
|
|
419
|
+
// src/ci-environment/index.ts
|
|
420
|
+
var services = [
|
|
421
|
+
heroku_default,
|
|
422
|
+
github_actions_default,
|
|
423
|
+
circleci_default,
|
|
424
|
+
travis_default,
|
|
425
|
+
buildkite_default,
|
|
426
|
+
gitlab_default,
|
|
427
|
+
bitrise_default,
|
|
428
|
+
git_default
|
|
429
|
+
];
|
|
430
|
+
function createContext() {
|
|
431
|
+
return { env: process.env };
|
|
432
|
+
}
|
|
433
|
+
function getCiService(context) {
|
|
434
|
+
return services.find((service9) => service9.detect(context));
|
|
435
|
+
}
|
|
436
|
+
function getMergeBaseCommitSha2(input) {
|
|
437
|
+
const context = createContext();
|
|
438
|
+
const service9 = getCiService(context);
|
|
439
|
+
if (!service9) {
|
|
440
|
+
return null;
|
|
441
|
+
}
|
|
442
|
+
return service9.getMergeBaseCommitSha(input, context);
|
|
443
|
+
}
|
|
444
|
+
function listParentCommits2(input) {
|
|
445
|
+
const context = createContext();
|
|
446
|
+
const service9 = getCiService(context);
|
|
447
|
+
if (!service9) {
|
|
448
|
+
return null;
|
|
449
|
+
}
|
|
450
|
+
return service9.listParentCommits(input, context);
|
|
451
|
+
}
|
|
452
|
+
async function getCiEnvironment() {
|
|
453
|
+
const context = createContext();
|
|
454
|
+
debug("Detecting CI environment", context);
|
|
455
|
+
const service9 = getCiService(context);
|
|
456
|
+
if (service9) {
|
|
457
|
+
debug("Internal service matched", service9.name);
|
|
458
|
+
const variables = await service9.config(context);
|
|
459
|
+
const ciEnvironment = {
|
|
460
|
+
name: service9.name,
|
|
461
|
+
key: service9.key,
|
|
462
|
+
...variables
|
|
463
|
+
};
|
|
464
|
+
debug("CI environment", ciEnvironment);
|
|
465
|
+
return ciEnvironment;
|
|
466
|
+
}
|
|
467
|
+
return null;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// src/config.ts
|
|
471
|
+
var mustBeApiBaseUrl = (value) => {
|
|
472
|
+
const URL_REGEX = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/;
|
|
473
|
+
if (!URL_REGEX.test(value)) {
|
|
474
|
+
throw new Error("Invalid Argos API base URL");
|
|
475
|
+
}
|
|
476
|
+
};
|
|
477
|
+
var mustBeCommit = (value) => {
|
|
478
|
+
const SHA1_REGEX = /^[0-9a-f]{40}$/;
|
|
479
|
+
if (!SHA1_REGEX.test(value)) {
|
|
480
|
+
const SHA1_SHORT_REGEX = /^[0-9a-f]{7}$/;
|
|
481
|
+
if (SHA1_SHORT_REGEX.test(value)) {
|
|
482
|
+
throw new Error("Short SHA1 is not allowed");
|
|
483
|
+
}
|
|
484
|
+
throw new Error("Invalid commit");
|
|
485
|
+
}
|
|
486
|
+
};
|
|
487
|
+
var mustBeArgosToken = (value) => {
|
|
488
|
+
if (value && value.length !== 40) {
|
|
489
|
+
throw new Error("Invalid Argos repository token (must be 40 characters)");
|
|
490
|
+
}
|
|
491
|
+
};
|
|
492
|
+
convict.addFormat({
|
|
493
|
+
name: "float-percent",
|
|
494
|
+
validate: function(val) {
|
|
495
|
+
if (val !== 0 && (!val || val > 1 || val < 0)) {
|
|
496
|
+
throw new Error("Must be a float between 0 and 1, inclusive.");
|
|
497
|
+
}
|
|
498
|
+
},
|
|
499
|
+
coerce: function(val) {
|
|
500
|
+
return parseFloat(val);
|
|
501
|
+
}
|
|
502
|
+
});
|
|
503
|
+
var schema = {
|
|
504
|
+
apiBaseUrl: {
|
|
505
|
+
env: "ARGOS_API_BASE_URL",
|
|
506
|
+
default: "https://api.argos-ci.com/v2/",
|
|
507
|
+
format: mustBeApiBaseUrl
|
|
508
|
+
},
|
|
509
|
+
commit: {
|
|
510
|
+
env: "ARGOS_COMMIT",
|
|
511
|
+
default: null,
|
|
512
|
+
format: mustBeCommit
|
|
513
|
+
},
|
|
514
|
+
branch: {
|
|
515
|
+
env: "ARGOS_BRANCH",
|
|
516
|
+
default: null,
|
|
517
|
+
format: String
|
|
518
|
+
},
|
|
519
|
+
token: {
|
|
520
|
+
env: "ARGOS_TOKEN",
|
|
521
|
+
default: null,
|
|
522
|
+
format: mustBeArgosToken
|
|
523
|
+
},
|
|
524
|
+
buildName: {
|
|
525
|
+
env: "ARGOS_BUILD_NAME",
|
|
526
|
+
default: null,
|
|
527
|
+
format: String,
|
|
528
|
+
nullable: true
|
|
529
|
+
},
|
|
530
|
+
mode: {
|
|
531
|
+
env: "ARGOS_MODE",
|
|
532
|
+
format: ["ci", "monitoring"],
|
|
533
|
+
default: null,
|
|
534
|
+
nullable: true
|
|
535
|
+
},
|
|
536
|
+
prNumber: {
|
|
537
|
+
env: "ARGOS_PR_NUMBER",
|
|
538
|
+
format: Number,
|
|
539
|
+
default: null,
|
|
540
|
+
nullable: true
|
|
541
|
+
},
|
|
542
|
+
prHeadCommit: {
|
|
543
|
+
env: "ARGOS_PR_HEAD_COMMIT",
|
|
544
|
+
format: String,
|
|
545
|
+
default: null,
|
|
546
|
+
nullable: true
|
|
547
|
+
},
|
|
548
|
+
prBaseBranch: {
|
|
549
|
+
env: "ARGOS_PR_BASE_BRANCH",
|
|
550
|
+
format: String,
|
|
551
|
+
default: null,
|
|
552
|
+
nullable: true
|
|
553
|
+
},
|
|
554
|
+
parallel: {
|
|
555
|
+
env: "ARGOS_PARALLEL",
|
|
556
|
+
default: false,
|
|
557
|
+
format: Boolean
|
|
558
|
+
},
|
|
559
|
+
parallelNonce: {
|
|
560
|
+
env: "ARGOS_PARALLEL_NONCE",
|
|
561
|
+
format: String,
|
|
562
|
+
default: null,
|
|
563
|
+
nullable: true
|
|
564
|
+
},
|
|
565
|
+
parallelIndex: {
|
|
566
|
+
env: "ARGOS_PARALLEL_INDEX",
|
|
567
|
+
format: "nat",
|
|
568
|
+
default: null,
|
|
569
|
+
nullable: true
|
|
570
|
+
},
|
|
571
|
+
parallelTotal: {
|
|
572
|
+
env: "ARGOS_PARALLEL_TOTAL",
|
|
573
|
+
format: "int",
|
|
574
|
+
default: null,
|
|
575
|
+
nullable: true
|
|
576
|
+
},
|
|
577
|
+
referenceBranch: {
|
|
578
|
+
env: "ARGOS_REFERENCE_BRANCH",
|
|
579
|
+
format: String,
|
|
580
|
+
default: null,
|
|
581
|
+
nullable: true
|
|
582
|
+
},
|
|
583
|
+
referenceCommit: {
|
|
584
|
+
env: "ARGOS_REFERENCE_COMMIT",
|
|
585
|
+
format: String,
|
|
586
|
+
default: null,
|
|
587
|
+
nullable: true
|
|
588
|
+
},
|
|
589
|
+
jobId: {
|
|
590
|
+
format: String,
|
|
591
|
+
default: null,
|
|
592
|
+
nullable: true
|
|
593
|
+
},
|
|
594
|
+
runId: {
|
|
595
|
+
format: String,
|
|
596
|
+
default: null,
|
|
597
|
+
nullable: true
|
|
598
|
+
},
|
|
599
|
+
runAttempt: {
|
|
600
|
+
format: "nat",
|
|
601
|
+
default: null,
|
|
602
|
+
nullable: true
|
|
603
|
+
},
|
|
604
|
+
owner: {
|
|
605
|
+
format: String,
|
|
606
|
+
default: null,
|
|
607
|
+
nullable: true
|
|
608
|
+
},
|
|
609
|
+
repository: {
|
|
610
|
+
format: String,
|
|
611
|
+
default: null,
|
|
612
|
+
nullable: true
|
|
613
|
+
},
|
|
614
|
+
ciProvider: {
|
|
615
|
+
format: String,
|
|
616
|
+
default: null,
|
|
617
|
+
nullable: true
|
|
618
|
+
},
|
|
619
|
+
threshold: {
|
|
620
|
+
env: "ARGOS_THRESHOLD",
|
|
621
|
+
format: "float-percent",
|
|
622
|
+
default: null,
|
|
623
|
+
nullable: true
|
|
624
|
+
}
|
|
625
|
+
};
|
|
626
|
+
var createConfig = () => {
|
|
627
|
+
return convict(schema, {
|
|
628
|
+
args: []
|
|
629
|
+
});
|
|
630
|
+
};
|
|
631
|
+
async function readConfig(options = {}) {
|
|
632
|
+
const config = createConfig();
|
|
633
|
+
const ciEnv = await getCiEnvironment();
|
|
634
|
+
config.load({
|
|
635
|
+
apiBaseUrl: options.apiBaseUrl || config.get("apiBaseUrl"),
|
|
636
|
+
commit: options.commit || config.get("commit") || ciEnv?.commit || null,
|
|
637
|
+
branch: options.branch || config.get("branch") || ciEnv?.branch || null,
|
|
638
|
+
token: options.token || config.get("token") || null,
|
|
639
|
+
buildName: options.buildName || config.get("buildName") || null,
|
|
640
|
+
prNumber: options.prNumber || config.get("prNumber") || ciEnv?.prNumber || null,
|
|
641
|
+
prHeadCommit: config.get("prHeadCommit") || ciEnv?.prHeadCommit || null,
|
|
642
|
+
prBaseBranch: config.get("prBaseBranch") || ciEnv?.prBaseBranch || null,
|
|
643
|
+
referenceBranch: options.referenceBranch || config.get("referenceBranch") || null,
|
|
644
|
+
referenceCommit: options.referenceCommit || config.get("referenceCommit") || null,
|
|
645
|
+
owner: ciEnv?.owner || null,
|
|
646
|
+
repository: ciEnv?.repository || null,
|
|
647
|
+
jobId: ciEnv?.jobId || null,
|
|
648
|
+
runId: ciEnv?.runId || null,
|
|
649
|
+
runAttempt: ciEnv?.runAttempt || null,
|
|
650
|
+
parallel: options.parallel ?? config.get("parallel") ?? false,
|
|
651
|
+
parallelNonce: options.parallelNonce || config.get("parallelNonce") || ciEnv?.nonce || null,
|
|
652
|
+
parallelTotal: options.parallelTotal ?? config.get("parallelTotal") ?? null,
|
|
653
|
+
parallelIndex: options.parallelIndex ?? config.get("parallelIndex") ?? null,
|
|
654
|
+
mode: options.mode || config.get("mode") || null,
|
|
655
|
+
ciProvider: ciEnv?.key || null
|
|
656
|
+
});
|
|
657
|
+
config.validate();
|
|
658
|
+
return config.get();
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
// src/discovery.ts
|
|
662
|
+
import { resolve } from "node:path";
|
|
663
|
+
import glob from "fast-glob";
|
|
664
|
+
var discoverScreenshots = async (patterns, { root = process.cwd(), ignore } = {}) => {
|
|
665
|
+
debug(
|
|
666
|
+
`Discovering screenshots with patterns: ${Array.isArray(patterns) ? patterns.join(", ") : patterns} in ${root}`
|
|
667
|
+
);
|
|
668
|
+
const matches = await glob(patterns, { onlyFiles: true, ignore, cwd: root });
|
|
669
|
+
return matches.map((match) => {
|
|
670
|
+
debug(`Found screenshot: ${match}`);
|
|
671
|
+
const path = resolve(root, match);
|
|
672
|
+
return {
|
|
673
|
+
name: match,
|
|
674
|
+
path
|
|
675
|
+
};
|
|
676
|
+
});
|
|
677
|
+
};
|
|
678
|
+
|
|
679
|
+
// src/optimize.ts
|
|
680
|
+
import { promisify } from "node:util";
|
|
681
|
+
import sharp from "sharp";
|
|
682
|
+
import tmp from "tmp";
|
|
683
|
+
var tmpFile = promisify(tmp.file);
|
|
684
|
+
var optimizeScreenshot = async (filepath) => {
|
|
685
|
+
try {
|
|
686
|
+
const resultFilePath = await tmpFile();
|
|
687
|
+
await sharp(filepath).resize(2048, 64e3, {
|
|
688
|
+
fit: "inside",
|
|
689
|
+
withoutEnlargement: true
|
|
690
|
+
}).png({ force: true }).toFile(resultFilePath);
|
|
691
|
+
return resultFilePath;
|
|
692
|
+
} catch (error) {
|
|
693
|
+
const message = error instanceof Error ? error.message : "Unknown Error";
|
|
694
|
+
throw new Error(`Error while processing image (${filepath}): ${message}`, {
|
|
695
|
+
cause: error
|
|
696
|
+
});
|
|
697
|
+
}
|
|
698
|
+
};
|
|
699
|
+
|
|
700
|
+
// src/hashing.ts
|
|
701
|
+
import { createReadStream } from "node:fs";
|
|
702
|
+
import { createHash } from "node:crypto";
|
|
703
|
+
var hashFile = async (filepath) => {
|
|
704
|
+
const fileStream = createReadStream(filepath);
|
|
705
|
+
const hash = createHash("sha256");
|
|
706
|
+
await new Promise((resolve2, reject) => {
|
|
707
|
+
fileStream.on("error", reject);
|
|
708
|
+
hash.on("error", reject);
|
|
709
|
+
hash.on("finish", resolve2);
|
|
710
|
+
fileStream.pipe(hash);
|
|
711
|
+
});
|
|
712
|
+
return hash.digest("hex");
|
|
713
|
+
};
|
|
714
|
+
|
|
715
|
+
// src/auth.ts
|
|
716
|
+
var base64Encode = (obj) => Buffer.from(JSON.stringify(obj), "utf8").toString("base64");
|
|
717
|
+
function getAuthToken({
|
|
718
|
+
token,
|
|
719
|
+
ciProvider,
|
|
720
|
+
owner,
|
|
721
|
+
repository,
|
|
722
|
+
jobId,
|
|
723
|
+
runId,
|
|
724
|
+
prNumber
|
|
725
|
+
}) {
|
|
726
|
+
if (token) {
|
|
727
|
+
return token;
|
|
728
|
+
}
|
|
729
|
+
switch (ciProvider) {
|
|
730
|
+
case "github-actions": {
|
|
731
|
+
if (!owner || !repository || !jobId || !runId) {
|
|
732
|
+
throw new Error(
|
|
733
|
+
`Automatic GitHub Actions variables detection failed. Please add the 'ARGOS_TOKEN'`
|
|
734
|
+
);
|
|
735
|
+
}
|
|
736
|
+
return `tokenless-github-${base64Encode({
|
|
737
|
+
owner,
|
|
738
|
+
repository,
|
|
739
|
+
jobId,
|
|
740
|
+
runId,
|
|
741
|
+
prNumber
|
|
742
|
+
})}`;
|
|
743
|
+
}
|
|
744
|
+
default:
|
|
745
|
+
throw new Error("Missing Argos repository token 'ARGOS_TOKEN'");
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
// src/s3.ts
|
|
750
|
+
import { readFile } from "node:fs/promises";
|
|
751
|
+
import axios2 from "axios";
|
|
752
|
+
var upload = async (input) => {
|
|
753
|
+
const file = await readFile(input.path);
|
|
754
|
+
await axios2({
|
|
755
|
+
method: "PUT",
|
|
756
|
+
url: input.url,
|
|
757
|
+
data: file,
|
|
758
|
+
headers: {
|
|
759
|
+
"Content-Type": input.contentType
|
|
760
|
+
}
|
|
761
|
+
});
|
|
762
|
+
};
|
|
763
|
+
|
|
764
|
+
// src/util/chunk.ts
|
|
765
|
+
var chunk = (collection, size) => {
|
|
766
|
+
const result = [];
|
|
767
|
+
for (let x = 0; x < Math.ceil(collection.length / size); x++) {
|
|
768
|
+
let start = x * size;
|
|
769
|
+
let end = start + size;
|
|
770
|
+
result.push(collection.slice(start, end));
|
|
771
|
+
}
|
|
772
|
+
return result;
|
|
773
|
+
};
|
|
774
|
+
|
|
775
|
+
// src/upload.ts
|
|
776
|
+
import { getPlaywrightTracePath, readMetadata } from "@argos-ci/util";
|
|
777
|
+
|
|
778
|
+
// src/version.ts
|
|
779
|
+
import { readVersionFromPackage } from "@argos-ci/util";
|
|
780
|
+
import { createRequire } from "node:module";
|
|
781
|
+
var require2 = createRequire(import.meta.url);
|
|
782
|
+
async function getArgosCoreSDKIdentifier() {
|
|
783
|
+
const pkgPath = require2.resolve("@argos-ci/core/package.json");
|
|
784
|
+
const version = await readVersionFromPackage(pkgPath);
|
|
785
|
+
return `@argos-ci/core@${version}`;
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
// src/upload.ts
|
|
789
|
+
var CHUNK_SIZE = 10;
|
|
790
|
+
async function getConfigFromOptions({
|
|
791
|
+
parallel,
|
|
792
|
+
...options
|
|
793
|
+
}) {
|
|
794
|
+
return readConfig({
|
|
795
|
+
...options,
|
|
796
|
+
parallel: Boolean(parallel),
|
|
797
|
+
parallelNonce: parallel ? parallel.nonce : null,
|
|
798
|
+
parallelTotal: parallel ? parallel.total : null,
|
|
799
|
+
parallelIndex: parallel ? parallel.index : null
|
|
800
|
+
});
|
|
801
|
+
}
|
|
802
|
+
async function uploadFilesToS3(files) {
|
|
803
|
+
debug(`Split files in chunks of ${CHUNK_SIZE}`);
|
|
804
|
+
const chunks = chunk(files, CHUNK_SIZE);
|
|
805
|
+
debug(`Starting upload of ${chunks.length} chunks`);
|
|
806
|
+
for (let i = 0; i < chunks.length; i++) {
|
|
807
|
+
debug(`Uploading chunk ${i + 1}/${chunks.length}`);
|
|
808
|
+
const timeLabel = `Chunk ${i + 1}/${chunks.length}`;
|
|
809
|
+
debugTime(timeLabel);
|
|
810
|
+
const chunk2 = chunks[i];
|
|
811
|
+
if (!chunk2) {
|
|
812
|
+
throw new Error(`Invariant: chunk ${i} is empty`);
|
|
813
|
+
}
|
|
814
|
+
await Promise.all(
|
|
815
|
+
chunk2.map(async ({ url, path, contentType }) => {
|
|
816
|
+
await upload({
|
|
817
|
+
url,
|
|
818
|
+
path,
|
|
819
|
+
contentType
|
|
820
|
+
});
|
|
821
|
+
})
|
|
822
|
+
);
|
|
823
|
+
debugTimeEnd(timeLabel);
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
async function upload2(params) {
|
|
827
|
+
debug("Starting upload with params", params);
|
|
828
|
+
const [config, argosSdk] = await Promise.all([
|
|
829
|
+
getConfigFromOptions(params),
|
|
830
|
+
getArgosCoreSDKIdentifier()
|
|
831
|
+
]);
|
|
832
|
+
const files = params.files ?? ["**/*.{png,jpg,jpeg}"];
|
|
833
|
+
debug("Using config and files", config, files);
|
|
834
|
+
const authToken = getAuthToken(config);
|
|
835
|
+
const apiClient = createClient({
|
|
836
|
+
baseUrl: config.apiBaseUrl,
|
|
837
|
+
authToken
|
|
838
|
+
});
|
|
839
|
+
const foundScreenshots = await discoverScreenshots(files, {
|
|
840
|
+
root: params.root,
|
|
841
|
+
ignore: params.ignore
|
|
842
|
+
});
|
|
843
|
+
debug("Found screenshots", foundScreenshots);
|
|
844
|
+
const screenshots = await Promise.all(
|
|
845
|
+
foundScreenshots.map(async (screenshot) => {
|
|
846
|
+
const [metadata, pwTracePath, optimizedPath] = await Promise.all([
|
|
847
|
+
readMetadata(screenshot.path),
|
|
848
|
+
getPlaywrightTracePath(screenshot.path),
|
|
849
|
+
optimizeScreenshot(screenshot.path)
|
|
850
|
+
]);
|
|
851
|
+
const [hash, pwTraceHash] = await Promise.all([
|
|
852
|
+
hashFile(optimizedPath),
|
|
853
|
+
pwTracePath ? hashFile(pwTracePath) : null
|
|
854
|
+
]);
|
|
855
|
+
const threshold = metadata?.transient?.threshold ?? null;
|
|
856
|
+
const baseName = metadata?.transient?.baseName ?? null;
|
|
857
|
+
if (metadata) {
|
|
858
|
+
delete metadata.transient;
|
|
859
|
+
}
|
|
860
|
+
return {
|
|
861
|
+
...screenshot,
|
|
862
|
+
hash,
|
|
863
|
+
optimizedPath,
|
|
864
|
+
metadata,
|
|
865
|
+
threshold,
|
|
866
|
+
baseName,
|
|
867
|
+
pwTrace: pwTracePath && pwTraceHash ? { path: pwTracePath, hash: pwTraceHash } : null
|
|
868
|
+
};
|
|
869
|
+
})
|
|
870
|
+
);
|
|
871
|
+
debug("Fetch project");
|
|
872
|
+
const projectResponse = await apiClient.GET("/project");
|
|
873
|
+
if (projectResponse.error) {
|
|
874
|
+
throwAPIError(projectResponse.error);
|
|
875
|
+
}
|
|
876
|
+
debug("Project fetched", projectResponse.data);
|
|
877
|
+
const { defaultBaseBranch, hasRemoteContentAccess } = projectResponse.data;
|
|
878
|
+
const referenceCommit = (() => {
|
|
879
|
+
if (config.referenceCommit) {
|
|
880
|
+
debug("Found reference commit in config", config.referenceCommit);
|
|
881
|
+
return config.referenceCommit;
|
|
882
|
+
}
|
|
883
|
+
if (hasRemoteContentAccess) {
|
|
884
|
+
return null;
|
|
885
|
+
}
|
|
886
|
+
const base = config.referenceBranch || config.prBaseBranch || defaultBaseBranch;
|
|
887
|
+
const sha = getMergeBaseCommitSha2({ base, head: config.branch });
|
|
888
|
+
if (sha) {
|
|
889
|
+
debug("Found merge base", sha);
|
|
890
|
+
} else {
|
|
891
|
+
debug("No merge base found");
|
|
892
|
+
}
|
|
893
|
+
return sha;
|
|
894
|
+
})();
|
|
895
|
+
const parentCommits = (() => {
|
|
896
|
+
if (hasRemoteContentAccess) {
|
|
897
|
+
return null;
|
|
898
|
+
}
|
|
899
|
+
if (referenceCommit) {
|
|
900
|
+
const commits = listParentCommits2({ sha: referenceCommit });
|
|
901
|
+
if (commits) {
|
|
902
|
+
debug("Found parent commits", commits);
|
|
903
|
+
} else {
|
|
904
|
+
debug("No parent commits found");
|
|
905
|
+
}
|
|
906
|
+
return commits;
|
|
907
|
+
}
|
|
908
|
+
return null;
|
|
909
|
+
})();
|
|
910
|
+
debug("Creating build");
|
|
911
|
+
const [pwTraceKeys, screenshotKeys] = screenshots.reduce(
|
|
912
|
+
([pwTraceKeys2, screenshotKeys2], screenshot) => {
|
|
913
|
+
if (screenshot.pwTrace && !pwTraceKeys2.includes(screenshot.pwTrace.hash)) {
|
|
914
|
+
pwTraceKeys2.push(screenshot.pwTrace.hash);
|
|
915
|
+
}
|
|
916
|
+
if (!screenshotKeys2.includes(screenshot.hash)) {
|
|
917
|
+
screenshotKeys2.push(screenshot.hash);
|
|
918
|
+
}
|
|
919
|
+
return [pwTraceKeys2, screenshotKeys2];
|
|
920
|
+
},
|
|
921
|
+
[[], []]
|
|
922
|
+
);
|
|
923
|
+
const createBuildResponse = await apiClient.POST("/builds", {
|
|
924
|
+
body: {
|
|
925
|
+
commit: config.commit,
|
|
926
|
+
branch: config.branch,
|
|
927
|
+
name: config.buildName,
|
|
928
|
+
mode: config.mode,
|
|
929
|
+
parallel: config.parallel,
|
|
930
|
+
parallelNonce: config.parallelNonce,
|
|
931
|
+
screenshotKeys,
|
|
932
|
+
pwTraceKeys,
|
|
933
|
+
prNumber: config.prNumber,
|
|
934
|
+
prHeadCommit: config.prHeadCommit,
|
|
935
|
+
referenceBranch: config.referenceBranch,
|
|
936
|
+
referenceCommit,
|
|
937
|
+
parentCommits,
|
|
938
|
+
argosSdk,
|
|
939
|
+
ciProvider: config.ciProvider,
|
|
940
|
+
runId: config.runId,
|
|
941
|
+
runAttempt: config.runAttempt
|
|
942
|
+
}
|
|
943
|
+
});
|
|
944
|
+
if (createBuildResponse.error) {
|
|
945
|
+
throwAPIError(createBuildResponse.error);
|
|
946
|
+
}
|
|
947
|
+
const result = createBuildResponse.data;
|
|
948
|
+
debug("Got uploads url", result);
|
|
949
|
+
const uploadFiles = [
|
|
950
|
+
...result.screenshots.map(({ key, putUrl }) => {
|
|
951
|
+
const screenshot = screenshots.find((s) => s.hash === key);
|
|
952
|
+
if (!screenshot) {
|
|
953
|
+
throw new Error(`Invariant: screenshot with hash ${key} not found`);
|
|
954
|
+
}
|
|
955
|
+
return {
|
|
956
|
+
url: putUrl,
|
|
957
|
+
path: screenshot.optimizedPath,
|
|
958
|
+
contentType: "image/png"
|
|
959
|
+
};
|
|
960
|
+
}),
|
|
961
|
+
...result.pwTraces?.map(({ key, putUrl }) => {
|
|
962
|
+
const screenshot = screenshots.find(
|
|
963
|
+
(s) => s.pwTrace && s.pwTrace.hash === key
|
|
964
|
+
);
|
|
965
|
+
if (!screenshot || !screenshot.pwTrace) {
|
|
966
|
+
throw new Error(`Invariant: trace with ${key} not found`);
|
|
967
|
+
}
|
|
968
|
+
return {
|
|
969
|
+
url: putUrl,
|
|
970
|
+
path: screenshot.pwTrace.path,
|
|
971
|
+
contentType: "application/json"
|
|
972
|
+
};
|
|
973
|
+
}) ?? []
|
|
974
|
+
];
|
|
975
|
+
await uploadFilesToS3(uploadFiles);
|
|
976
|
+
debug("Updating build");
|
|
977
|
+
const uploadBuildResponse = await apiClient.PUT("/builds/{buildId}", {
|
|
978
|
+
params: {
|
|
979
|
+
path: {
|
|
980
|
+
buildId: result.build.id
|
|
981
|
+
}
|
|
982
|
+
},
|
|
983
|
+
body: {
|
|
984
|
+
screenshots: screenshots.map((screenshot) => ({
|
|
985
|
+
key: screenshot.hash,
|
|
986
|
+
name: screenshot.name,
|
|
987
|
+
metadata: screenshot.metadata,
|
|
988
|
+
pwTraceKey: screenshot.pwTrace?.hash ?? null,
|
|
989
|
+
threshold: screenshot.threshold ?? config?.threshold ?? null,
|
|
990
|
+
baseName: screenshot.baseName
|
|
991
|
+
})),
|
|
992
|
+
parallel: config.parallel,
|
|
993
|
+
parallelTotal: config.parallelTotal,
|
|
994
|
+
parallelIndex: config.parallelIndex,
|
|
995
|
+
metadata: params.metadata
|
|
996
|
+
}
|
|
997
|
+
});
|
|
998
|
+
if (uploadBuildResponse.error) {
|
|
999
|
+
throwAPIError(uploadBuildResponse.error);
|
|
1000
|
+
}
|
|
1001
|
+
return { build: uploadBuildResponse.data.build, screenshots };
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
// src/finalize.ts
|
|
1005
|
+
import { createClient as createClient2, throwAPIError as throwAPIError2 } from "@argos-ci/api-client";
|
|
1006
|
+
async function finalize(params) {
|
|
1007
|
+
const config = await readConfig({
|
|
1008
|
+
parallelNonce: params.parallel?.nonce ?? null
|
|
1009
|
+
});
|
|
1010
|
+
const authToken = getAuthToken(config);
|
|
1011
|
+
const apiClient = createClient2({
|
|
1012
|
+
baseUrl: config.apiBaseUrl,
|
|
1013
|
+
authToken
|
|
1014
|
+
});
|
|
1015
|
+
if (!config.parallelNonce) {
|
|
1016
|
+
throw new Error("parallel.nonce is required to finalize the build");
|
|
1017
|
+
}
|
|
1018
|
+
const finalizeBuildsResult = await apiClient.POST("/builds/finalize", {
|
|
1019
|
+
body: {
|
|
1020
|
+
parallelNonce: config.parallelNonce
|
|
1021
|
+
}
|
|
1022
|
+
});
|
|
1023
|
+
if (finalizeBuildsResult.error) {
|
|
1024
|
+
throwAPIError2(finalizeBuildsResult.error);
|
|
1025
|
+
}
|
|
1026
|
+
return finalizeBuildsResult.data;
|
|
1027
|
+
}
|
|
1028
|
+
export {
|
|
1029
|
+
finalize,
|
|
1030
|
+
readConfig,
|
|
1031
|
+
upload2 as upload
|
|
1032
|
+
};
|