@getexception/cli 0.1.13 → 0.1.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/index.js +42 -13
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -26,9 +26,9 @@ Permanent upload tokens cannot be created or used for projects with GitLab authe
|
|
|
26
26
|
|
|
27
27
|
Deploy the resulting `dist` unchanged. Upload failure does not undo preparation: retry within the same job using the same prepared directory. **Do not save source maps as GitLab artifacts or cache when access has not been verified.** The GitLab identity flow keeps them only in the isolated job workspace and uploads directly to GetException. A retry after that job ends requires a new build with a new job prefix; it cannot replace maps for the old build. Do not run `prepare` twice or rebuild only the maps. Never publish the private artifact directory alongside the application. Other map formats (for example CSS maps) should also be excluded from the public deploy artifact by your build configuration.
|
|
28
28
|
|
|
29
|
-
`upload` sends an authenticated manifest, uploads files with checksums, then waits for background validation. It
|
|
29
|
+
`upload` sends an authenticated manifest, gzip-compresses each map in memory, uploads up to eight files concurrently with checksums, then waits for background validation. It retries bounded transient network/server failures, resumes the same manifest from its missing files, has a twenty-minute deadline per batch, and exits nonzero on failure. Neither production nor preview deployment depends on GetException availability. Deploy the safe prepared JavaScript first, then attempt map delivery from the same isolated job workspace. The CI wrapper catches monitoring failures and emits a prominent production warning or a single neutral preview log line. Only report successful map delivery when all batches are ready. A failed local prepare requires one clean rebuild without maps; a late successful upload automatically reprocesses existing errors. Do not retain maps in GitLab artifacts as a fallback. Raw maps have no download endpoint.
|
|
30
30
|
|
|
31
|
-
Limits: 128 JS files and 128 MiB per upload, 16 MiB per map, 1 GiB per project, 10 GiB per installation. Non-indexed Source Map v3 JSON only; no archives
|
|
31
|
+
Limits: 128 JS files and 128 MiB per upload, 16 MiB per map, 1 GiB per project, 10 GiB per installation. Non-indexed Source Map v3 JSON only; no archives or remote source downloads. Gzip is used only as bounded HTTPS transfer encoding and does not change the stored map format. Existing events are processed after a late upload. Errors thrown in the browser console have no source file and cannot gain a source snippet from a map.
|
|
32
32
|
|
|
33
33
|
MIT. See `THIRD-PARTY-NOTICES.md`.
|
|
34
34
|
|
|
@@ -46,6 +46,6 @@ With GitLab build authentication, use the exact `release` and `deployment` retur
|
|
|
46
46
|
|
|
47
47
|
Debug IDs are deterministic for identical JS, maps and paths, regardless of the release SHA. The server reuses private bytes within the project and `upload` skips files it already has. Every release still sends its own manifest. Changed JavaScript requires its matching map: there is no fallback to the latest production map. Ready maps older than 30 days are eligible for cleanup only when their release has no retained events or pending inbox events. Shared files survive until all references expire.
|
|
48
48
|
|
|
49
|
-
CI wrappers must
|
|
49
|
+
CI wrappers must keep `ci context` and release registration on short timeouts. Run map delivery only after the application is deployed and give each upload subprocess a hard timeout slightly longer than the CLI's twenty-minute deadline. Do not impose one small shared timeout on all batches: that makes valid larger builds fail based on transfer time. See the [failure handling contract](../../docs/gitlab-ci.md#обработка-сбоев-в-account). Do not mark the whole build job as allowed to fail. Compiler, deployment and public-output safety failures remain real job failures.
|
|
50
50
|
|
|
51
51
|
On failure the CLI exits 1 and prints one `GETEXCEPTION_DIAGNOSTIC` JSON line to stderr with `version:1`, an allowlisted `code`, optional `httpStatus` and optional server UUID `requestId`. Network, response-contract and CI identity failures have separate codes. It never prints raw errors, tokens, headers or response bodies. Callers must parse and validate this diagnostic, not forward arbitrary stderr. Successful `ci context` stdout remains JSON only. Older CLI versions without this format can be handled with a generic safe failure code without blocking application deployment.
|
package/dist/index.js
CHANGED
|
@@ -19217,7 +19217,9 @@ async function readPreparedMap(directory, entry) {
|
|
|
19217
19217
|
}
|
|
19218
19218
|
|
|
19219
19219
|
// packages/cli/src/upload.ts
|
|
19220
|
+
import { promisify } from "util";
|
|
19220
19221
|
import { setTimeout as delay2 } from "timers/promises";
|
|
19222
|
+
import { constants, gzip } from "zlib";
|
|
19221
19223
|
|
|
19222
19224
|
// packages/cli/src/api.ts
|
|
19223
19225
|
import { setTimeout as delay } from "timers/promises";
|
|
@@ -19378,7 +19380,15 @@ async function httpError(response) {
|
|
|
19378
19380
|
}
|
|
19379
19381
|
|
|
19380
19382
|
// packages/cli/src/api.ts
|
|
19381
|
-
function
|
|
19383
|
+
function retryableNetworkError(error61) {
|
|
19384
|
+
return [
|
|
19385
|
+
"NETWORK_DNS",
|
|
19386
|
+
"NETWORK_CONNECTION",
|
|
19387
|
+
"NETWORK_TIMEOUT",
|
|
19388
|
+
"NETWORK_ERROR"
|
|
19389
|
+
].includes(error61.code);
|
|
19390
|
+
}
|
|
19391
|
+
function projectApi(address, project, token, transport = fetch, options = {}) {
|
|
19382
19392
|
let url2;
|
|
19383
19393
|
try {
|
|
19384
19394
|
url2 = new URL(address);
|
|
@@ -19393,8 +19403,9 @@ function projectApi(address, project, token, transport = fetch) {
|
|
|
19393
19403
|
}
|
|
19394
19404
|
const authorization = authorizationHeader(token);
|
|
19395
19405
|
const base = `${url2.origin}/api/v1/projects/${project}`;
|
|
19396
|
-
const deadline = Date.now() + 12e4;
|
|
19397
|
-
|
|
19406
|
+
const deadline = Date.now() + (options.deadlineMs ?? 12e4);
|
|
19407
|
+
const requestTimeout = options.requestTimeoutMs ?? 3e4;
|
|
19408
|
+
return async function request(path, method = "GET", body, contentEncoding) {
|
|
19398
19409
|
for (let attempt = 0; attempt < 3; attempt++) {
|
|
19399
19410
|
if (Date.now() >= deadline) {
|
|
19400
19411
|
throw new CliError("NETWORK_TIMEOUT");
|
|
@@ -19405,15 +19416,23 @@ function projectApi(address, project, token, transport = fetch) {
|
|
|
19405
19416
|
method,
|
|
19406
19417
|
headers: {
|
|
19407
19418
|
Authorization: authorization,
|
|
19408
|
-
"Content-Type": "application/json"
|
|
19419
|
+
"Content-Type": "application/json",
|
|
19420
|
+
...contentEncoding ? { "Content-Encoding": contentEncoding } : {}
|
|
19409
19421
|
},
|
|
19410
19422
|
body,
|
|
19411
19423
|
redirect: "error",
|
|
19412
19424
|
credentials: "omit",
|
|
19413
|
-
signal: AbortSignal.timeout(
|
|
19425
|
+
signal: AbortSignal.timeout(
|
|
19426
|
+
Math.min(requestTimeout, deadline - Date.now())
|
|
19427
|
+
)
|
|
19414
19428
|
});
|
|
19415
19429
|
} catch (error61) {
|
|
19416
|
-
|
|
19430
|
+
const failure = networkError(error61);
|
|
19431
|
+
if (retryableNetworkError(failure) && attempt < 2) {
|
|
19432
|
+
await delay(1e3 * 2 ** attempt);
|
|
19433
|
+
continue;
|
|
19434
|
+
}
|
|
19435
|
+
throw failure;
|
|
19417
19436
|
}
|
|
19418
19437
|
if (response.ok) {
|
|
19419
19438
|
return responseJson(response);
|
|
@@ -19442,10 +19461,15 @@ var receiptSchema = external_exports.object({
|
|
|
19442
19461
|
)
|
|
19443
19462
|
});
|
|
19444
19463
|
var uploadConcurrency = 8;
|
|
19464
|
+
var uploadDeadlineMs = 20 * 6e4;
|
|
19465
|
+
var gzipAsync = promisify(gzip);
|
|
19445
19466
|
async function uploadMaps(directory, address, project, token, transport = fetch) {
|
|
19446
19467
|
const manifest = await readPrepared(directory);
|
|
19447
|
-
const api = projectApi(address, project, token, transport
|
|
19448
|
-
|
|
19468
|
+
const api = projectApi(address, project, token, transport, {
|
|
19469
|
+
deadlineMs: uploadDeadlineMs,
|
|
19470
|
+
requestTimeoutMs: 6e4
|
|
19471
|
+
});
|
|
19472
|
+
const request = (path, method = "GET", body, contentEncoding) => api("/source-maps" + path, method, body, contentEncoding);
|
|
19449
19473
|
const receipt = receiptSchema.parse(
|
|
19450
19474
|
await request("", "POST", JSON.stringify(manifest))
|
|
19451
19475
|
);
|
|
@@ -19467,13 +19491,18 @@ async function uploadMaps(directory, address, project, token, transport = fetch)
|
|
|
19467
19491
|
for (let start = 0; start < pending.length; start += uploadConcurrency) {
|
|
19468
19492
|
const group = pending.slice(start, start + uploadConcurrency);
|
|
19469
19493
|
await Promise.all(
|
|
19470
|
-
group.map(
|
|
19471
|
-
|
|
19494
|
+
group.map(async ({ entry, id }) => {
|
|
19495
|
+
const bytes = await readPreparedMap(directory, entry);
|
|
19496
|
+
const compressed = await gzipAsync(bytes, {
|
|
19497
|
+
level: constants.Z_BEST_SPEED
|
|
19498
|
+
});
|
|
19499
|
+
return request(
|
|
19472
19500
|
`/${receipt.uploadId}/${id}`,
|
|
19473
19501
|
"PUT",
|
|
19474
|
-
|
|
19475
|
-
|
|
19476
|
-
|
|
19502
|
+
compressed,
|
|
19503
|
+
"gzip"
|
|
19504
|
+
);
|
|
19505
|
+
})
|
|
19477
19506
|
);
|
|
19478
19507
|
}
|
|
19479
19508
|
await request(`/${receipt.uploadId}`, "POST");
|