@plune-ai/cli 0.23.0 → 0.25.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 +68 -8
- package/dist/cli.cjs +418 -61
- package/dist/index.cjs +9 -2
- package/dist/index.js +9 -2
- package/dist/reporter-core.cjs +95 -10
- package/dist/reporter-core.d.cts +22 -3
- package/dist/reporter-core.d.ts +22 -3
- package/dist/reporter-core.js +95 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -119,8 +119,8 @@ 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. |
|
|
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
|
|
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
|
+
| `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 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. |
|
|
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. |
|
|
126
126
|
| `plune run exec -- <command>` | Open a run, run the command inside it, close the run — and exit with whatever the command returned. Sets `PLUNE_SHARED_RUN` for you, so anything reporting inside joins that run. Flags: `--key <externalKey>`, `--fail-on-failures` to exit with [the gate](#a-release-gate---fail-on-failures)'s code instead — or with the command's own when no result reached the run. |
|
|
@@ -129,6 +129,7 @@ the last resort, used only when `PLUNE_TOKEN` is set nowhere else. Nothing write
|
|
|
129
129
|
| `plune ingest [dir]` | Record a [Cairn](https://github.com/plune-ai/cairn) run in Plune. Omit `[dir]` for the newest run under `./runs`, or name the directory holding `report.json`. Generated cases arrive as **review proposals** — nothing is created until a person approves it. |
|
|
130
130
|
| `plune pull [file]` | Write the project's test cases as **one Markdown document** — Testomat's classical format, plus Plune's own columns — to `plune/cases.md` (or `[file]`). Refuses to overwrite a file git sees as modified; `--force` overrides, `--suite <id>` takes one suite or folder and what is under it. |
|
|
131
131
|
| `plune push [file] [--dry-run]` | Send the document back. Cases are matched by `id`; a block without one becomes a draft; an unknown id is refused by line; **nothing is deleted**. Prints the report — created, updated, unchanged, refused, warnings — and with `--dry-run` writes nothing. Exit `4` when anything was refused. |
|
|
132
|
+
| `plune requirements import <file.md>` | Import requirements from a **Markdown file** (an SRS, a PRD, a page exported from a wiki). With no mapping flags it lists the tables and sections the file has and exits `0`; with `--table` or `--sections` it is a **dry run** that writes nothing until you add `--yes`. See [Requirements from a Markdown file](#requirements-from-a-markdown-file). Needs a Plune platform that serves `POST /v1/projects/:id/requirements/import` (plune-ai/plune#1621). |
|
|
132
133
|
| `plune plan grep <id>` | Print a [test plan](https://docs.plune.ai/platform/plans/) as **one `--grep` pattern** — `npx playwright test --grep "$(plune plan grep <id>)"` or `npx vitest run -t "$(plune plan grep <id>)"` runs exactly what the plan collects. Each case contributes its title path from the `path-title` key the reporter wrote (the file left out, `[ >#]+` between the segments — a space for Playwright, jest, mocha and vitest ≤ 4, ` > ` for vitest 5), escaped, joined with `\|`; a case without one goes in by title. The pattern is all that reaches stdout; the summary — how many cases, how many keyed — goes to stderr. An empty plan prints `(?!)`, which matches nothing, because an empty `--grep` would run everything. |
|
|
133
134
|
| `plune run grep <run> [--failed]` | Print a run's **automated** cases as one `--grep` pattern, built the way `plan grep` builds a plan's — so the commands Plune shows for a run work as they are: `npx playwright test --grep "$(plune run grep <run-id> --failed)"` re-runs an automated run's failures where your tests run (Plune starts no CI), and `PLUNE_RUN=<key> npx playwright test --grep "$(plune run grep <key>)"` runs the automated half of a mixed run launched without a plan. `<run>` is the run's id (a UUID) or its key (`rk_` and ten hex); anything else is refused with exit `2` before anything is sent, and a key no run carries exits `1`. The cases are the ones the run expected — cases added on its page among them, run or not — or holds a result of; manual cases are left out. `--failed` keeps the cases whose latest result the platform counts as failed or broken, so a muted case's failure stays out, as from **Re-run N failed**. Only the pattern reaches stdout; how many cases were taken and how many left out goes to stderr; nothing taken prints `(?!)`. Needs a Plune that serves `GET /v1/runs/:id/cases` (plune-ai/plune#1333). |
|
|
134
135
|
|
|
@@ -136,6 +137,38 @@ Global flags: `-c, --config <path>` · `-v, --verbose` · `--no-color`.
|
|
|
136
137
|
|
|
137
138
|
**Exit codes:** `0` everything passed · `1` an assertion failed · `2` configuration or execution error · `3` `pull` refused to overwrite uncommitted changes · `4` `push` had refusals.
|
|
138
139
|
|
|
140
|
+
### Requirements from a Markdown file
|
|
141
|
+
|
|
142
|
+
`plune requirements import <file.md>` reads a requirements document (UTF-8, at most 1 MiB, at most 2000 requirements) and brings it into the project in two steps. It sends the file as it is and changes nothing by itself: you say which table or section holds the requirements and which column is which, and the platform tells you what it would do before it does it.
|
|
143
|
+
|
|
144
|
+
**1. See what the file holds.** With no mapping flags:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
plune requirements import srs.md
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
It lists every table (number, nearest heading, header cells, row count, and `<- looks like requirements` where the first column holds ids and a header names a requirement) and every section (number, heading, bullets and sub-headings under it), then the commands to try next. Exit `0`.
|
|
151
|
+
|
|
152
|
+
**2a. A table.** Name the table and its columns by their header text (any case):
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
plune requirements import srs.md --table 0 --key-column ID --text-column Requirement --priority-column Priority --map-priority "Critical=Must-have,High=Should-have"
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
`--key-column`, `--title-column` or `--text-column` (one of the two: the title is the first line of the text and the whole text is kept as the description), `--description-column`, `--priority-column`, `--category-column`, `--type-column`, `--status-column` (`draft`, `active` or `done`), `--criteria-column`; `--map-priority` and `--map-category` (`"Quality=non-functional"`) rename values; `--type user_story|feature|epic` is the type of a row that has none.
|
|
159
|
+
|
|
160
|
+
**2b. A section.** One requirement per bullet or per sub-heading:
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
plune requirements import prd.md --sections Requirements --unit bullets --key-prefix REQ-
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
`--sections` takes the heading or its number; `--unit bullets|subheadings` (default: sub-headings when the section has any); `--key-prefix` is text a bullet starts with that carries its key (`REQ-12: The system shall ...`; no pattern is accepted); `--priority-from subheading` makes the bullets under a sub-heading take its text as their priority.
|
|
167
|
+
|
|
168
|
+
**3. Read the plan, then write it.** Without `--yes` nothing is written. The report is the summary line (`create 3 · update 0 · unchanged 12 · skip 1`), the rows grouped by action with their line in the file, key, title and reason (a skipped row says why), the priority and category values seen and what they map to, and what was **not imported** (other tables, other sections, frontmatter keys). It ends with `dry run — nothing was written; add --yes to apply`. Add `--yes` to apply the same plan in one transaction. A row with a key matches the requirement of that key; one without matches by exact title; running the same file again changes nothing (every row is `unchanged`). A skipped row does not fail the command.
|
|
169
|
+
|
|
170
|
+
`--json` prints the platform's answer instead. The project is the only one of the account, or `--project <id>` / `PLUNE_PROJECT` (a token only works in the project it was minted in). Exit `0` on success, `2` for a flag combination that cannot work (`--table` with `--sections`, `--title-column` with `--text-column`), an unreadable or over-limit file, or a rejected token, `1` when the platform refuses (400, 403, 409 a key taken meanwhile, 413) or does not have the routes yet.
|
|
171
|
+
|
|
139
172
|
### A release gate: `--fail-on-failures`
|
|
140
173
|
|
|
141
174
|
`plune run finish`, `plune run exec`, `plune run import` and `plune sync` take `--fail-on-failures`. The exit
|
|
@@ -193,7 +226,7 @@ bytes, up to 512 KiB each and 10 per result. The summary counts them apart from
|
|
|
193
226
|
A project that has connected its own storage bucket in Plune takes more. Every file an attempt kept
|
|
194
227
|
— screenshots, text, a Playwright trace, a video — goes straight from this machine into that bucket
|
|
195
228
|
over short-lived links Plune hands out per file, so a trace of up to 100 MB is streamed from disk,
|
|
196
|
-
never read into memory. Up to
|
|
229
|
+
never read into memory. Up to 16 files go at once (`PLUNE_UPLOAD_CONCURRENCY`). The limits there are larger: 10 MiB for an
|
|
197
230
|
image, 5 MiB for a text, 50 MiB for any other file and 100 MiB for a video or a trace, 30 files a
|
|
198
231
|
result. The links are write keys to your bucket, so they are never printed — not in the log, not with
|
|
199
232
|
`--verbose`, not in an error; a line names the file and a code. The first time a run finds the
|
|
@@ -322,6 +355,32 @@ job closes the run instead of the explicit `finish`.
|
|
|
322
355
|
`plune run exec` sets it for you — anything reporting inside it, this command included, joins
|
|
323
356
|
without closing.
|
|
324
357
|
|
|
358
|
+
#### Closing a sharded run by itself: `PLUNE_SHARD`
|
|
359
|
+
|
|
360
|
+
When you know how many shards there are, let them close the run instead of a final `finish` step:
|
|
361
|
+
|
|
362
|
+
```bash
|
|
363
|
+
PLUNE_SHARD=2/4 plune run import ./junit.xml --key "$GITHUB_RUN_ID-$GITHUB_RUN_ATTEMPT"
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
`PLUNE_SHARD=k/N` says "this job is shard k of N" (k counts from 1, `1 <= k <= N <= 1000`; a Playwright
|
|
367
|
+
`--shard=2/4` user passes the same string). It implies `PLUNE_SHARED_RUN`: the job uploads its results, then tells
|
|
368
|
+
Plune that shard k is done. The run closes once, when N different shards have said so, in whatever order and however
|
|
369
|
+
often a shard is retried (a repeat counts once; a red shard counts too). The job's exit code does not depend on the counts.
|
|
370
|
+
|
|
371
|
+
```
|
|
372
|
+
plune: shard 2 of 4 done — 2 of 4 shards reported
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
The shard that is the Nth also prints `plune: run closed` just above that line, so the `shard ... done` line is always the last one.
|
|
376
|
+
|
|
377
|
+
- A value that is not `k/N` (`0/4`, `5/4`, `2`, `a/b`) is a configuration error: nothing is sent and the command fails.
|
|
378
|
+
Every shard of a run must use the same N; a different one fails with a message naming both numbers, and the run is left as it was.
|
|
379
|
+
- The index is not guessed from CI variables (matrix indexes are 0-based on some runners and 1-based on others); set it.
|
|
380
|
+
- On a platform that does not support it yet, the job warns once (`the platform does not support shard auto-close yet`)
|
|
381
|
+
and leaves the run open, as `PLUNE_SHARED_RUN` does: close it with `plune run finish`.
|
|
382
|
+
- With `--fail-on-failures`, the shard that closes the run gates on its summary; the others have no verdict yet.
|
|
383
|
+
|
|
325
384
|
### Many screenshots: upload concurrency and retries
|
|
326
385
|
|
|
327
386
|
Files go to Plune up to 100 to a request: `uploads-batch` announces them (with the SHA-256 of each, read as a stream,
|
|
@@ -329,18 +388,19 @@ so a file the platform already has is answered `exists` and is not sent again),
|
|
|
329
388
|
(or, for a project without one, 100 files or 6 MiB at a time through `direct-batch`), and `complete-batch` confirms
|
|
330
389
|
them. Every file has its own verdict: one that is refused is reported and the rest of its request goes through. A
|
|
331
390
|
report of 2760 screenshots is about 56 requests to Plune instead of 5500. The bytes themselves still go one request each,
|
|
332
|
-
so the PUTs to a bucket are bound by the round trip, not by bandwidth:
|
|
333
|
-
with a
|
|
391
|
+
so the PUTs to a bucket are bound by the round trip, not by bandwidth: 16 at a time by default, lower it on a runner
|
|
392
|
+
with a fragile link or a bucket that throttles:
|
|
334
393
|
|
|
335
394
|
```bash
|
|
336
|
-
PLUNE_UPLOAD_CONCURRENCY=
|
|
337
|
-
plune run import ./playwright.json --upload-concurrency
|
|
395
|
+
PLUNE_UPLOAD_CONCURRENCY=4 plune run import ./playwright.json # 1 to 32, default 16
|
|
396
|
+
plune run import ./playwright.json --upload-concurrency 4 # the same, as a flag (it wins)
|
|
338
397
|
```
|
|
339
398
|
|
|
340
399
|
When the platform answers 429 with a `Retry-After` (seconds or an HTTP date, never waited longer than 60 s),
|
|
341
400
|
the request is tried again after that time plus up to half as much, and every other request of the import
|
|
342
401
|
waits through the same pause. `PLUNE_UPLOAD_RETRIES` (0 to 20, default 6) says how many times; a 429 that
|
|
343
|
-
|
|
402
|
+
carries a `quota` object in its body is the quota and is not retried, with or without `Retry-After` (the message names the
|
|
403
|
+
limit and when it resets); one with neither the object nor a `Retry-After` is read the same way. A platform that is down (a 5xx or no answer) is still tried
|
|
344
404
|
twice (three times for a bucket). A 429 on a whole request waits the same way; a file that the bucket did not
|
|
345
405
|
answer for at `complete` is confirmed again, on its own. A file that stays refused is reported without it, never fails
|
|
346
406
|
the import, and is counted in the line printed before the summary:
|