@plune-ai/cli 0.24.0 → 0.26.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/README.md +78 -7
- package/dist/cli.cjs +501 -61
- package/dist/reporter-core.cjs +126 -12
- package/dist/reporter-core.d.cts +22 -3
- package/dist/reporter-core.d.ts +22 -3
- package/dist/reporter-core.js +128 -14
- package/package.json +1 -1
package/dist/reporter-core.cjs
CHANGED
|
@@ -95,12 +95,14 @@ function parseRetryAfter(raw, now = Date.now()) {
|
|
|
95
95
|
if (!Number.isFinite(seconds)) return null;
|
|
96
96
|
return Math.min(Math.max(seconds, 0) * 1e3, RETRY_AFTER_CAP_MS);
|
|
97
97
|
}
|
|
98
|
+
var POOLS = { "http:": new import_node_http.Agent({ keepAlive: true }), "https:": new import_node_https.Agent({ keepAlive: true }) };
|
|
98
99
|
var IDLE_MS = 3e5;
|
|
99
100
|
function nodeTransport(url, init, idleMs = IDLE_MS) {
|
|
100
101
|
return new Promise((resolve2, reject) => {
|
|
101
102
|
const target = new URL(url);
|
|
102
103
|
const send = target.protocol === "https:" ? import_node_https.request : import_node_http.request;
|
|
103
|
-
const
|
|
104
|
+
const agent = POOLS[target.protocol === "https:" ? "https:" : "http:"];
|
|
105
|
+
const req = send(target, { method: init.method, headers: init.headers, agent }, (res) => {
|
|
104
106
|
const chunks = [];
|
|
105
107
|
res.on("data", (chunk2) => chunks.push(chunk2));
|
|
106
108
|
res.on("error", reject);
|
|
@@ -161,14 +163,25 @@ var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
|
161
163
|
async function detailOf(res) {
|
|
162
164
|
try {
|
|
163
165
|
const body = await res.json();
|
|
166
|
+
const q = body.quota;
|
|
164
167
|
return {
|
|
168
|
+
...typeof body.shardsExpected === "number" && Number.isInteger(body.shardsExpected) ? { shardsExpected: body.shardsExpected } : {},
|
|
165
169
|
detail: typeof body.error === "string" ? body.error : "",
|
|
170
|
+
...typeof q === "object" && q !== null && !Array.isArray(q) ? { quota: q } : {},
|
|
166
171
|
...typeof body.code === "string" && /^[a-z0-9_]{1,60}$/.test(body.code) ? { code: body.code } : {}
|
|
167
172
|
};
|
|
168
173
|
} catch {
|
|
169
174
|
return { detail: "" };
|
|
170
175
|
}
|
|
171
176
|
}
|
|
177
|
+
function describeQuota(q) {
|
|
178
|
+
const num = (v) => typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
179
|
+
const kind = typeof q["kind"] === "string" ? q["kind"].slice(0, 60) : "limit";
|
|
180
|
+
const [used, limit] = [num(q["used"]), num(q["limit"])];
|
|
181
|
+
const parts = [used !== void 0 && limit !== void 0 ? `${kind}: ${used} of ${limit} used` : kind];
|
|
182
|
+
if (typeof q["resetsAt"] === "string" && q["resetsAt"] !== "") parts.push(`resets ${q["resetsAt"].slice(0, 40)}`);
|
|
183
|
+
return parts.join(", ");
|
|
184
|
+
}
|
|
172
185
|
function classify(status) {
|
|
173
186
|
if (status === 401 || status === 403) return "auth";
|
|
174
187
|
if (status === 409) return "conflict";
|
|
@@ -233,12 +246,12 @@ function createClient(opts) {
|
|
|
233
246
|
}
|
|
234
247
|
const said = await detailOf(res);
|
|
235
248
|
const kind = classify(res.status);
|
|
236
|
-
last = { kind, status: res.status, detail: scrub(said.detail), ...said.code !== void 0 ? { code: said.code } : {} };
|
|
249
|
+
last = { kind, status: res.status, detail: scrub(said.detail), ...said.code !== void 0 ? { code: said.code } : {}, ...said.shardsExpected !== void 0 ? { shardsExpected: said.shardsExpected } : {} };
|
|
237
250
|
if (kind !== "unavailable") break;
|
|
238
251
|
const said429 = parseRetryAfter(res.headers.get("retry-after"), now());
|
|
239
|
-
if (res.status === 429 && said429 === null) {
|
|
252
|
+
if (res.status === 429 && (said.quota !== void 0 || said429 === null)) {
|
|
240
253
|
last.kind = "invalid";
|
|
241
|
-
last.detail =
|
|
254
|
+
last.detail = `quota exceeded (${said.quota === void 0 ? "no Retry-After" : scrub(describeQuota(said.quota))})`;
|
|
242
255
|
if (said.detail) last.detail += ` \u2014 ${scrub(said.detail)}`;
|
|
243
256
|
break;
|
|
244
257
|
}
|
|
@@ -251,6 +264,7 @@ function createClient(opts) {
|
|
|
251
264
|
post: (path3, body, retries) => send("POST", path3, { body: JSON.stringify(body), type: "application/json" }, retries),
|
|
252
265
|
upload: (path3, bytes, contentType) => send("POST", path3, { body: bytes, type: contentType }, uploadRetries ?? DEFAULT_RETRIES),
|
|
253
266
|
del: (path3) => send("DELETE", path3),
|
|
267
|
+
get: (path3) => send("GET", path3),
|
|
254
268
|
async put(url, headers, source) {
|
|
255
269
|
const putRetries = uploadRetries ?? 3;
|
|
256
270
|
let last = { ok: false, status: null, code: "network" };
|
|
@@ -289,7 +303,18 @@ function createClient(opts) {
|
|
|
289
303
|
// src/reporter-core/env.ts
|
|
290
304
|
var path = __toESM(require("path"), 1);
|
|
291
305
|
var UNSUPPORTED_VARS = ["PLUNE_GROUP"];
|
|
292
|
-
var
|
|
306
|
+
var SHARDS_MAX = 1e3;
|
|
307
|
+
function shardOf(env) {
|
|
308
|
+
const raw = value(env, "PLUNE_SHARD");
|
|
309
|
+
if (raw === void 0) return {};
|
|
310
|
+
const m = /^(\d{1,4})\/(\d{1,4})$/.exec(raw);
|
|
311
|
+
const [k, n] = m ? [Number(m[1]), Number(m[2])] : [0, 0];
|
|
312
|
+
if (k >= 1 && k <= n && n <= SHARDS_MAX) return { shard: { index: k, total: n } };
|
|
313
|
+
return {
|
|
314
|
+
error: `PLUNE_SHARD=${JSON.stringify(raw.slice(0, 40))} is not "k/N" with 1 <= k <= N <= ${SHARDS_MAX} (for example 2/4) - nothing was sent.`
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
var UPLOAD_CONCURRENCY = { min: 1, max: 32, default: 16 };
|
|
293
318
|
var UPLOAD_RETRIES = { min: 0, max: 20, default: 6 };
|
|
294
319
|
function value(env, name) {
|
|
295
320
|
const raw = env[name]?.trim();
|
|
@@ -333,6 +358,7 @@ function readEnv(env = process.env) {
|
|
|
333
358
|
const labels = list(env, "PLUNE_LABELS");
|
|
334
359
|
const offerDiscovered = flag(env, "PLUNE_CREATE");
|
|
335
360
|
const full = flag(env, "PLUNE_FULL_RUN");
|
|
361
|
+
const { shard, error: shardError } = shardOf(env);
|
|
336
362
|
return {
|
|
337
363
|
config: {
|
|
338
364
|
...externalKey !== void 0 ? { externalKey } : {},
|
|
@@ -353,7 +379,11 @@ function readEnv(env = process.env) {
|
|
|
353
379
|
// Two names for two situations that end the same way. `SHARED_RUN` says other processes are
|
|
354
380
|
// reporting into this run; `PROCEED` says the job will close it on its own schedule. Either
|
|
355
381
|
// way this process must not be the one to call it finished.
|
|
356
|
-
|
|
382
|
+
// `PLUNE_SHARD` implies it: the run is joined by the shared key and this process does not close it
|
|
383
|
+
// by itself; `leaveOpen` reports the shard instead.
|
|
384
|
+
keepOpen: flag(env, "PLUNE_SHARED_RUN") || flag(env, "PLUNE_PROCEED") || shard !== void 0 || shardError !== void 0,
|
|
385
|
+
...shard !== void 0 ? { shard } : {},
|
|
386
|
+
...shardError !== void 0 ? { shardError } : {},
|
|
357
387
|
ignored: UNSUPPORTED_VARS.filter((name) => value(env, name) !== void 0),
|
|
358
388
|
warnings
|
|
359
389
|
};
|
|
@@ -563,6 +593,7 @@ var RESOLVE_CHUNK = 500;
|
|
|
563
593
|
var BATCH_MAX = 500;
|
|
564
594
|
var DISCOVER_MAX = 500;
|
|
565
595
|
var BATCH_DEFAULT = 500;
|
|
596
|
+
var SEND_CONCURRENCY = 4;
|
|
566
597
|
var EXPECTED_MAX = 1e4;
|
|
567
598
|
var KEY_MAX = 1024;
|
|
568
599
|
var TITLE_MAX = 300;
|
|
@@ -700,6 +731,7 @@ function warnIfDropped(sent, run, log) {
|
|
|
700
731
|
}
|
|
701
732
|
async function startRun(passed, declared) {
|
|
702
733
|
const env = readEnv();
|
|
734
|
+
if (env.shardError !== void 0) throw new Error(env.shardError);
|
|
703
735
|
const stated = Object.fromEntries(Object.entries(passed).filter(([, v]) => v !== void 0));
|
|
704
736
|
const cfg = { ...env.config, ...stated };
|
|
705
737
|
const log = cfg.log ?? ((line) => void process.stderr.write(`${line}
|
|
@@ -741,6 +773,9 @@ async function startRun(passed, declared) {
|
|
|
741
773
|
const resolved = /* @__PURE__ */ new Map();
|
|
742
774
|
let buffer = [];
|
|
743
775
|
const discoveries = /* @__PURE__ */ new Map();
|
|
776
|
+
const held = /* @__PURE__ */ new Map();
|
|
777
|
+
let createdWithResult = 0;
|
|
778
|
+
let createdUndelivered = 0;
|
|
744
779
|
let runId = null;
|
|
745
780
|
let joined = false;
|
|
746
781
|
let offline = token === "";
|
|
@@ -869,6 +904,7 @@ async function startRun(passed, declared) {
|
|
|
869
904
|
specRef: result.specRef,
|
|
870
905
|
rawStatus: result.rawStatus
|
|
871
906
|
});
|
|
907
|
+
held.set(result.keys[0].value, result);
|
|
872
908
|
}
|
|
873
909
|
async function offer() {
|
|
874
910
|
if (discoveries.size === 0 || offline || runId === null) return;
|
|
@@ -887,7 +923,24 @@ async function startRun(passed, declared) {
|
|
|
887
923
|
return;
|
|
888
924
|
}
|
|
889
925
|
stats.offered += out.body.results.filter((r) => r?.outcome === "queued").length;
|
|
890
|
-
|
|
926
|
+
const made = out.body.results.filter((r) => r?.outcome === "created");
|
|
927
|
+
stats.created += made.length;
|
|
928
|
+
const submissions = [];
|
|
929
|
+
const filesByKey = /* @__PURE__ */ new Map();
|
|
930
|
+
for (const r of made) {
|
|
931
|
+
const pending = typeof r.id === "string" && r.id !== "" ? held.get(r.key?.value) : void 0;
|
|
932
|
+
if (pending === void 0) continue;
|
|
933
|
+
held.delete(r.key.value);
|
|
934
|
+
const { keys: _keys, files, ...rest } = pending;
|
|
935
|
+
submissions.push({ ...rest, testCaseId: r.id });
|
|
936
|
+
if (files !== void 0) filesByKey.set(pending.resultKey, files);
|
|
937
|
+
}
|
|
938
|
+
if (submissions.length > 0) {
|
|
939
|
+
const before = stats.deferred;
|
|
940
|
+
await send(submissions, filesByKey);
|
|
941
|
+
createdWithResult += submissions.length;
|
|
942
|
+
createdUndelivered += stats.deferred - before;
|
|
943
|
+
}
|
|
891
944
|
}
|
|
892
945
|
}
|
|
893
946
|
function skip(kind, name, why) {
|
|
@@ -1294,7 +1347,29 @@ async function startRun(passed, declared) {
|
|
|
1294
1347
|
else await uploadBatched(jobs);
|
|
1295
1348
|
uploadMs += Date.now() - started;
|
|
1296
1349
|
}
|
|
1350
|
+
const sending = /* @__PURE__ */ new Set();
|
|
1351
|
+
let sendError;
|
|
1352
|
+
let answered = false;
|
|
1353
|
+
async function dispatch(work) {
|
|
1354
|
+
while (sending.size >= (answered ? SEND_CONCURRENCY : 1)) await Promise.race(sending);
|
|
1355
|
+
const task = work().catch((err) => {
|
|
1356
|
+
sendError ??= err;
|
|
1357
|
+
}).finally(() => sending.delete(task));
|
|
1358
|
+
sending.add(task);
|
|
1359
|
+
}
|
|
1360
|
+
async function settle() {
|
|
1361
|
+
while (sending.size > 0) await Promise.all([...sending]);
|
|
1362
|
+
if (sendError !== void 0) {
|
|
1363
|
+
const err = sendError;
|
|
1364
|
+
sendError = void 0;
|
|
1365
|
+
throw err;
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1297
1368
|
async function flush() {
|
|
1369
|
+
await flushBuffer();
|
|
1370
|
+
await settle();
|
|
1371
|
+
}
|
|
1372
|
+
async function flushBuffer() {
|
|
1298
1373
|
if (buffer.length === 0) return;
|
|
1299
1374
|
const pending = buffer;
|
|
1300
1375
|
buffer = [];
|
|
@@ -1302,6 +1377,9 @@ async function startRun(passed, declared) {
|
|
|
1302
1377
|
defer(pending);
|
|
1303
1378
|
return;
|
|
1304
1379
|
}
|
|
1380
|
+
await dispatch(() => deliver(pending));
|
|
1381
|
+
}
|
|
1382
|
+
async function deliver(pending) {
|
|
1305
1383
|
await resolveKeys(pending.flatMap((p) => p.keys));
|
|
1306
1384
|
const submissions = [];
|
|
1307
1385
|
const unanswered = [];
|
|
@@ -1324,6 +1402,9 @@ async function startRun(passed, declared) {
|
|
|
1324
1402
|
progress();
|
|
1325
1403
|
return;
|
|
1326
1404
|
}
|
|
1405
|
+
await send(submissions, filesByKey);
|
|
1406
|
+
}
|
|
1407
|
+
async function send(submissions, filesByKey) {
|
|
1327
1408
|
for (const part of packed(submissions)) {
|
|
1328
1409
|
if (offline) {
|
|
1329
1410
|
defer(part);
|
|
@@ -1339,6 +1420,7 @@ async function startRun(passed, declared) {
|
|
|
1339
1420
|
for (const k of told) stats[k] += counts[k];
|
|
1340
1421
|
progress();
|
|
1341
1422
|
await upload(out.ok ? out.body?.items : void 0, filesByKey);
|
|
1423
|
+
answered = true;
|
|
1342
1424
|
continue;
|
|
1343
1425
|
}
|
|
1344
1426
|
if (out.ok) {
|
|
@@ -1367,7 +1449,10 @@ async function startRun(passed, declared) {
|
|
|
1367
1449
|
if (stats.rejected > 0) parts.push(`${stats.rejected} rejected`);
|
|
1368
1450
|
if (stats.unresolved > 0) parts.push(`${stats.unresolved} with no matching test case`);
|
|
1369
1451
|
if (stats.offered > 0) parts.push(`${stats.offered} offered for review`);
|
|
1370
|
-
if (stats.created > 0)
|
|
1452
|
+
if (stats.created > 0) {
|
|
1453
|
+
const recorded = createdWithResult === stats.created && createdUndelivered === 0;
|
|
1454
|
+
parts.push(`${stats.created} added as cases (trusted source)${recorded ? " and their results recorded" : ""}`);
|
|
1455
|
+
}
|
|
1371
1456
|
const kinds = [stats.screenshots, stats.textFiles, stats.otherFiles];
|
|
1372
1457
|
const sum = (what) => kinds.reduce((n, counts) => n + counts[what], 0);
|
|
1373
1458
|
if (bucketSeen) {
|
|
@@ -1397,6 +1482,7 @@ async function startRun(passed, declared) {
|
|
|
1397
1482
|
log(`::warning::${stats.deferred} result(s) were not delivered to Plune \u2014 see the reporting step's log.`);
|
|
1398
1483
|
}
|
|
1399
1484
|
}
|
|
1485
|
+
const shardLines = [];
|
|
1400
1486
|
return {
|
|
1401
1487
|
get runId() {
|
|
1402
1488
|
return runId;
|
|
@@ -1409,7 +1495,7 @@ async function startRun(passed, declared) {
|
|
|
1409
1495
|
},
|
|
1410
1496
|
async add(result) {
|
|
1411
1497
|
buffer.push({ ...result, keys: bound(result.keys) });
|
|
1412
|
-
if (buffer.length >= batchSize) await
|
|
1498
|
+
if (buffer.length >= batchSize) await flushBuffer();
|
|
1413
1499
|
},
|
|
1414
1500
|
flush,
|
|
1415
1501
|
async finish(reason) {
|
|
@@ -1434,15 +1520,43 @@ async function startRun(passed, declared) {
|
|
|
1434
1520
|
summarise();
|
|
1435
1521
|
return frozen;
|
|
1436
1522
|
},
|
|
1437
|
-
|
|
1438
|
-
|
|
1523
|
+
shardLines,
|
|
1524
|
+
async leaveOpen(opts) {
|
|
1525
|
+
if (done) return void 0;
|
|
1439
1526
|
done = true;
|
|
1440
1527
|
await flush();
|
|
1441
1528
|
await offer();
|
|
1442
|
-
|
|
1529
|
+
let frozen;
|
|
1530
|
+
const after = [];
|
|
1531
|
+
const shard = env.shard;
|
|
1532
|
+
let counted = false;
|
|
1533
|
+
let mismatch;
|
|
1534
|
+
if (runId !== null && shard !== void 0 && !offline) {
|
|
1535
|
+
const out = await client.post(`/v1/runs/${runId}/events`, { event: "finish", shardIndex: shard.index, shardsExpected: shard.total });
|
|
1536
|
+
if (out.ok && typeof out.body?.shardsReported === "number") {
|
|
1537
|
+
counted = true;
|
|
1538
|
+
frozen = out.body.summary ?? void 0;
|
|
1539
|
+
const n = out.body.shardsExpected ?? shard.total;
|
|
1540
|
+
if (out.body.run?.statusEvent === "finished") after.push("plune: run closed");
|
|
1541
|
+
after.push(`plune: shard ${shard.index} of ${shard.total} done \u2014 ${out.body.shardsReported} of ${n} shards reported`);
|
|
1542
|
+
} else if (out.ok || out.kind === "invalid") {
|
|
1543
|
+
log('plune: the platform does not support shard auto-close yet \u2014 the run is left open for "plune run finish".');
|
|
1544
|
+
} else if (out.code === "shards_mismatch") {
|
|
1545
|
+
mismatch = new Error(
|
|
1546
|
+
`PLUNE_SHARD=${shard.index}/${shard.total} says ${shard.total} shards, but run ${runId} already expects ${out.shardsExpected ?? "a different number of"} \u2014 every shard of one run must use the same N. The results are uploaded; the run is left open.`
|
|
1547
|
+
);
|
|
1548
|
+
} else {
|
|
1549
|
+
log(`plune: could not report shard ${shard.index} of ${shard.total} to run ${runId} (${out.detail || out.kind}) \u2014 the results are uploaded; close the run with "plune run finish ${runId}".`);
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
if (runId !== null && !counted) {
|
|
1443
1553
|
log(`plune: run ${runId} is still open \u2014 close it with "plune run finish ${runId}".`);
|
|
1444
1554
|
}
|
|
1445
1555
|
summarise();
|
|
1556
|
+
shardLines.push(...after);
|
|
1557
|
+
if (opts?.holdShardLines !== true) for (const line of after) log(line);
|
|
1558
|
+
if (mismatch !== void 0) throw mismatch;
|
|
1559
|
+
return frozen;
|
|
1446
1560
|
}
|
|
1447
1561
|
};
|
|
1448
1562
|
}
|
package/dist/reporter-core.d.cts
CHANGED
|
@@ -326,7 +326,7 @@ interface ReporterConfig {
|
|
|
326
326
|
labels?: string[];
|
|
327
327
|
/** Results per request. The platform's ceiling is 500. */
|
|
328
328
|
batchSize?: number;
|
|
329
|
-
/** Files in flight at once (`PLUNE_UPLOAD_CONCURRENCY`, 1 to 32, default
|
|
329
|
+
/** Files in flight at once (`PLUNE_UPLOAD_CONCURRENCY`, 1 to 32, default 16). */
|
|
330
330
|
uploadConcurrency?: number;
|
|
331
331
|
/** Retries of a request the platform or a bucket asked to wait for (`PLUNE_UPLOAD_RETRIES`, 0 to 20, default 6). */
|
|
332
332
|
uploadRetries?: number;
|
|
@@ -377,8 +377,15 @@ interface RunSession {
|
|
|
377
377
|
* `undefined` when it answered with none: no run, a refused finish, a deployment older than the summary.
|
|
378
378
|
*/
|
|
379
379
|
finish(reason?: string): Promise<FrozenSummary | undefined>;
|
|
380
|
-
/**
|
|
381
|
-
|
|
380
|
+
/**
|
|
381
|
+
* Send what is left and deliberately leave the run open - the shard case (AC-03). With `PLUNE_SHARD=k/N`
|
|
382
|
+
* it reports "shard k of N is done" instead (cli#110) and resolves to the summary when that was the Nth.
|
|
383
|
+
*/
|
|
384
|
+
leaveOpen(opts?: {
|
|
385
|
+
holdShardLines?: boolean;
|
|
386
|
+
}): Promise<FrozenSummary | undefined>;
|
|
387
|
+
/** The closing lines of a `PLUNE_SHARD` report (`run closed`, `shard k of N done`). Printed by `leaveOpen` unless it was told to hold them for a caller that has more to say first. */
|
|
388
|
+
readonly shardLines: readonly string[];
|
|
382
389
|
}
|
|
383
390
|
declare function startRun(passed: ReporterConfig, declared?: readonly (readonly KeyRef[])[]): Promise<RunSession>;
|
|
384
391
|
|
|
@@ -424,11 +431,20 @@ declare function resultKey(testId: string, retry: number): string;
|
|
|
424
431
|
* put them. `PLUNE_GROUP` stays: a run group is D6's, and a group of one run means nothing.
|
|
425
432
|
*/
|
|
426
433
|
declare const UNSUPPORTED_VARS: readonly ["PLUNE_GROUP"];
|
|
434
|
+
interface ShardOf {
|
|
435
|
+
/** 1-based. */
|
|
436
|
+
index: number;
|
|
437
|
+
total: number;
|
|
438
|
+
}
|
|
427
439
|
interface EnvSettings {
|
|
428
440
|
/** Config fields the environment supplied. Anything passed explicitly outranks these. */
|
|
429
441
|
config: Partial<ReporterConfig>;
|
|
430
442
|
/** This process must not close the run — several share it, or the job closes it itself. */
|
|
431
443
|
keepOpen: boolean;
|
|
444
|
+
/** `PLUNE_SHARD=k/N`: this process is shard k of N, and tells the platform when it is done (cli#110). */
|
|
445
|
+
shard?: ShardOf;
|
|
446
|
+
/** `PLUNE_SHARD` was set to something that is not `k/N`. A configuration error: the caller refuses to start. */
|
|
447
|
+
shardError?: string;
|
|
432
448
|
/** Variables that were set and have nowhere to land. The caller says so once. */
|
|
433
449
|
ignored: string[];
|
|
434
450
|
/** Variables that were set to something unusable and fell back to their default. The caller says so once. */
|
|
@@ -562,6 +578,7 @@ type ClientOutcome<T> = {
|
|
|
562
578
|
status: number | null;
|
|
563
579
|
detail: string;
|
|
564
580
|
code?: string;
|
|
581
|
+
shardsExpected?: number;
|
|
565
582
|
};
|
|
566
583
|
/** What a PUT to a bucket carries: a file on disk, streamed at a length known beforehand, or bytes in memory. */
|
|
567
584
|
type PutSource = {
|
|
@@ -612,6 +629,8 @@ interface PlatformClient {
|
|
|
612
629
|
* a proxy error page that echoes the request would put a token in a CI log.
|
|
613
630
|
*/
|
|
614
631
|
del<T>(path: string): Promise<ClientOutcome<T>>;
|
|
632
|
+
/** A GET, through the same retries, classification and token scrubbing. */
|
|
633
|
+
get<T>(path: string): Promise<ClientOutcome<T>>;
|
|
615
634
|
/**
|
|
616
635
|
* The bytes to a short-lived link of the customer's bucket (plune-ai/plune#946): exactly the headers the
|
|
617
636
|
* link came with, no token of ours. Network failures and 5xx wait and try again, up to three times; a 403
|
package/dist/reporter-core.d.ts
CHANGED
|
@@ -326,7 +326,7 @@ interface ReporterConfig {
|
|
|
326
326
|
labels?: string[];
|
|
327
327
|
/** Results per request. The platform's ceiling is 500. */
|
|
328
328
|
batchSize?: number;
|
|
329
|
-
/** Files in flight at once (`PLUNE_UPLOAD_CONCURRENCY`, 1 to 32, default
|
|
329
|
+
/** Files in flight at once (`PLUNE_UPLOAD_CONCURRENCY`, 1 to 32, default 16). */
|
|
330
330
|
uploadConcurrency?: number;
|
|
331
331
|
/** Retries of a request the platform or a bucket asked to wait for (`PLUNE_UPLOAD_RETRIES`, 0 to 20, default 6). */
|
|
332
332
|
uploadRetries?: number;
|
|
@@ -377,8 +377,15 @@ interface RunSession {
|
|
|
377
377
|
* `undefined` when it answered with none: no run, a refused finish, a deployment older than the summary.
|
|
378
378
|
*/
|
|
379
379
|
finish(reason?: string): Promise<FrozenSummary | undefined>;
|
|
380
|
-
/**
|
|
381
|
-
|
|
380
|
+
/**
|
|
381
|
+
* Send what is left and deliberately leave the run open - the shard case (AC-03). With `PLUNE_SHARD=k/N`
|
|
382
|
+
* it reports "shard k of N is done" instead (cli#110) and resolves to the summary when that was the Nth.
|
|
383
|
+
*/
|
|
384
|
+
leaveOpen(opts?: {
|
|
385
|
+
holdShardLines?: boolean;
|
|
386
|
+
}): Promise<FrozenSummary | undefined>;
|
|
387
|
+
/** The closing lines of a `PLUNE_SHARD` report (`run closed`, `shard k of N done`). Printed by `leaveOpen` unless it was told to hold them for a caller that has more to say first. */
|
|
388
|
+
readonly shardLines: readonly string[];
|
|
382
389
|
}
|
|
383
390
|
declare function startRun(passed: ReporterConfig, declared?: readonly (readonly KeyRef[])[]): Promise<RunSession>;
|
|
384
391
|
|
|
@@ -424,11 +431,20 @@ declare function resultKey(testId: string, retry: number): string;
|
|
|
424
431
|
* put them. `PLUNE_GROUP` stays: a run group is D6's, and a group of one run means nothing.
|
|
425
432
|
*/
|
|
426
433
|
declare const UNSUPPORTED_VARS: readonly ["PLUNE_GROUP"];
|
|
434
|
+
interface ShardOf {
|
|
435
|
+
/** 1-based. */
|
|
436
|
+
index: number;
|
|
437
|
+
total: number;
|
|
438
|
+
}
|
|
427
439
|
interface EnvSettings {
|
|
428
440
|
/** Config fields the environment supplied. Anything passed explicitly outranks these. */
|
|
429
441
|
config: Partial<ReporterConfig>;
|
|
430
442
|
/** This process must not close the run — several share it, or the job closes it itself. */
|
|
431
443
|
keepOpen: boolean;
|
|
444
|
+
/** `PLUNE_SHARD=k/N`: this process is shard k of N, and tells the platform when it is done (cli#110). */
|
|
445
|
+
shard?: ShardOf;
|
|
446
|
+
/** `PLUNE_SHARD` was set to something that is not `k/N`. A configuration error: the caller refuses to start. */
|
|
447
|
+
shardError?: string;
|
|
432
448
|
/** Variables that were set and have nowhere to land. The caller says so once. */
|
|
433
449
|
ignored: string[];
|
|
434
450
|
/** Variables that were set to something unusable and fell back to their default. The caller says so once. */
|
|
@@ -562,6 +578,7 @@ type ClientOutcome<T> = {
|
|
|
562
578
|
status: number | null;
|
|
563
579
|
detail: string;
|
|
564
580
|
code?: string;
|
|
581
|
+
shardsExpected?: number;
|
|
565
582
|
};
|
|
566
583
|
/** What a PUT to a bucket carries: a file on disk, streamed at a length known beforehand, or bytes in memory. */
|
|
567
584
|
type PutSource = {
|
|
@@ -612,6 +629,8 @@ interface PlatformClient {
|
|
|
612
629
|
* a proxy error page that echoes the request would put a token in a CI log.
|
|
613
630
|
*/
|
|
614
631
|
del<T>(path: string): Promise<ClientOutcome<T>>;
|
|
632
|
+
/** A GET, through the same retries, classification and token scrubbing. */
|
|
633
|
+
get<T>(path: string): Promise<ClientOutcome<T>>;
|
|
615
634
|
/**
|
|
616
635
|
* The bytes to a short-lived link of the customer's bucket (plune-ai/plune#946): exactly the headers the
|
|
617
636
|
* link came with, no token of ours. Network failures and 5xx wait and try again, up to three times; a 403
|