@plune-ai/cli 0.23.0 → 0.24.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 +1 -1
- package/dist/cli.cjs +14 -4
- package/dist/index.cjs +9 -2
- package/dist/index.js +9 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -119,7 +119,7 @@ the last resort, used only when `PLUNE_TOKEN` is set nowhere else. Nothing write
|
|
|
119
119
|
| `plune init` | Scaffold `plune.yaml`, a sample dataset, and `.env.example`. Flags: `--yes` (non-interactive), `--force`. |
|
|
120
120
|
| `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. |
|
|
121
121
|
| `plune logout` | Remove the saved token. |
|
|
122
|
-
| `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. |
|
|
122
|
+
| `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. |
|
|
123
123
|
| `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), `--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 4](#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. |
|
|
124
124
|
| `plune run start` | Open a platform run — or join the one already carrying `--key` — and print its id. Flags: `--key <externalKey>` (generated when absent), `--json` for one machine-readable line. |
|
|
125
125
|
| `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
|
@@ -4086,12 +4086,18 @@ async function handleSync(deps = {}) {
|
|
|
4086
4086
|
const run = readRun(cwd, deps.file);
|
|
4087
4087
|
const doFetch = deps.fetchImpl ?? fetch;
|
|
4088
4088
|
const endpoint = `${apiUrl}/v1/runs`;
|
|
4089
|
+
const payload = JSON.stringify(run);
|
|
4090
|
+
const idempotencyKey = (0, import_node_crypto4.createHash)("sha256").update(payload).digest("hex");
|
|
4089
4091
|
let res;
|
|
4090
4092
|
try {
|
|
4091
4093
|
res = await doFetch(endpoint, {
|
|
4092
4094
|
method: "POST",
|
|
4093
|
-
headers: {
|
|
4094
|
-
|
|
4095
|
+
headers: {
|
|
4096
|
+
"content-type": "application/json",
|
|
4097
|
+
authorization: `Bearer ${token}`,
|
|
4098
|
+
"idempotency-key": idempotencyKey
|
|
4099
|
+
},
|
|
4100
|
+
body: payload
|
|
4095
4101
|
});
|
|
4096
4102
|
} catch {
|
|
4097
4103
|
throw new SyncNetworkError(apiUrl);
|
|
@@ -4127,11 +4133,12 @@ function reportSyncFailure(err, write) {
|
|
|
4127
4133
|
}
|
|
4128
4134
|
return null;
|
|
4129
4135
|
}
|
|
4130
|
-
var fs8, path9, NotLoggedInError, TokenRejectedError, SyncFileError, SyncNetworkError, SyncHttpError;
|
|
4136
|
+
var import_node_crypto4, fs8, path9, NotLoggedInError, TokenRejectedError, SyncFileError, SyncNetworkError, SyncHttpError;
|
|
4131
4137
|
var init_sync = __esm({
|
|
4132
4138
|
"src/cli/commands/sync.ts"() {
|
|
4133
4139
|
"use strict";
|
|
4134
4140
|
init_cjs_shims();
|
|
4141
|
+
import_node_crypto4 = require("crypto");
|
|
4135
4142
|
fs8 = __toESM(require("fs"), 1);
|
|
4136
4143
|
path9 = __toESM(require("path"), 1);
|
|
4137
4144
|
init_run_result_schema();
|
|
@@ -5363,7 +5370,10 @@ function describe(result, apiUrl) {
|
|
|
5363
5370
|
);
|
|
5364
5371
|
}
|
|
5365
5372
|
if (result.offered > 0) {
|
|
5366
|
-
|
|
5373
|
+
const source = result.format === "playwright-json" ? "playwright" : result.format;
|
|
5374
|
+
lines.push(
|
|
5375
|
+
`${result.offered} unknown test(s) offered to the review queue. They wait there for a decision and do not count on the run until approved. To skip review for this source, add \`${source}\` under Settings \u2192 Trusted sources.`
|
|
5376
|
+
);
|
|
5367
5377
|
} else if (result.unresolved > 0 && result.created === 0 && !result.offering) {
|
|
5368
5378
|
lines.push("Nothing was offered to the review queue \u2014 pass --create to propose the unmatched tests.");
|
|
5369
5379
|
} else if (result.unresolved > 0 && result.created === 0 && result.unoffered === 0) {
|
package/dist/index.cjs
CHANGED
|
@@ -1610,6 +1610,7 @@ async function handleRun(options, depsFactory) {
|
|
|
1610
1610
|
}
|
|
1611
1611
|
|
|
1612
1612
|
// src/cli/commands/sync.ts
|
|
1613
|
+
var import_node_crypto2 = require("crypto");
|
|
1613
1614
|
var fs6 = __toESM(require("fs"), 1);
|
|
1614
1615
|
var path5 = __toESM(require("path"), 1);
|
|
1615
1616
|
|
|
@@ -1794,12 +1795,18 @@ async function handleSync(deps = {}) {
|
|
|
1794
1795
|
const run = readRun(cwd, deps.file);
|
|
1795
1796
|
const doFetch = deps.fetchImpl ?? fetch;
|
|
1796
1797
|
const endpoint = `${apiUrl}/v1/runs`;
|
|
1798
|
+
const payload = JSON.stringify(run);
|
|
1799
|
+
const idempotencyKey = (0, import_node_crypto2.createHash)("sha256").update(payload).digest("hex");
|
|
1797
1800
|
let res;
|
|
1798
1801
|
try {
|
|
1799
1802
|
res = await doFetch(endpoint, {
|
|
1800
1803
|
method: "POST",
|
|
1801
|
-
headers: {
|
|
1802
|
-
|
|
1804
|
+
headers: {
|
|
1805
|
+
"content-type": "application/json",
|
|
1806
|
+
authorization: `Bearer ${token}`,
|
|
1807
|
+
"idempotency-key": idempotencyKey
|
|
1808
|
+
},
|
|
1809
|
+
body: payload
|
|
1803
1810
|
});
|
|
1804
1811
|
} catch {
|
|
1805
1812
|
throw new SyncNetworkError(apiUrl);
|
package/dist/index.js
CHANGED
|
@@ -1554,6 +1554,7 @@ async function handleRun(options, depsFactory) {
|
|
|
1554
1554
|
}
|
|
1555
1555
|
|
|
1556
1556
|
// src/cli/commands/sync.ts
|
|
1557
|
+
import { createHash as createHash2 } from "crypto";
|
|
1557
1558
|
import * as fs6 from "fs";
|
|
1558
1559
|
import * as path5 from "path";
|
|
1559
1560
|
|
|
@@ -1738,12 +1739,18 @@ async function handleSync(deps = {}) {
|
|
|
1738
1739
|
const run = readRun(cwd, deps.file);
|
|
1739
1740
|
const doFetch = deps.fetchImpl ?? fetch;
|
|
1740
1741
|
const endpoint = `${apiUrl}/v1/runs`;
|
|
1742
|
+
const payload = JSON.stringify(run);
|
|
1743
|
+
const idempotencyKey = createHash2("sha256").update(payload).digest("hex");
|
|
1741
1744
|
let res;
|
|
1742
1745
|
try {
|
|
1743
1746
|
res = await doFetch(endpoint, {
|
|
1744
1747
|
method: "POST",
|
|
1745
|
-
headers: {
|
|
1746
|
-
|
|
1748
|
+
headers: {
|
|
1749
|
+
"content-type": "application/json",
|
|
1750
|
+
authorization: `Bearer ${token}`,
|
|
1751
|
+
"idempotency-key": idempotencyKey
|
|
1752
|
+
},
|
|
1753
|
+
body: payload
|
|
1747
1754
|
});
|
|
1748
1755
|
} catch {
|
|
1749
1756
|
throw new SyncNetworkError(apiUrl);
|
package/package.json
CHANGED