@sentinelqa/uploader 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +98 -69
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -190,6 +190,26 @@ const extractTestsFromReport = (reportJson) => {
|
|
|
190
190
|
const computeRunStatus = (tests) => {
|
|
191
191
|
return tests.some((t) => t.status === "failed") ? "failed" : "passed";
|
|
192
192
|
};
|
|
193
|
+
const bestEffortComplete = async (appUrl, ingestToken, runId, status, tests) => {
|
|
194
|
+
try {
|
|
195
|
+
await fetch(`${appUrl}/api/runs/${runId}/complete`, {
|
|
196
|
+
method: "POST",
|
|
197
|
+
headers: {
|
|
198
|
+
"content-type": "application/json",
|
|
199
|
+
authorization: `Bearer ${ingestToken}`
|
|
200
|
+
},
|
|
201
|
+
body: JSON.stringify({
|
|
202
|
+
status,
|
|
203
|
+
finishedAt: new Date().toISOString(),
|
|
204
|
+
tests,
|
|
205
|
+
artifacts: []
|
|
206
|
+
})
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
// Ignore best-effort failures; original error will be surfaced.
|
|
211
|
+
}
|
|
212
|
+
};
|
|
193
213
|
const detectGitLabEnv = () => {
|
|
194
214
|
const isGitLab = readEnv("GITLAB_CI") === "true" || !!readEnv("CI_PROJECT_ID");
|
|
195
215
|
if (!isGitLab)
|
|
@@ -374,77 +394,86 @@ const main = async () => {
|
|
|
374
394
|
return { ...artifact, sizeBytes: stat.size, contentType };
|
|
375
395
|
});
|
|
376
396
|
let finalArtifacts = artifactsWithMeta;
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
headers: {
|
|
404
|
-
"content-type": "application/json",
|
|
405
|
-
authorization: `Bearer ${ingestToken}`
|
|
406
|
-
},
|
|
407
|
-
body: JSON.stringify({
|
|
408
|
-
items: finalArtifacts.map((artifact) => ({
|
|
409
|
-
relPath: artifact.objectKey,
|
|
410
|
-
contentType: artifact.contentType,
|
|
411
|
-
sizeBytes: artifact.sizeBytes,
|
|
412
|
-
kind: artifact.type
|
|
413
|
-
}))
|
|
414
|
-
})
|
|
415
|
-
}), "POST /api/uploads/presign");
|
|
416
|
-
if (!presignRes.ok) {
|
|
417
|
-
const body = await presignRes.text();
|
|
418
|
-
fail(`POST /api/uploads/presign failed (${presignRes.status}): ${body}`);
|
|
397
|
+
try {
|
|
398
|
+
if (byoBucket) {
|
|
399
|
+
const s3Region = readEnv("SENTINEL_S3_REGION") ||
|
|
400
|
+
readEnv("AWS_REGION") ||
|
|
401
|
+
readEnv("S3_REGION");
|
|
402
|
+
if (!s3Region)
|
|
403
|
+
fail("SENTINEL_S3_REGION is required for BYO S3.");
|
|
404
|
+
const s3Endpoint = readEnv("SENTINEL_S3_ENDPOINT") || readEnv("S3_ENDPOINT");
|
|
405
|
+
const accessKeyId = readEnv("SENTINEL_S3_ACCESS_KEY_ID") || readEnv("AWS_ACCESS_KEY_ID");
|
|
406
|
+
const secretAccessKey = readEnv("SENTINEL_S3_SECRET_ACCESS_KEY") ||
|
|
407
|
+
readEnv("AWS_SECRET_ACCESS_KEY");
|
|
408
|
+
if (!accessKeyId || !secretAccessKey) {
|
|
409
|
+
fail("SENTINEL_S3_ACCESS_KEY_ID and SENTINEL_S3_SECRET_ACCESS_KEY are required.");
|
|
410
|
+
}
|
|
411
|
+
const s3Client = new client_s3_1.S3Client({
|
|
412
|
+
region: s3Region,
|
|
413
|
+
...(s3Endpoint ? { endpoint: s3Endpoint } : {}),
|
|
414
|
+
credentials: { accessKeyId, secretAccessKey }
|
|
415
|
+
});
|
|
416
|
+
await withRetry(async () => {
|
|
417
|
+
await uploadArtifacts(s3Client, byoBucket, finalArtifacts);
|
|
418
|
+
}, "upload artifacts");
|
|
419
|
+
finalArtifacts = finalArtifacts.map((artifact) => ({
|
|
420
|
+
...artifact,
|
|
421
|
+
bucket: byoBucket
|
|
422
|
+
}));
|
|
419
423
|
}
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
}
|
|
440
|
-
}, `upload ${artifact.objectKey}`);
|
|
441
|
-
artifact.objectKey = item.objectKey;
|
|
424
|
+
else {
|
|
425
|
+
const presignRes = await withRetry(() => fetch(`${appUrl}/api/uploads/presign`, {
|
|
426
|
+
method: "POST",
|
|
427
|
+
headers: {
|
|
428
|
+
"content-type": "application/json",
|
|
429
|
+
authorization: `Bearer ${ingestToken}`
|
|
430
|
+
},
|
|
431
|
+
body: JSON.stringify({
|
|
432
|
+
items: finalArtifacts.map((artifact) => ({
|
|
433
|
+
relPath: artifact.objectKey,
|
|
434
|
+
contentType: artifact.contentType,
|
|
435
|
+
sizeBytes: artifact.sizeBytes,
|
|
436
|
+
kind: artifact.type
|
|
437
|
+
}))
|
|
438
|
+
})
|
|
439
|
+
}), "POST /api/uploads/presign");
|
|
440
|
+
if (!presignRes.ok) {
|
|
441
|
+
const body = await presignRes.text();
|
|
442
|
+
fail(`POST /api/uploads/presign failed (${presignRes.status}): ${body}`);
|
|
442
443
|
}
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
444
|
+
const presignData = await presignRes.json();
|
|
445
|
+
const uploadMap = new Map((presignData.items || []).map((item) => [item.relPath, item]));
|
|
446
|
+
await withRetry(async () => {
|
|
447
|
+
for (const artifact of finalArtifacts) {
|
|
448
|
+
const item = uploadMap.get(artifact.objectKey);
|
|
449
|
+
if (!item?.uploadUrl || !item?.objectKey) {
|
|
450
|
+
throw new Error(`Missing upload URL for ${artifact.objectKey}`);
|
|
451
|
+
}
|
|
452
|
+
await withRetry(async () => {
|
|
453
|
+
const body = fs_1.default.createReadStream(artifact.filePath);
|
|
454
|
+
const res = await fetch(item.uploadUrl, {
|
|
455
|
+
method: "PUT",
|
|
456
|
+
headers: {
|
|
457
|
+
"content-type": artifact.contentType
|
|
458
|
+
},
|
|
459
|
+
body
|
|
460
|
+
});
|
|
461
|
+
if (!res.ok) {
|
|
462
|
+
throw new Error(`Upload failed (${res.status}) for ${artifact.objectKey}`);
|
|
463
|
+
}
|
|
464
|
+
}, `upload ${artifact.objectKey}`);
|
|
465
|
+
artifact.objectKey = item.objectKey;
|
|
466
|
+
}
|
|
467
|
+
}, "upload artifacts");
|
|
468
|
+
finalArtifacts = finalArtifacts.map((artifact) => ({
|
|
469
|
+
...artifact,
|
|
470
|
+
bucket: presignData.bucket
|
|
471
|
+
}));
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
catch (err) {
|
|
475
|
+
await bestEffortComplete(appUrl, ingestToken, runId, "failed", tests);
|
|
476
|
+
fail(err?.message || String(err));
|
|
448
477
|
}
|
|
449
478
|
const uploadDurationMs = Date.now() - uploadStart;
|
|
450
479
|
const completeRes = await withRetry(() => fetch(`${appUrl}/api/runs/${runId}/complete`, {
|