@plune-ai/cli 0.30.2 → 0.32.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 +71 -10
- package/dist/cli.cjs +279 -175
- package/dist/index.cjs +90 -66
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +90 -66
- package/dist/reporter-core.cjs +31 -4
- package/dist/reporter-core.d.cts +13 -0
- package/dist/reporter-core.d.ts +13 -0
- package/dist/reporter-core.js +31 -4
- package/package.json +1 -1
package/dist/reporter-core.d.ts
CHANGED
|
@@ -324,6 +324,12 @@ interface ReporterConfig {
|
|
|
324
324
|
environment?: string;
|
|
325
325
|
/** How the run is marked — smoke, nightly, a team. */
|
|
326
326
|
labels?: string[];
|
|
327
|
+
/**
|
|
328
|
+
* The combination this job runs, group name → value label (`PLUNE_CONFIGURATION`, plune-ai/plune#1251):
|
|
329
|
+
* sent as `configuration` on every results batch. Not the run's own `configuration` (what it expects).
|
|
330
|
+
* Stated, it is all there is: the variable is not read, and `{}` sends none (what a replay passes for a line that recorded none).
|
|
331
|
+
*/
|
|
332
|
+
configuration?: Record<string, string>;
|
|
327
333
|
/** Results per request. The platform's ceiling is 500. */
|
|
328
334
|
batchSize?: number;
|
|
329
335
|
/** Files in flight at once (`PLUNE_UPLOAD_CONCURRENCY`, 1 to 32, default 16). */
|
|
@@ -445,6 +451,8 @@ interface EnvSettings {
|
|
|
445
451
|
shard?: ShardOf;
|
|
446
452
|
/** `PLUNE_SHARD` was set to something that is not `k/N`. A configuration error: the caller refuses to start. */
|
|
447
453
|
shardError?: string;
|
|
454
|
+
/** `PLUNE_CONFIGURATION` was set to something that is not `Group=Value;…`. Refused the same way. */
|
|
455
|
+
configurationError?: string;
|
|
448
456
|
/** `PLUNE_IMPORT_TIMEOUT` in seconds, when set and usable. */
|
|
449
457
|
importTimeout?: number;
|
|
450
458
|
/** Variables that were set and have nowhere to land. The caller says so once. */
|
|
@@ -498,12 +506,17 @@ declare const DEFAULT_FALLBACK_PATH: string;
|
|
|
498
506
|
* A run of 24 tests at a batch of 10 is three lines, and replay opens one run for the lines that share a
|
|
499
507
|
* marker rather than one for each. A line written before the field existed has none, and is one run's
|
|
500
508
|
* worth by itself — nothing in it says otherwise.
|
|
509
|
+
*
|
|
510
|
+
* `configuration` is the combination the job named (`PLUNE_CONFIGURATION`, plune-ai/plune#1251), recorded for
|
|
511
|
+
* the same reason as `meta`: replay sends the batch into the combination it ran in, whatever the machine that
|
|
512
|
+
* replays it has set. A line without it was written by a job that named none, or before the field existed.
|
|
501
513
|
*/
|
|
502
514
|
interface DeferredBatch {
|
|
503
515
|
runId: string | null;
|
|
504
516
|
externalKey: string | null;
|
|
505
517
|
session?: string;
|
|
506
518
|
meta?: RunMeta;
|
|
519
|
+
configuration?: Record<string, string>;
|
|
507
520
|
results: DeferredResult[];
|
|
508
521
|
}
|
|
509
522
|
/**
|
package/dist/reporter-core.js
CHANGED
|
@@ -265,6 +265,25 @@ function shardOf(env) {
|
|
|
265
265
|
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.`
|
|
266
266
|
};
|
|
267
267
|
}
|
|
268
|
+
function configurationOf(env) {
|
|
269
|
+
const raw = value(env, "PLUNE_CONFIGURATION");
|
|
270
|
+
if (raw === void 0) return {};
|
|
271
|
+
const pairs = [];
|
|
272
|
+
for (const pair of raw.split(";").map((s) => s.trim()).filter((s) => s !== "")) {
|
|
273
|
+
const at = pair.indexOf("=");
|
|
274
|
+
const name = at < 0 ? "" : pair.slice(0, at).trim();
|
|
275
|
+
const label = at < 0 ? "" : pair.slice(at + 1).trim();
|
|
276
|
+
const twice = pairs.find(([seen]) => seen.toLowerCase() === name.toLowerCase());
|
|
277
|
+
const why = at < 0 ? 'has no "="' : name === "" ? "has no group name" : label === "" ? "has no value" : twice !== void 0 ? `names the group ${JSON.stringify(twice[0])} a second time` : void 0;
|
|
278
|
+
if (why !== void 0) {
|
|
279
|
+
return {
|
|
280
|
+
error: `PLUNE_CONFIGURATION: ${JSON.stringify(pair.slice(0, 40))} ${why} - write it as "Group=Value;Group=Value" (for example "Browser=Chrome;Operating system=Windows") - nothing was sent.`
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
pairs.push([name, label]);
|
|
284
|
+
}
|
|
285
|
+
return pairs.length === 0 ? {} : { configuration: Object.fromEntries(pairs) };
|
|
286
|
+
}
|
|
268
287
|
var UPLOAD_CONCURRENCY = { min: 1, max: 32, default: 16 };
|
|
269
288
|
var UPLOAD_RETRIES = { min: 0, max: 20, default: 6 };
|
|
270
289
|
var IMPORT_TIMEOUT = { min: 0, max: 86400, default: 600 };
|
|
@@ -312,6 +331,7 @@ function readEnv(env = process.env) {
|
|
|
312
331
|
const offerDiscovered = flag(env, "PLUNE_CREATE");
|
|
313
332
|
const full = flag(env, "PLUNE_FULL_RUN");
|
|
314
333
|
const { shard, error: shardError } = shardOf(env);
|
|
334
|
+
const { configuration, error: configurationError } = configurationOf(env);
|
|
315
335
|
return {
|
|
316
336
|
config: {
|
|
317
337
|
...externalKey !== void 0 ? { externalKey } : {},
|
|
@@ -324,6 +344,7 @@ function readEnv(env = process.env) {
|
|
|
324
344
|
...title !== void 0 ? { title } : {},
|
|
325
345
|
...environment !== void 0 ? { environment } : {},
|
|
326
346
|
...labels !== void 0 ? { labels } : {},
|
|
347
|
+
...configuration !== void 0 ? { configuration } : {},
|
|
327
348
|
// Only when set: `false` here would outrank a config file that asked for it, and an unset
|
|
328
349
|
// variable is not an instruction.
|
|
329
350
|
...offerDiscovered ? { offerDiscovered } : {},
|
|
@@ -337,6 +358,7 @@ function readEnv(env = process.env) {
|
|
|
337
358
|
keepOpen: flag(env, "PLUNE_SHARED_RUN") || flag(env, "PLUNE_PROCEED") || shard !== void 0 || shardError !== void 0,
|
|
338
359
|
...shard !== void 0 ? { shard } : {},
|
|
339
360
|
...shardError !== void 0 ? { shardError } : {},
|
|
361
|
+
...configurationError !== void 0 ? { configurationError } : {},
|
|
340
362
|
...importTimeout !== void 0 ? { importTimeout } : {},
|
|
341
363
|
ignored: UNSUPPORTED_VARS.filter((name) => value(env, name) !== void 0),
|
|
342
364
|
warnings
|
|
@@ -547,6 +569,7 @@ function appendBatch(file, batch) {
|
|
|
547
569
|
externalKey: batch.externalKey,
|
|
548
570
|
...batch.session !== void 0 ? { session: batch.session } : {},
|
|
549
571
|
...batch.meta !== void 0 ? { meta: batch.meta } : {},
|
|
572
|
+
...batch.configuration !== void 0 ? { configuration: batch.configuration } : {},
|
|
550
573
|
results: batch.results
|
|
551
574
|
};
|
|
552
575
|
fs.appendFileSync(file, `${JSON.stringify(line)}
|
|
@@ -697,8 +720,10 @@ function warnIfDropped(sent, run, log) {
|
|
|
697
720
|
async function startRun(passed, declared) {
|
|
698
721
|
const env = readEnv();
|
|
699
722
|
if (env.shardError !== void 0) throw new Error(env.shardError);
|
|
723
|
+
if (env.configurationError !== void 0 && passed.configuration === void 0) throw new Error(env.configurationError);
|
|
700
724
|
const stated = Object.fromEntries(Object.entries(passed).filter(([, v]) => v !== void 0));
|
|
701
725
|
const cfg = { ...env.config, ...stated };
|
|
726
|
+
const configuration = cfg.configuration !== void 0 && Object.keys(cfg.configuration).length > 0 ? cfg.configuration : void 0;
|
|
702
727
|
const log = cfg.log ?? ((line) => void process.stderr.write(`${line}
|
|
703
728
|
`));
|
|
704
729
|
const fallbackPath = cfg.fallbackPath ?? DEFAULT_FALLBACK_PATH;
|
|
@@ -782,6 +807,7 @@ async function startRun(passed, declared) {
|
|
|
782
807
|
externalKey,
|
|
783
808
|
session: sessionMarker,
|
|
784
809
|
...cfg.meta !== void 0 ? { meta: cfg.meta } : {},
|
|
810
|
+
...configuration !== void 0 ? { configuration } : {},
|
|
785
811
|
results: results.map(onDisk)
|
|
786
812
|
});
|
|
787
813
|
stats.deferred += results.length;
|
|
@@ -806,7 +832,7 @@ async function startRun(passed, declared) {
|
|
|
806
832
|
if (!offline) {
|
|
807
833
|
if (expected !== void 0) await resolveKeys(expected.flat());
|
|
808
834
|
const stated2 = expected?.slice(0, EXPECTED_MAX) ?? [];
|
|
809
|
-
const
|
|
835
|
+
const configuration2 = stated2.length === 0 ? void 0 : {
|
|
810
836
|
expected: stated2.flatMap((keys) => {
|
|
811
837
|
const hit = keys.map((k) => resolved.get(k.value)).find((v) => typeof v === "string");
|
|
812
838
|
if (typeof hit === "string") return [{ testCaseId: hit }];
|
|
@@ -830,7 +856,7 @@ async function startRun(passed, declared) {
|
|
|
830
856
|
kind: cfg.kind ?? "automated",
|
|
831
857
|
...externalKey !== null ? { externalKey } : {},
|
|
832
858
|
...cfg.meta !== void 0 ? { meta: cfg.meta } : {},
|
|
833
|
-
...
|
|
859
|
+
...configuration2 !== void 0 ? { configuration: configuration2 } : {},
|
|
834
860
|
// Named even when nobody named it — the list has to read without opening a row. Only what
|
|
835
861
|
// the caller DESCRIBED goes to `warnIfDropped` below: a title this side invented is not one
|
|
836
862
|
// a person will miss, and a deployment older than the field must not be nagged on every run.
|
|
@@ -838,7 +864,7 @@ async function startRun(passed, declared) {
|
|
|
838
864
|
...described
|
|
839
865
|
};
|
|
840
866
|
let start = await client.post("/v1/runs", request);
|
|
841
|
-
if (!start.ok && start.status === 413 &&
|
|
867
|
+
if (!start.ok && start.status === 413 && configuration2 !== void 0) {
|
|
842
868
|
log(
|
|
843
869
|
`plune: the platform refused the run's list of expected tests as too large (${start.detail || start.kind}) \u2014 starting without it: the results still land, but this run counts no tests that did not run, and claims no full run.`
|
|
844
870
|
);
|
|
@@ -1384,7 +1410,8 @@ async function startRun(passed, declared) {
|
|
|
1384
1410
|
}
|
|
1385
1411
|
const out = await client.post(
|
|
1386
1412
|
`/v1/runs/${runId}/results`,
|
|
1387
|
-
|
|
1413
|
+
// The job's combination on every batch (plune-ai/plune#1251); a refusal of it is a 422 said below as it came.
|
|
1414
|
+
{ results: part, ...configuration !== void 0 ? { configuration } : {} }
|
|
1388
1415
|
);
|
|
1389
1416
|
const counts = out.ok ? out.body?.counts : void 0;
|
|
1390
1417
|
const told = COUNTED.filter((k) => typeof counts?.[k] === "number");
|
package/package.json
CHANGED