@plune-ai/cli 0.30.0 → 0.30.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/README.md +1 -1
- package/dist/cli.cjs +46 -21
- package/dist/index.cjs +31 -17
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +31 -17
- package/dist/reporter-core.cjs +9 -2
- package/dist/reporter-core.js +9 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -121,7 +121,7 @@ the last resort, used only when `PLUNE_TOKEN` is set nowhere else. Nothing write
|
|
|
121
121
|
| `plune init` | Scaffold `plune.yaml`, a sample dataset, and `.env.example`. Flags: `--yes` (non-interactive), `--force`. |
|
|
122
122
|
| `plune login` | Save a [Plune platform](https://plune.ai/platform) API token so `sync` and `ingest` can reach it. The token is **checked against the API before it is saved**, so a wrong one fails here rather than two commands later. Get one at `https://beta.plune.ai` → Account → API tokens. Flags: `--token <token>` (omit to paste it or pipe it via stdin), `--skip-verify` (save without checking, for offline setup). In CI, skip this step: every platform command reads `PLUNE_TOKEN` from the environment first, and the saved login only when it is not set. |
|
|
123
123
|
| `plune logout` | Remove the saved token. |
|
|
124
|
-
| `plune sync` | Upload the latest local run to the platform. Flags: `--file <path>` to send a specific run JSON, `--fail-on-failures` to read the run's summary back once after the upload and exit with [the gate](#a-release-gate---fail-on-failures)'s code. Each upload carries an `Idempotency-Key` (SHA-256 of the body), so sending the same run again returns the same run rather than creating a second one. Against a platform with `POST /v1/jobs` the upload is also reported as a job (kind `
|
|
124
|
+
| `plune sync` | Upload the latest local run to the platform. Flags: `--file <path>` to send a specific run JSON, `--fail-on-failures` to read the run's summary back once after the upload and exit with [the gate](#a-release-gate---fail-on-failures)'s code. Each upload carries an `Idempotency-Key` (SHA-256 of the body), so sending the same run again returns the same run rather than creating a second one. Against a platform with `POST /v1/jobs` the upload is also reported as a job (kind `sync`, counting the run's rows) that ends completed, or failed with the error text if the sync fails; an older platform without the route is not told, and a job report never fails the sync: when the sync goes through, one the platform did not take is said in one line on stderr. |
|
|
125
125
|
| `plune run import <file>` | Turn a **JUnit XML** or **Playwright JSON** report, or an **Allure `allure-results` folder**, into a run in Plune. Needs no provider key — nothing is generated. Flags: `--format junit\|playwright-json\|allure` (detected from the file when omitted; a folder is Allure), `--name <text>` to call the run something other than the [folder-based default](#what-the-run-is-called), `--key <externalKey>` to land several reports in one run (with `PLUNE_SHARED_RUN=1` — see below), `--create` to offer unmatched tests to the review queue, `--fail-on-missing-attachments` to exit 1 when a file a JUnit report names with `[[ATTACHMENT\|path]]`, or an Allure result names as a `source`, is not there (the results are sent either way), `--upload-concurrency <n>` for files uploaded at once ([1 to 32, default 16](#many-screenshots-upload-concurrency-and-retries)), `--fail-on-failures` to exit with [the gate](#a-release-gate---fail-on-failures)'s code on the summary the platform froze when this import finished the run. |
|
|
126
126
|
| `plune run start` | Open a platform run — or join the one already carrying `--key` — and print its id. Flags: `--key <externalKey>` (generated when absent), `--name <text>` to call the run something other than the [default](#what-the-run-is-called), `--json` for one machine-readable line. |
|
|
127
127
|
| `plune run finish <id>` | Close a platform run. This is what the reporter tells you to do for a run it had to leave open. Flags: `--terminate` to record it as cut short, `--reason <text>`, `--fail-on-failures` to exit with [the gate](#a-release-gate---fail-on-failures)'s code on the summary the platform froze. |
|
package/dist/cli.cjs
CHANGED
|
@@ -3230,7 +3230,7 @@ async function startRun(passed, declared2) {
|
|
|
3230
3230
|
...cfg.environment !== void 0 ? { environment: cfg.environment } : {},
|
|
3231
3231
|
...cfg.labels !== void 0 ? { labels: cfg.labels } : {}
|
|
3232
3232
|
};
|
|
3233
|
-
const
|
|
3233
|
+
const request = {
|
|
3234
3234
|
schemaVersion: 2,
|
|
3235
3235
|
kind: cfg.kind ?? "automated",
|
|
3236
3236
|
...externalKey !== null ? { externalKey } : {},
|
|
@@ -3241,7 +3241,14 @@ async function startRun(passed, declared2) {
|
|
|
3241
3241
|
// a person will miss, and a deployment older than the field must not be nagged on every run.
|
|
3242
3242
|
title: defaultRunTitle(),
|
|
3243
3243
|
...described
|
|
3244
|
-
}
|
|
3244
|
+
};
|
|
3245
|
+
let start = await client.post("/v1/runs", request);
|
|
3246
|
+
if (!start.ok && start.status === 413 && configuration !== void 0) {
|
|
3247
|
+
log(
|
|
3248
|
+
`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.`
|
|
3249
|
+
);
|
|
3250
|
+
start = await client.post("/v1/runs", { ...request, configuration: void 0 });
|
|
3251
|
+
}
|
|
3245
3252
|
if (start.ok && typeof start.body?.run?.id === "string") {
|
|
3246
3253
|
runId = start.body.run.id;
|
|
3247
3254
|
joined = start.status === 200;
|
|
@@ -4369,10 +4376,11 @@ async function handleSync(deps = {}) {
|
|
|
4369
4376
|
const idempotencyKey = (0, import_node_crypto4.createHash)("sha256").update(payload).digest("hex");
|
|
4370
4377
|
const bearer = token;
|
|
4371
4378
|
const job = new JobReporter(doFetch, apiUrl, bearer);
|
|
4372
|
-
await job.open(path9.basename(deps.file !== void 0 ? path9.resolve(cwd, deps.file) : cwd));
|
|
4379
|
+
await job.open(path9.basename(deps.file !== void 0 ? path9.resolve(cwd, deps.file) : cwd), run.summary.total);
|
|
4373
4380
|
try {
|
|
4374
4381
|
const result = await upload();
|
|
4375
4382
|
await job.finish({ ok: true });
|
|
4383
|
+
if (job.problem !== void 0) result.jobProblem = job.problem;
|
|
4376
4384
|
return result;
|
|
4377
4385
|
} catch (err) {
|
|
4378
4386
|
await job.finish({ ok: false, reason: err instanceof Error ? err.message : "Sync failed." });
|
|
@@ -4401,7 +4409,7 @@ async function handleSync(deps = {}) {
|
|
|
4401
4409
|
const body = await res.json().catch(() => ({}));
|
|
4402
4410
|
const id = typeof body.id === "string" ? body.id : "";
|
|
4403
4411
|
const result = { id, url: `${endpoint}/${id}` };
|
|
4404
|
-
await job.
|
|
4412
|
+
await job.uploaded(id || void 0);
|
|
4405
4413
|
if (deps.failOnFailures === true) {
|
|
4406
4414
|
result.gate = gateExit(await readVerdict(doFetch, `${endpoint}/${id}/summary`, bearer));
|
|
4407
4415
|
}
|
|
@@ -4428,7 +4436,7 @@ function reportSyncFailure(err, write) {
|
|
|
4428
4436
|
}
|
|
4429
4437
|
return null;
|
|
4430
4438
|
}
|
|
4431
|
-
var import_node_crypto4, fs8, path9, NotLoggedInError, TokenRejectedError, SyncFileError, SyncNetworkError, SyncHttpError, JOB_KIND, JOB_TIMEOUT_MS, JobReporter;
|
|
4439
|
+
var import_node_crypto4, fs8, path9, NotLoggedInError, TokenRejectedError, SyncFileError, SyncNetworkError, SyncHttpError, JOB_KIND, JOB_TIMEOUT_MS, JOB_SUBJECT_NAME_MAX, JOB_REASON_MAX, JobReporter;
|
|
4432
4440
|
var init_sync = __esm({
|
|
4433
4441
|
"src/cli/commands/sync.ts"() {
|
|
4434
4442
|
"use strict";
|
|
@@ -4476,8 +4484,10 @@ var init_sync = __esm({
|
|
|
4476
4484
|
}
|
|
4477
4485
|
status;
|
|
4478
4486
|
};
|
|
4479
|
-
JOB_KIND = "
|
|
4487
|
+
JOB_KIND = "sync";
|
|
4480
4488
|
JOB_TIMEOUT_MS = 5e3;
|
|
4489
|
+
JOB_SUBJECT_NAME_MAX = 200;
|
|
4490
|
+
JOB_REASON_MAX = 2e3;
|
|
4481
4491
|
JobReporter = class {
|
|
4482
4492
|
constructor(doFetch, base, token) {
|
|
4483
4493
|
this.doFetch = doFetch;
|
|
@@ -4488,32 +4498,43 @@ var init_sync = __esm({
|
|
|
4488
4498
|
base;
|
|
4489
4499
|
token;
|
|
4490
4500
|
id;
|
|
4491
|
-
|
|
4501
|
+
/** The run's rows, the job's `total`; `null` while there are none to count. */
|
|
4502
|
+
size = null;
|
|
4503
|
+
/** The first report the platform did not take, for the CLI to say once. */
|
|
4504
|
+
problem;
|
|
4505
|
+
async send(method, route, body) {
|
|
4492
4506
|
try {
|
|
4493
|
-
const res = await this.doFetch(
|
|
4507
|
+
const res = await this.doFetch(`${this.base}/v1/jobs${route}`, {
|
|
4494
4508
|
method,
|
|
4495
4509
|
headers: { "content-type": "application/json", authorization: `Bearer ${this.token}` },
|
|
4496
4510
|
body: JSON.stringify(body),
|
|
4497
4511
|
signal: AbortSignal.timeout(JOB_TIMEOUT_MS)
|
|
4498
4512
|
});
|
|
4499
4513
|
const json = await res.json().catch(() => void 0);
|
|
4500
|
-
|
|
4501
|
-
|
|
4502
|
-
|
|
4514
|
+
if (res.ok) return json ?? {};
|
|
4515
|
+
if (route === "" && (res.status === 404 || res.status === 405)) return void 0;
|
|
4516
|
+
this.problem ??= `HTTP ${res.status}${typeof json?.error === "string" ? ` \u2014 ${json.error}` : ""}`;
|
|
4517
|
+
} catch (err) {
|
|
4518
|
+
this.problem ??= err instanceof Error ? err.message : String(err);
|
|
4503
4519
|
}
|
|
4520
|
+
return void 0;
|
|
4504
4521
|
}
|
|
4505
|
-
async open(subjectName) {
|
|
4506
|
-
|
|
4522
|
+
async open(subjectName, rows) {
|
|
4523
|
+
this.size = Number.isInteger(rows) && rows > 0 ? rows : null;
|
|
4524
|
+
const name = subjectName.trim().slice(0, JOB_SUBJECT_NAME_MAX);
|
|
4525
|
+
const body = await this.send("POST", "", {
|
|
4507
4526
|
kind: JOB_KIND,
|
|
4508
|
-
subjectName,
|
|
4509
|
-
progress: 0
|
|
4527
|
+
...name !== "" ? { subjectName: name } : {},
|
|
4528
|
+
...this.size !== null ? { progress: { done: 0, total: this.size } } : {}
|
|
4510
4529
|
});
|
|
4511
4530
|
if (typeof body?.id === "string") this.id = body.id;
|
|
4512
4531
|
}
|
|
4513
|
-
|
|
4532
|
+
/** The upload is in: every row of the run is on the platform. */
|
|
4533
|
+
async uploaded(cursor) {
|
|
4514
4534
|
if (this.id === void 0) return;
|
|
4515
|
-
await this.send("PATCH",
|
|
4516
|
-
|
|
4535
|
+
await this.send("PATCH", `/${encodeURIComponent(this.id)}/progress`, {
|
|
4536
|
+
done: this.size,
|
|
4537
|
+
total: this.size,
|
|
4517
4538
|
...cursor !== void 0 ? { cursor } : {}
|
|
4518
4539
|
});
|
|
4519
4540
|
}
|
|
@@ -4523,8 +4544,8 @@ var init_sync = __esm({
|
|
|
4523
4544
|
this.id = void 0;
|
|
4524
4545
|
await this.send(
|
|
4525
4546
|
"PATCH",
|
|
4526
|
-
|
|
4527
|
-
outcome.ok ? {
|
|
4547
|
+
`/${encodeURIComponent(id)}/state`,
|
|
4548
|
+
outcome.ok ? { state: "completed" } : { state: "failed", reason: outcome.reason.slice(0, JOB_REASON_MAX) }
|
|
4528
4549
|
);
|
|
4529
4550
|
}
|
|
4530
4551
|
};
|
|
@@ -7658,13 +7679,17 @@ ${missed.map((m) => ` - ${m}`).join("\n")}
|
|
|
7658
7679
|
const { handleSync: handleSync2, reportSyncFailure: reportSyncFailure2 } = await Promise.resolve().then(() => (init_sync(), sync_exports));
|
|
7659
7680
|
const cwd = globals.config !== void 0 ? (0, import_node_path6.dirname)((0, import_node_path6.resolve)(globals.config)) : process.cwd();
|
|
7660
7681
|
try {
|
|
7661
|
-
const { id, url, gate } = await handleSync2({
|
|
7682
|
+
const { id, url, gate, jobProblem } = await handleSync2({
|
|
7662
7683
|
cwd,
|
|
7663
7684
|
...options.file !== void 0 ? { file: options.file } : {},
|
|
7664
7685
|
...options.failOnFailures ? { failOnFailures: true } : {}
|
|
7665
7686
|
});
|
|
7666
7687
|
process.stdout.write(`Synced run ${id} \u2192 ${url}
|
|
7667
7688
|
`);
|
|
7689
|
+
if (jobProblem !== void 0) {
|
|
7690
|
+
process.stderr.write(`plune: could not report this sync to the job queue (${jobProblem}) \u2014 the run is uploaded.
|
|
7691
|
+
`);
|
|
7692
|
+
}
|
|
7668
7693
|
if (gate !== void 0) {
|
|
7669
7694
|
process.stdout.write(`${gate.line}
|
|
7670
7695
|
`);
|
package/dist/index.cjs
CHANGED
|
@@ -1892,8 +1892,10 @@ var SyncHttpError = class extends Error {
|
|
|
1892
1892
|
}
|
|
1893
1893
|
status;
|
|
1894
1894
|
};
|
|
1895
|
-
var JOB_KIND = "
|
|
1895
|
+
var JOB_KIND = "sync";
|
|
1896
1896
|
var JOB_TIMEOUT_MS = 5e3;
|
|
1897
|
+
var JOB_SUBJECT_NAME_MAX = 200;
|
|
1898
|
+
var JOB_REASON_MAX = 2e3;
|
|
1897
1899
|
var JobReporter = class {
|
|
1898
1900
|
constructor(doFetch, base, token) {
|
|
1899
1901
|
this.doFetch = doFetch;
|
|
@@ -1904,32 +1906,43 @@ var JobReporter = class {
|
|
|
1904
1906
|
base;
|
|
1905
1907
|
token;
|
|
1906
1908
|
id;
|
|
1907
|
-
|
|
1909
|
+
/** The run's rows, the job's `total`; `null` while there are none to count. */
|
|
1910
|
+
size = null;
|
|
1911
|
+
/** The first report the platform did not take, for the CLI to say once. */
|
|
1912
|
+
problem;
|
|
1913
|
+
async send(method, route, body) {
|
|
1908
1914
|
try {
|
|
1909
|
-
const res = await this.doFetch(
|
|
1915
|
+
const res = await this.doFetch(`${this.base}/v1/jobs${route}`, {
|
|
1910
1916
|
method,
|
|
1911
1917
|
headers: { "content-type": "application/json", authorization: `Bearer ${this.token}` },
|
|
1912
1918
|
body: JSON.stringify(body),
|
|
1913
1919
|
signal: AbortSignal.timeout(JOB_TIMEOUT_MS)
|
|
1914
1920
|
});
|
|
1915
1921
|
const json = await res.json().catch(() => void 0);
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1922
|
+
if (res.ok) return json ?? {};
|
|
1923
|
+
if (route === "" && (res.status === 404 || res.status === 405)) return void 0;
|
|
1924
|
+
this.problem ??= `HTTP ${res.status}${typeof json?.error === "string" ? ` \u2014 ${json.error}` : ""}`;
|
|
1925
|
+
} catch (err) {
|
|
1926
|
+
this.problem ??= err instanceof Error ? err.message : String(err);
|
|
1919
1927
|
}
|
|
1928
|
+
return void 0;
|
|
1920
1929
|
}
|
|
1921
|
-
async open(subjectName) {
|
|
1922
|
-
|
|
1930
|
+
async open(subjectName, rows) {
|
|
1931
|
+
this.size = Number.isInteger(rows) && rows > 0 ? rows : null;
|
|
1932
|
+
const name = subjectName.trim().slice(0, JOB_SUBJECT_NAME_MAX);
|
|
1933
|
+
const body = await this.send("POST", "", {
|
|
1923
1934
|
kind: JOB_KIND,
|
|
1924
|
-
subjectName,
|
|
1925
|
-
progress: 0
|
|
1935
|
+
...name !== "" ? { subjectName: name } : {},
|
|
1936
|
+
...this.size !== null ? { progress: { done: 0, total: this.size } } : {}
|
|
1926
1937
|
});
|
|
1927
1938
|
if (typeof body?.id === "string") this.id = body.id;
|
|
1928
1939
|
}
|
|
1929
|
-
|
|
1940
|
+
/** The upload is in: every row of the run is on the platform. */
|
|
1941
|
+
async uploaded(cursor) {
|
|
1930
1942
|
if (this.id === void 0) return;
|
|
1931
|
-
await this.send("PATCH",
|
|
1932
|
-
|
|
1943
|
+
await this.send("PATCH", `/${encodeURIComponent(this.id)}/progress`, {
|
|
1944
|
+
done: this.size,
|
|
1945
|
+
total: this.size,
|
|
1933
1946
|
...cursor !== void 0 ? { cursor } : {}
|
|
1934
1947
|
});
|
|
1935
1948
|
}
|
|
@@ -1939,8 +1952,8 @@ var JobReporter = class {
|
|
|
1939
1952
|
this.id = void 0;
|
|
1940
1953
|
await this.send(
|
|
1941
1954
|
"PATCH",
|
|
1942
|
-
|
|
1943
|
-
outcome.ok ? {
|
|
1955
|
+
`/${encodeURIComponent(id)}/state`,
|
|
1956
|
+
outcome.ok ? { state: "completed" } : { state: "failed", reason: outcome.reason.slice(0, JOB_REASON_MAX) }
|
|
1944
1957
|
);
|
|
1945
1958
|
}
|
|
1946
1959
|
};
|
|
@@ -1986,10 +1999,11 @@ async function handleSync(deps = {}) {
|
|
|
1986
1999
|
const idempotencyKey = (0, import_node_crypto2.createHash)("sha256").update(payload).digest("hex");
|
|
1987
2000
|
const bearer = token;
|
|
1988
2001
|
const job = new JobReporter(doFetch, apiUrl, bearer);
|
|
1989
|
-
await job.open(path5.basename(deps.file !== void 0 ? path5.resolve(cwd, deps.file) : cwd));
|
|
2002
|
+
await job.open(path5.basename(deps.file !== void 0 ? path5.resolve(cwd, deps.file) : cwd), run.summary.total);
|
|
1990
2003
|
try {
|
|
1991
2004
|
const result = await upload();
|
|
1992
2005
|
await job.finish({ ok: true });
|
|
2006
|
+
if (job.problem !== void 0) result.jobProblem = job.problem;
|
|
1993
2007
|
return result;
|
|
1994
2008
|
} catch (err) {
|
|
1995
2009
|
await job.finish({ ok: false, reason: err instanceof Error ? err.message : "Sync failed." });
|
|
@@ -2018,7 +2032,7 @@ async function handleSync(deps = {}) {
|
|
|
2018
2032
|
const body = await res.json().catch(() => ({}));
|
|
2019
2033
|
const id = typeof body.id === "string" ? body.id : "";
|
|
2020
2034
|
const result = { id, url: `${endpoint}/${id}` };
|
|
2021
|
-
await job.
|
|
2035
|
+
await job.uploaded(id || void 0);
|
|
2022
2036
|
if (deps.failOnFailures === true) {
|
|
2023
2037
|
result.gate = gateExit(await readVerdict(doFetch, `${endpoint}/${id}/summary`, bearer));
|
|
2024
2038
|
}
|
package/dist/index.d.cts
CHANGED
|
@@ -364,6 +364,8 @@ interface SyncResult {
|
|
|
364
364
|
code: number;
|
|
365
365
|
line: string;
|
|
366
366
|
};
|
|
367
|
+
/** Why the platform's job queue did not take this sync's report: the first refusal or error. The upload went in. */
|
|
368
|
+
jobProblem?: string;
|
|
367
369
|
}
|
|
368
370
|
/**
|
|
369
371
|
* Upload the latest (or `--file`) local run to `POST {apiUrl}/v1/runs` with the stored Bearer token.
|
package/dist/index.d.ts
CHANGED
|
@@ -364,6 +364,8 @@ interface SyncResult {
|
|
|
364
364
|
code: number;
|
|
365
365
|
line: string;
|
|
366
366
|
};
|
|
367
|
+
/** Why the platform's job queue did not take this sync's report: the first refusal or error. The upload went in. */
|
|
368
|
+
jobProblem?: string;
|
|
367
369
|
}
|
|
368
370
|
/**
|
|
369
371
|
* Upload the latest (or `--file`) local run to `POST {apiUrl}/v1/runs` with the stored Bearer token.
|
package/dist/index.js
CHANGED
|
@@ -1832,8 +1832,10 @@ var SyncHttpError = class extends Error {
|
|
|
1832
1832
|
}
|
|
1833
1833
|
status;
|
|
1834
1834
|
};
|
|
1835
|
-
var JOB_KIND = "
|
|
1835
|
+
var JOB_KIND = "sync";
|
|
1836
1836
|
var JOB_TIMEOUT_MS = 5e3;
|
|
1837
|
+
var JOB_SUBJECT_NAME_MAX = 200;
|
|
1838
|
+
var JOB_REASON_MAX = 2e3;
|
|
1837
1839
|
var JobReporter = class {
|
|
1838
1840
|
constructor(doFetch, base, token) {
|
|
1839
1841
|
this.doFetch = doFetch;
|
|
@@ -1844,32 +1846,43 @@ var JobReporter = class {
|
|
|
1844
1846
|
base;
|
|
1845
1847
|
token;
|
|
1846
1848
|
id;
|
|
1847
|
-
|
|
1849
|
+
/** The run's rows, the job's `total`; `null` while there are none to count. */
|
|
1850
|
+
size = null;
|
|
1851
|
+
/** The first report the platform did not take, for the CLI to say once. */
|
|
1852
|
+
problem;
|
|
1853
|
+
async send(method, route, body) {
|
|
1848
1854
|
try {
|
|
1849
|
-
const res = await this.doFetch(
|
|
1855
|
+
const res = await this.doFetch(`${this.base}/v1/jobs${route}`, {
|
|
1850
1856
|
method,
|
|
1851
1857
|
headers: { "content-type": "application/json", authorization: `Bearer ${this.token}` },
|
|
1852
1858
|
body: JSON.stringify(body),
|
|
1853
1859
|
signal: AbortSignal.timeout(JOB_TIMEOUT_MS)
|
|
1854
1860
|
});
|
|
1855
1861
|
const json = await res.json().catch(() => void 0);
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1862
|
+
if (res.ok) return json ?? {};
|
|
1863
|
+
if (route === "" && (res.status === 404 || res.status === 405)) return void 0;
|
|
1864
|
+
this.problem ??= `HTTP ${res.status}${typeof json?.error === "string" ? ` \u2014 ${json.error}` : ""}`;
|
|
1865
|
+
} catch (err) {
|
|
1866
|
+
this.problem ??= err instanceof Error ? err.message : String(err);
|
|
1859
1867
|
}
|
|
1868
|
+
return void 0;
|
|
1860
1869
|
}
|
|
1861
|
-
async open(subjectName) {
|
|
1862
|
-
|
|
1870
|
+
async open(subjectName, rows) {
|
|
1871
|
+
this.size = Number.isInteger(rows) && rows > 0 ? rows : null;
|
|
1872
|
+
const name = subjectName.trim().slice(0, JOB_SUBJECT_NAME_MAX);
|
|
1873
|
+
const body = await this.send("POST", "", {
|
|
1863
1874
|
kind: JOB_KIND,
|
|
1864
|
-
subjectName,
|
|
1865
|
-
progress: 0
|
|
1875
|
+
...name !== "" ? { subjectName: name } : {},
|
|
1876
|
+
...this.size !== null ? { progress: { done: 0, total: this.size } } : {}
|
|
1866
1877
|
});
|
|
1867
1878
|
if (typeof body?.id === "string") this.id = body.id;
|
|
1868
1879
|
}
|
|
1869
|
-
|
|
1880
|
+
/** The upload is in: every row of the run is on the platform. */
|
|
1881
|
+
async uploaded(cursor) {
|
|
1870
1882
|
if (this.id === void 0) return;
|
|
1871
|
-
await this.send("PATCH",
|
|
1872
|
-
|
|
1883
|
+
await this.send("PATCH", `/${encodeURIComponent(this.id)}/progress`, {
|
|
1884
|
+
done: this.size,
|
|
1885
|
+
total: this.size,
|
|
1873
1886
|
...cursor !== void 0 ? { cursor } : {}
|
|
1874
1887
|
});
|
|
1875
1888
|
}
|
|
@@ -1879,8 +1892,8 @@ var JobReporter = class {
|
|
|
1879
1892
|
this.id = void 0;
|
|
1880
1893
|
await this.send(
|
|
1881
1894
|
"PATCH",
|
|
1882
|
-
|
|
1883
|
-
outcome.ok ? {
|
|
1895
|
+
`/${encodeURIComponent(id)}/state`,
|
|
1896
|
+
outcome.ok ? { state: "completed" } : { state: "failed", reason: outcome.reason.slice(0, JOB_REASON_MAX) }
|
|
1884
1897
|
);
|
|
1885
1898
|
}
|
|
1886
1899
|
};
|
|
@@ -1926,10 +1939,11 @@ async function handleSync(deps = {}) {
|
|
|
1926
1939
|
const idempotencyKey = createHash2("sha256").update(payload).digest("hex");
|
|
1927
1940
|
const bearer = token;
|
|
1928
1941
|
const job = new JobReporter(doFetch, apiUrl, bearer);
|
|
1929
|
-
await job.open(path5.basename(deps.file !== void 0 ? path5.resolve(cwd, deps.file) : cwd));
|
|
1942
|
+
await job.open(path5.basename(deps.file !== void 0 ? path5.resolve(cwd, deps.file) : cwd), run.summary.total);
|
|
1930
1943
|
try {
|
|
1931
1944
|
const result = await upload();
|
|
1932
1945
|
await job.finish({ ok: true });
|
|
1946
|
+
if (job.problem !== void 0) result.jobProblem = job.problem;
|
|
1933
1947
|
return result;
|
|
1934
1948
|
} catch (err) {
|
|
1935
1949
|
await job.finish({ ok: false, reason: err instanceof Error ? err.message : "Sync failed." });
|
|
@@ -1958,7 +1972,7 @@ async function handleSync(deps = {}) {
|
|
|
1958
1972
|
const body = await res.json().catch(() => ({}));
|
|
1959
1973
|
const id = typeof body.id === "string" ? body.id : "";
|
|
1960
1974
|
const result = { id, url: `${endpoint}/${id}` };
|
|
1961
|
-
await job.
|
|
1975
|
+
await job.uploaded(id || void 0);
|
|
1962
1976
|
if (deps.failOnFailures === true) {
|
|
1963
1977
|
result.gate = gateExit(await readVerdict(doFetch, `${endpoint}/${id}/summary`, bearer));
|
|
1964
1978
|
}
|
package/dist/reporter-core.cjs
CHANGED
|
@@ -875,7 +875,7 @@ async function startRun(passed, declared) {
|
|
|
875
875
|
...cfg.environment !== void 0 ? { environment: cfg.environment } : {},
|
|
876
876
|
...cfg.labels !== void 0 ? { labels: cfg.labels } : {}
|
|
877
877
|
};
|
|
878
|
-
const
|
|
878
|
+
const request = {
|
|
879
879
|
schemaVersion: 2,
|
|
880
880
|
kind: cfg.kind ?? "automated",
|
|
881
881
|
...externalKey !== null ? { externalKey } : {},
|
|
@@ -886,7 +886,14 @@ async function startRun(passed, declared) {
|
|
|
886
886
|
// a person will miss, and a deployment older than the field must not be nagged on every run.
|
|
887
887
|
title: defaultRunTitle(),
|
|
888
888
|
...described
|
|
889
|
-
}
|
|
889
|
+
};
|
|
890
|
+
let start = await client.post("/v1/runs", request);
|
|
891
|
+
if (!start.ok && start.status === 413 && configuration !== void 0) {
|
|
892
|
+
log(
|
|
893
|
+
`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.`
|
|
894
|
+
);
|
|
895
|
+
start = await client.post("/v1/runs", { ...request, configuration: void 0 });
|
|
896
|
+
}
|
|
890
897
|
if (start.ok && typeof start.body?.run?.id === "string") {
|
|
891
898
|
runId = start.body.run.id;
|
|
892
899
|
joined = start.status === 200;
|
package/dist/reporter-core.js
CHANGED
|
@@ -825,7 +825,7 @@ async function startRun(passed, declared) {
|
|
|
825
825
|
...cfg.environment !== void 0 ? { environment: cfg.environment } : {},
|
|
826
826
|
...cfg.labels !== void 0 ? { labels: cfg.labels } : {}
|
|
827
827
|
};
|
|
828
|
-
const
|
|
828
|
+
const request = {
|
|
829
829
|
schemaVersion: 2,
|
|
830
830
|
kind: cfg.kind ?? "automated",
|
|
831
831
|
...externalKey !== null ? { externalKey } : {},
|
|
@@ -836,7 +836,14 @@ async function startRun(passed, declared) {
|
|
|
836
836
|
// a person will miss, and a deployment older than the field must not be nagged on every run.
|
|
837
837
|
title: defaultRunTitle(),
|
|
838
838
|
...described
|
|
839
|
-
}
|
|
839
|
+
};
|
|
840
|
+
let start = await client.post("/v1/runs", request);
|
|
841
|
+
if (!start.ok && start.status === 413 && configuration !== void 0) {
|
|
842
|
+
log(
|
|
843
|
+
`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
|
+
);
|
|
845
|
+
start = await client.post("/v1/runs", { ...request, configuration: void 0 });
|
|
846
|
+
}
|
|
840
847
|
if (start.ok && typeof start.body?.run?.id === "string") {
|
|
841
848
|
runId = start.body.run.id;
|
|
842
849
|
joined = start.status === 200;
|
package/package.json
CHANGED