allure 3.20.1 → 3.22.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.
Files changed (48) hide show
  1. package/README.md +64 -3
  2. package/dist/commands/{agent.js → agents/index.js} +13 -109
  3. package/dist/commands/agents/lifecycle.d.ts +20 -0
  4. package/dist/commands/agents/lifecycle.js +91 -0
  5. package/dist/commands/{agent-run.d.ts → agents/run.d.ts} +1 -13
  6. package/dist/commands/{agent-run.js → agents/run.js} +12 -67
  7. package/dist/commands/commons/generate.d.ts +1 -0
  8. package/dist/commands/commons/generate.js +28 -2
  9. package/dist/commands/generate.d.ts +2 -0
  10. package/dist/commands/generate.js +19 -1
  11. package/dist/commands/ghWorkflow.d.ts +11 -0
  12. package/dist/commands/ghWorkflow.js +77 -0
  13. package/dist/commands/index.d.ts +4 -1
  14. package/dist/commands/index.js +4 -1
  15. package/dist/commands/jira/clear.js +3 -3
  16. package/dist/commands/migrate.d.ts +6 -0
  17. package/dist/commands/migrate.js +15 -0
  18. package/dist/commands/open.d.ts +3 -10
  19. package/dist/commands/open.js +20 -99
  20. package/dist/commands/qualityGate.js +11 -1
  21. package/dist/commands/run.d.ts +4 -0
  22. package/dist/commands/run.js +27 -5
  23. package/dist/commands/serve.d.ts +19 -0
  24. package/dist/commands/serve.js +168 -0
  25. package/dist/commands/slack.d.ts +3 -2
  26. package/dist/commands/slack.js +17 -5
  27. package/dist/commands/testopsPlan.js +4 -4
  28. package/dist/commands/watch.d.ts +1 -0
  29. package/dist/commands/watch.js +10 -1
  30. package/dist/index.js +32 -9
  31. package/dist/utils/allure2Hints.d.ts +8 -0
  32. package/dist/utils/allure2Hints.js +90 -0
  33. package/dist/utils/cleanOutput.d.ts +9 -0
  34. package/dist/utils/cleanOutput.js +62 -0
  35. package/dist/utils/fileSystem.d.ts +4 -1
  36. package/dist/utils/fileSystem.js +3 -3
  37. package/dist/utils/ghWorkflow.d.ts +10 -0
  38. package/dist/utils/ghWorkflow.js +75 -0
  39. package/dist/utils/serverOptions.d.ts +5 -0
  40. package/dist/utils/serverOptions.js +4 -0
  41. package/dist/utils/updateNotifier.d.ts +6 -0
  42. package/dist/utils/updateNotifier.js +58 -0
  43. package/dist/utils/verbosity.d.ts +7 -0
  44. package/dist/utils/verbosity.js +32 -0
  45. package/package.json +35 -28
  46. /package/dist/commands/{agent-human-report.d.ts → agents/human-report.d.ts} +0 -0
  47. /package/dist/commands/{agent-human-report.js → agents/human-report.js} +0 -0
  48. /package/dist/commands/{agent.d.ts → agents/index.d.ts} +0 -0
package/README.md CHANGED
@@ -73,6 +73,22 @@ Set `ALLURE_LOG_LEVEL=debug` to include details such as selected tests and tempo
73
73
  the generic `LOG_LEVEL` environment variable. Use `--silent` to hide the nested test process output while keeping
74
74
  Allure lifecycle messages visible.
75
75
 
76
+ ### Verbosity flags
77
+
78
+ `-v/--verbose` and `-q/--quiet` are global flags accepted by every command (place them before `--` in `allure run`;
79
+ everything after `--` is passed to your test command untouched).
80
+
81
+ | Flag | Allure log level | `allure run` test process output |
82
+ | --- | --- | --- |
83
+ | _(none)_ | `ALLURE_LOG_LEVEL`, default `info` | printed |
84
+ | `--verbose`, `-v` | `verbose` | printed |
85
+ | `--quiet`, `-q` | `error` (errors only) | hidden, same as `--silent` |
86
+
87
+ - The flags override `ALLURE_LOG_LEVEL`. `--verbose` and `--quiet` cannot be combined.
88
+ - `--quiet` implies `--silent`. Logs are still collected into the report unless `--ignore-logs` is set.
89
+ - `--silent` and `--ignore-logs` keep their meaning and are independent of `--verbose`.
90
+ - A lone `allure -v` still prints the version.
91
+
76
92
  ### Running Tests In Agent Mode
77
93
 
78
94
  When you need agent-friendly markdown output for review, debugging, or scope validation, use the `agent` command:
@@ -103,7 +119,7 @@ option writes the agentic output directory.
103
119
 
104
120
  `allure agent` and `allure agent inspect` use `--report auto` by default. This writes the agent-readable artifacts and, when the stored visible result count is 1000 or fewer, also writes a single-file Awesome report at `awesome/index.html` inside the agent output directory. Runs above that threshold skip the human report to avoid excessive output, and the status is recorded in `index.md`, `manifest/run.json`, and `manifest/human-report.json`.
105
121
 
106
- Use `--report off` for agent-only artifacts, `--report awesome` to force the single-file Awesome report regardless of result count, or `--report config` to force the configured non-agent report plugins inside the agent output directory. Configured presentation or export plugins such as Dashboard or TestOps are otherwise ignored for agent runs.
122
+ Use `--report off` for agent-only artifacts, `--report awesome` to force the single-file Awesome report regardless of result count, or `--report config` to force the configured non-agent report plugins inside the agent output directory. Configured presentation or export plugins such as Dashboard or Allure TestOps are otherwise ignored for agent runs.
107
123
 
108
124
  If you need the human-readable report from the most recent agent run, first run `npx allure agent latest` when the output directory is unknown. Then check `<output>/manifest/human-report.json`; when its status is `generated`, open `<output>/<path>` from that manifest, usually `<output>/awesome/index.html`.
109
125
 
@@ -123,6 +139,23 @@ npx allure agent query --latest tests --status failed
123
139
  npx allure agent query --from ./agent-output findings --severity high
124
140
  ```
125
141
 
142
+ ### Generating GitHub Actions Jobs
143
+
144
+ Print GitHub Actions jobs that run your tests once per environment and build a single Allure report from all of them:
145
+
146
+ ```bash
147
+ npx allure gh-workflow --env foo --env bar --env baz -- npm test
148
+ ```
149
+
150
+ Paste the output under `jobs:` in a workflow file and fill in the `TODO` steps (runtime setup and dependencies). The output has two jobs:
151
+
152
+ - `test` runs `allure run --environment=<id> --dump=allure-results-<id> -- <test command>` for every environment in a matrix and uploads `allure-results-<id>.zip` as an artifact;
153
+ - `report` downloads all `allure-results-*` artifacts and runs `allure generate --dump="allure-results-*/*.zip"`.
154
+
155
+ The environment id, the dump file, the artifact name and the download pattern have to agree with each other; the command keeps them consistent.
156
+
157
+ Environments come from `--env` (repeatable, no config needed). Without `--env`, the ids declared under `environments` in your Allure config are used (integer-like ids come first, as JavaScript orders object keys). Pass the test command after `--` (default: `npm test`); `--config` is also added to the generated `allure` commands. The versions of the GitHub actions in the output are examples, pin the ones you use. Errors are written to stderr with a non-zero exit code, while command-line syntax errors are reported by the CLI parser on stdout.
158
+
126
159
  ### Generating Reports Manually
127
160
 
128
161
  If you already have test results and wish to generate a report manually, use the `generate` command:
@@ -149,6 +182,26 @@ npx allure open allure-report/awesome
149
182
 
150
183
  If the provided directory doesn't contain previously generated report, it will be treated as a source of results, and a new report will be generated on the fly and opened in the browser.
151
184
 
185
+ To stop serving the report, press CTRL+C.
186
+
187
+ #### Serving a report without opening
188
+
189
+ Use the `serve` command to start a local report server without opening a browser:
190
+
191
+ ```bash
192
+ npx allure serve <reportDir>
193
+ ```
194
+
195
+ Like `open`, this command uses the configured output directory when `<reportDir>` is omitted and generates a report on the fly when given test results. Open the URL printed in the console to view the report.
196
+
197
+ Use `--host` and `--port` to control the interfaces and port to listen:
198
+
199
+ ```bash
200
+ npx allure serve --port 8080 --host 127.0.0.1
201
+ ```
202
+
203
+ Press CTRL+C to stop serving.
204
+
152
205
  ### Real-time Report Monitoring
153
206
 
154
207
  When you need to monitor test execution live, the `watch` command provides dynamic report updates. It continuously monitors the specified results directory for changes and refreshes the report automatically:
@@ -176,12 +229,20 @@ To check your installed version of Allure 3, use:
176
229
  npx allure --version
177
230
  ```
178
231
 
232
+ For direct interactive CLI invocations, Allure checks the npm registry for a newer version alongside the command.
233
+ Each eligible invocation makes one request with a one-second timeout; no update state is stored on disk. Missing
234
+ package metadata and network errors are silently ignored. The check is skipped for `allure agent`, help and version
235
+ output, quiet mode, CI, non-interactive terminals, and invocations through npm or Yarn. Disable it with `--no-update-notifier`
236
+ (for example, `allure --no-update-notifier generate ./allure-results`), or set `NO_UPDATE_NOTIFIER` for the environment.
237
+ Numbered prerelease versions (such as `3.21.0-beta.2`) check their matching npm dist-tag instead of the stable `latest`
238
+ tag. Unrecognized version formats are silently skipped.
239
+
179
240
  ## Configuration
180
241
 
181
242
  Allure 3 uses an `allurerc.ts`, `allurerc.mts`, `allurerc.cts`, `allurerc.mjs`, or `allurerc.js` configuration file to manage report settings, including the report name, output directory, and plugin options.
182
243
 
183
244
  > [!TIP]
184
- > We recommend using the **Awesome** plugin for the best experience.
245
+ > We recommend using the **Awesome** plugin for the best experience.
185
246
  > Support for Classic and Allure 2-style reports is currently experimental.
186
247
 
187
248
  ### Example Configuration File
@@ -248,7 +309,7 @@ For example, setting `"reportLanguage": "fr"` will render the report interface i
248
309
 
249
310
  You can also use `allurerc.json`, `allurerc.yaml`, or `allurerc.yml` files as a declarative way to configure Allure 3.
250
311
 
251
- > [!WARNING]
312
+ > [!WARNING]
252
313
  > Declarative formats don't support advanced features such as tests filtering or environments.
253
314
  > If you need these features, please consider using the `allurerc.ts`, `allurerc.mts`, `allurerc.cts`, `allurerc.mjs`, or `allurerc.js` files.
254
315
 
@@ -1,25 +1,16 @@
1
1
  import * as console from "node:console";
2
- import { randomUUID } from "node:crypto";
3
- import { mkdir, mkdtemp, realpath, writeFile } from "node:fs/promises";
4
- import { tmpdir } from "node:os";
5
- import { dirname, join, resolve } from "node:path";
2
+ import { mkdir, realpath, writeFile } from "node:fs/promises";
3
+ import { dirname, resolve } from "node:path";
6
4
  import process, { exit } from "node:process";
7
- import { AGENT_FINDING_CATEGORIES, AGENT_FINDING_SEVERITIES, AGENT_TASK_MAP_HELP, AGENT_TEST_STATUSES, AgentExpectationUsageError, assertExplicitAgentOutputDirIsSafe, buildAgentInlineExpectations, buildAgentQueryPayload, cleanupAgentRunState, cleanupStaleAgentRunStates, createAgentCapabilities, formatAgentOutputLinks, isAgentExpectationUsageError, isAgentTaskMapHelpRequest, isAgentUsageError, loadAgentOutput, normalizeAgentQueryLimit, normalizeAgentQueryView, normalizeAgentRerunPreset, normalizeRepeatedEnumValues, normalizeRepeatedStringValues, parseAgentLabelFilters, readLatestAgentState, resolveAgentSelectionOutputDir, resolveAgentStateDir, selectAgentTestPlan, validateAgentExpectationsFile, writeAgentRunState, writeInvalidAgentExpectationOutput, } from "@allurereport/plugin-agent";
5
+ import { AGENT_FINDING_CATEGORIES, AGENT_FINDING_SEVERITIES, AGENT_TASK_MAP_HELP, AGENT_TEST_STATUSES, AgentExpectationUsageError, assertExplicitAgentOutputDirIsSafe, buildAgentInlineExpectations, buildAgentQueryPayload, createAgentCapabilities, isAgentExpectationUsageError, isAgentTaskMapHelpRequest, isAgentUsageError, loadAgentOutput, normalizeAgentQueryLimit, normalizeAgentQueryView, normalizeAgentRerunPreset, normalizeRepeatedEnumValues, normalizeRepeatedStringValues, parseAgentLabelFilters, readLatestAgentState, resolveAgentSelectionOutputDir, resolveAgentStateDir, selectAgentTestPlan, validateAgentExpectationsFile, } from "@allurereport/plugin-agent";
8
6
  import { Command, Option, UsageError } from "clipanion";
9
- import { parseRunCommand } from "../utils/resultsPatterns.js";
7
+ import { parseRunCommand } from "../../utils/resultsPatterns.js";
8
+ import { formatAgentCommand, formatAgentInspectCommand, printAgentOutputLinks, writeInvalidAgentExpectationRun, } from "./lifecycle.js";
10
9
  export { AGENT_TASK_MAP_HELP, createAgentCapabilities, isAgentTaskMapHelpRequest };
11
10
  const readOptionalString = (value) => (typeof value === "string" ? value : undefined);
12
11
  const readOptionalBoolean = (value) => value === true;
13
12
  const readOptionalStringArray = (value) => (Array.isArray(value) ? value : undefined);
14
- const formatAgentCommand = (args) => args.join(" ");
15
13
  const AGENT_HUMAN_REPORT_MODES = ["auto", "off", "awesome", "config"];
16
- const formatAgentInspectCommand = (params) => [
17
- "allure",
18
- "agent",
19
- "inspect",
20
- ...(params.dumps ?? []).flatMap((dump) => ["--dump", dump]),
21
- ...(params.resultsDir ?? []),
22
- ].join(" ");
23
14
  const buildInlineExpectationsFromOptions = (options) => buildAgentInlineExpectations({
24
15
  goal: options.goal,
25
16
  taskId: options.taskId,
@@ -34,53 +25,6 @@ const buildInlineExpectationsFromOptions = (options) => buildAgentInlineExpectat
34
25
  expectAttachments: options.expectAttachments,
35
26
  expectAttachmentFilters: readOptionalStringArray(options.expectAttachmentFilters),
36
27
  });
37
- const printAgentOutputLinks = (outputDir) => {
38
- for (const line of formatAgentOutputLinks(outputDir)) {
39
- console.log(line);
40
- }
41
- };
42
- const persistAgentRunState = async (value) => {
43
- try {
44
- await writeAgentRunState(value);
45
- }
46
- catch (error) {
47
- console.error(`Could not update agent state in ${resolveAgentStateDir(value.cwd)}: ${error.message}`);
48
- }
49
- };
50
- const logAgentCleanupFailures = (failures) => {
51
- for (const failure of failures) {
52
- console.error(`Could not clean stale agent output ${failure.state.outputDir}: ${failure.error.message}`);
53
- }
54
- };
55
- const logAgentOrphanCleanupFailures = (failures) => {
56
- for (const failure of failures) {
57
- console.error(`Could not clean stale agent output ${failure.outputDir}: ${failure.error.message}`);
58
- }
59
- };
60
- const cleanupManagedAgentOutputs = async (params) => {
61
- try {
62
- const result = await cleanupAgentRunState({
63
- cwd: params.cwd,
64
- currentRunId: params.runId,
65
- keepManagedRuns: params.managedOutput ? 1 : 0,
66
- });
67
- logAgentCleanupFailures(result.failed);
68
- }
69
- catch (error) {
70
- console.error(`Could not clean agent state in ${resolveAgentStateDir(params.cwd)}: ${error.message}`);
71
- }
72
- try {
73
- const staleResult = await cleanupStaleAgentRunStates({
74
- cwd: params.cwd,
75
- currentRunId: params.runId,
76
- });
77
- logAgentCleanupFailures(staleResult.failed);
78
- logAgentOrphanCleanupFailures(staleResult.orphaned.failed);
79
- }
80
- catch (error) {
81
- console.error(`Could not clean agent state in ${resolveAgentStateDir(params.cwd)}: ${error.message}`);
82
- }
83
- };
84
28
  const agentEnvironmentOption = () => Option.String("--environment,--env", {
85
29
  description: "Force specific environment ID to all tests in the run. Given environment has higher priority than the one defined in the config file (default: empty string)",
86
30
  });
@@ -238,7 +182,7 @@ export class AgentCommand extends Command {
238
182
  output,
239
183
  expectations,
240
184
  });
241
- const { executeAgentMode } = await import("./agent-run.js");
185
+ const { executeAgentMode } = await import("./run.js");
242
186
  await executeAgentMode({
243
187
  configPath,
244
188
  cwd: configuredCwd,
@@ -263,33 +207,13 @@ export class AgentCommand extends Command {
263
207
  throwCliUsageError(error);
264
208
  }
265
209
  const expectationError = error;
266
- const cwd = await realpath(configuredCwd ?? process.cwd());
267
- const runId = randomUUID();
268
- const managedOutput = !output;
269
- const outputDir = output ? resolve(cwd, output) : await mkdtemp(join(tmpdir(), "allure-agent-"));
270
210
  const commandString = formatAgentCommand(args);
271
- const { generatedAt } = await writeInvalidAgentExpectationOutput({
272
- outputDir,
211
+ await writeInvalidAgentExpectationRun({
212
+ configuredCwd,
213
+ output,
273
214
  command: commandString,
274
215
  error: expectationError,
275
216
  });
276
- const generatedAtMs = Date.parse(generatedAt);
277
- const generatedAtTimestamp = Number.isFinite(generatedAtMs) ? generatedAtMs : Date.now();
278
- await persistAgentRunState({
279
- runId,
280
- cwd,
281
- outputDir,
282
- managedOutput,
283
- command: commandString,
284
- startedAt: generatedAtTimestamp,
285
- finishedAt: generatedAtTimestamp,
286
- status: "finished",
287
- exitCode: 1,
288
- pid: process.pid,
289
- });
290
- await cleanupManagedAgentOutputs({ cwd, runId, managedOutput });
291
- printAgentOutputLinks(outputDir);
292
- console.error(expectationError.message);
293
217
  exit(1);
294
218
  }
295
219
  }
@@ -428,7 +352,7 @@ export class AgentInspectCommand extends Command {
428
352
  output,
429
353
  expectations,
430
354
  });
431
- const { executeAgentInspectMode } = await import("./agent-run.js");
355
+ const { executeAgentInspectMode } = await import("./run.js");
432
356
  await executeAgentInspectMode({
433
357
  configPath,
434
358
  cwd: configuredCwd,
@@ -452,33 +376,13 @@ export class AgentInspectCommand extends Command {
452
376
  throwCliUsageError(error);
453
377
  }
454
378
  const expectationError = error;
455
- const cwd = await realpath(configuredCwd ?? process.cwd());
456
- const runId = randomUUID();
457
- const managedOutput = !output;
458
- const outputDir = output ? resolve(cwd, output) : await mkdtemp(join(tmpdir(), "allure-agent-"));
459
379
  const commandString = formatAgentInspectCommand({ dumps, resultsDir });
460
- const { generatedAt } = await writeInvalidAgentExpectationOutput({
461
- outputDir,
380
+ await writeInvalidAgentExpectationRun({
381
+ configuredCwd,
382
+ output,
462
383
  command: commandString,
463
384
  error: expectationError,
464
385
  });
465
- const generatedAtMs = Date.parse(generatedAt);
466
- const generatedAtTimestamp = Number.isFinite(generatedAtMs) ? generatedAtMs : Date.now();
467
- await persistAgentRunState({
468
- runId,
469
- cwd,
470
- outputDir,
471
- managedOutput,
472
- command: commandString,
473
- startedAt: generatedAtTimestamp,
474
- finishedAt: generatedAtTimestamp,
475
- status: "finished",
476
- exitCode: 1,
477
- pid: process.pid,
478
- });
479
- await cleanupManagedAgentOutputs({ cwd, runId, managedOutput });
480
- printAgentOutputLinks(outputDir);
481
- console.error(expectationError.message);
482
386
  exit(1);
483
387
  }
484
388
  }
@@ -0,0 +1,20 @@
1
+ import { writeAgentRunState, type AgentExpectationUsageError } from "@allurereport/plugin-agent";
2
+ export declare const formatAgentCommand: (args: string[]) => string;
3
+ export declare const formatAgentInspectCommand: (params: {
4
+ dumps?: string[];
5
+ resultsDir?: string[];
6
+ }) => string;
7
+ export declare const resolveAgentOutputDir: (cwd: string, output?: string) => string | Promise<string>;
8
+ export declare const printAgentOutputLinks: (outputDir: string) => void;
9
+ export declare const persistAgentRunState: (value: Parameters<typeof writeAgentRunState>[0]) => Promise<void>;
10
+ export declare const cleanupManagedAgentOutputs: (params: {
11
+ cwd: string;
12
+ runId: string;
13
+ managedOutput: boolean;
14
+ }) => Promise<void>;
15
+ export declare const writeInvalidAgentExpectationRun: (params: {
16
+ configuredCwd?: string;
17
+ output?: string;
18
+ command: string;
19
+ error: AgentExpectationUsageError;
20
+ }) => Promise<void>;
@@ -0,0 +1,91 @@
1
+ import * as console from "node:console";
2
+ import { randomUUID } from "node:crypto";
3
+ import { mkdtemp, realpath } from "node:fs/promises";
4
+ import { tmpdir } from "node:os";
5
+ import { join, resolve } from "node:path";
6
+ import process from "node:process";
7
+ import { cleanupAgentRunState, cleanupStaleAgentRunStates, formatAgentOutputLinks, resolveAgentStateDir, writeAgentRunState, writeInvalidAgentExpectationOutput, } from "@allurereport/plugin-agent";
8
+ export const formatAgentCommand = (args) => args.join(" ");
9
+ export const formatAgentInspectCommand = (params) => [
10
+ "allure",
11
+ "agent",
12
+ "inspect",
13
+ ...(params.dumps ?? []).flatMap((dump) => ["--dump", dump]),
14
+ ...(params.resultsDir ?? []),
15
+ ].join(" ");
16
+ export const resolveAgentOutputDir = (cwd, output) => output ? resolve(cwd, output) : mkdtemp(join(tmpdir(), "allure-agent-"));
17
+ export const printAgentOutputLinks = (outputDir) => {
18
+ for (const line of formatAgentOutputLinks(outputDir)) {
19
+ console.log(line);
20
+ }
21
+ };
22
+ export const persistAgentRunState = async (value) => {
23
+ try {
24
+ await writeAgentRunState(value);
25
+ }
26
+ catch (error) {
27
+ console.error(`Could not update agent state in ${resolveAgentStateDir(value.cwd)}: ${error.message}`);
28
+ }
29
+ };
30
+ const logAgentCleanupFailures = (failures) => {
31
+ for (const failure of failures) {
32
+ console.error(`Could not clean stale agent output ${failure.state.outputDir}: ${failure.error.message}`);
33
+ }
34
+ };
35
+ const logAgentOrphanCleanupFailures = (failures) => {
36
+ for (const failure of failures) {
37
+ console.error(`Could not clean stale agent output ${failure.outputDir}: ${failure.error.message}`);
38
+ }
39
+ };
40
+ export const cleanupManagedAgentOutputs = async (params) => {
41
+ try {
42
+ const result = await cleanupAgentRunState({
43
+ cwd: params.cwd,
44
+ currentRunId: params.runId,
45
+ keepManagedRuns: params.managedOutput ? 1 : 0,
46
+ });
47
+ logAgentCleanupFailures(result.failed);
48
+ }
49
+ catch (error) {
50
+ console.error(`Could not clean agent state in ${resolveAgentStateDir(params.cwd)}: ${error.message}`);
51
+ }
52
+ try {
53
+ const staleResult = await cleanupStaleAgentRunStates({
54
+ cwd: params.cwd,
55
+ currentRunId: params.runId,
56
+ });
57
+ logAgentCleanupFailures(staleResult.failed);
58
+ logAgentOrphanCleanupFailures(staleResult.orphaned.failed);
59
+ }
60
+ catch (error) {
61
+ console.error(`Could not clean agent state in ${resolveAgentStateDir(params.cwd)}: ${error.message}`);
62
+ }
63
+ };
64
+ export const writeInvalidAgentExpectationRun = async (params) => {
65
+ const cwd = await realpath(params.configuredCwd ?? process.cwd());
66
+ const runId = randomUUID();
67
+ const managedOutput = !params.output;
68
+ const outputDir = await resolveAgentOutputDir(cwd, params.output);
69
+ const { generatedAt } = await writeInvalidAgentExpectationOutput({
70
+ outputDir,
71
+ command: params.command,
72
+ error: params.error,
73
+ });
74
+ const generatedAtMs = Date.parse(generatedAt);
75
+ const generatedAtTimestamp = Number.isFinite(generatedAtMs) ? generatedAtMs : Date.now();
76
+ await persistAgentRunState({
77
+ runId,
78
+ cwd,
79
+ outputDir,
80
+ managedOutput,
81
+ command: params.command,
82
+ startedAt: generatedAtTimestamp,
83
+ finishedAt: generatedAtTimestamp,
84
+ status: "finished",
85
+ exitCode: 1,
86
+ pid: process.pid,
87
+ });
88
+ await cleanupManagedAgentOutputs({ cwd, runId, managedOutput });
89
+ printAgentOutputLinks(outputDir);
90
+ console.error(params.error.message);
91
+ };
@@ -1,21 +1,9 @@
1
- import { writeAgentRunState, type AgentExpectationsInput, type AgentHumanReportMode } from "@allurereport/plugin-agent";
2
- export declare const formatAgentCommand: (args: string[]) => string;
3
- export declare const formatAgentInspectCommand: (params: {
4
- dumps?: string[];
5
- resultsDir?: string[];
6
- }) => string;
7
- export declare const printAgentOutputLinks: (outputDir: string) => void;
1
+ import { type AgentExpectationsInput, type AgentHumanReportMode } from "@allurereport/plugin-agent";
8
2
  export declare const printAgentRunStart: (outputDir: string, commandString: string) => void;
9
3
  export declare const printAgentRunSummary: (outputDir: string, options?: {
10
4
  durationMs?: number;
11
5
  rerunCommand?: string;
12
6
  }) => Promise<void>;
13
- export declare const persistAgentRunState: (value: Parameters<typeof writeAgentRunState>[0]) => Promise<void>;
14
- export declare const cleanupManagedAgentOutputs: (params: {
15
- cwd: string;
16
- runId: string;
17
- managedOutput: boolean;
18
- }) => Promise<void>;
19
7
  export type ExecuteAgentModeParams = {
20
8
  configPath?: string;
21
9
  cwd?: string;
@@ -1,30 +1,17 @@
1
1
  import * as console from "node:console";
2
2
  import { randomUUID } from "node:crypto";
3
- import { mkdtemp, realpath, rm } from "node:fs/promises";
4
- import { tmpdir } from "node:os";
5
- import { join, resolve } from "node:path";
3
+ import { realpath, rm } from "node:fs/promises";
4
+ import { resolve } from "node:path";
6
5
  import process, { exit } from "node:process";
7
6
  import { AllureReport, isFileNotFoundError, readConfig } from "@allurereport/core";
8
- import { createAgentTestPlanContext, AgentUsageError, assertExplicitAgentOutputDirIsSafe, formatAgentOutputLinks, formatAgentRunSummary, isPathInside, loadAgentOutput, normalizeAgentRerunPreset, parseAgentLabelFilters, cleanupAgentRunState, cleanupStaleAgentRunStates, resolveAgentStateDir, writeAgentRunState, } from "@allurereport/plugin-agent";
9
- import { normalizeCommandEnvironmentOptions, resolveCommandEnvironment } from "../utils/environment.js";
10
- import { createChildAllureCliEnvironment, getActiveAllureCliCommand } from "../utils/execution-context.js";
11
- import { findFilesByGlobs } from "../utils/fileSystem.js";
12
- import { resolveAndFindResultsDirs, resolveResultsPatterns } from "../utils/resultsPatterns.js";
13
- import { createAgentHumanReportConfig } from "./agent-human-report.js";
14
- import { executeAllureRun, executeNestedAllureCommand } from "./commons/run.js";
15
- export const formatAgentCommand = (args) => args.join(" ");
16
- export const formatAgentInspectCommand = (params) => [
17
- "allure",
18
- "agent",
19
- "inspect",
20
- ...(params.dumps ?? []).flatMap((dump) => ["--dump", dump]),
21
- ...(params.resultsDir ?? []),
22
- ].join(" ");
23
- export const printAgentOutputLinks = (outputDir) => {
24
- for (const line of formatAgentOutputLinks(outputDir)) {
25
- console.log(line);
26
- }
27
- };
7
+ import { createAgentTestPlanContext, AgentUsageError, assertExplicitAgentOutputDirIsSafe, formatAgentRunSummary, isPathInside, loadAgentOutput, normalizeAgentRerunPreset, parseAgentLabelFilters, } from "@allurereport/plugin-agent";
8
+ import { normalizeCommandEnvironmentOptions, resolveCommandEnvironment } from "../../utils/environment.js";
9
+ import { createChildAllureCliEnvironment, getActiveAllureCliCommand } from "../../utils/execution-context.js";
10
+ import { findFilesByGlobs } from "../../utils/fileSystem.js";
11
+ import { resolveAndFindResultsDirs, resolveResultsPatterns } from "../../utils/resultsPatterns.js";
12
+ import { executeAllureRun, executeNestedAllureCommand } from "../commons/run.js";
13
+ import { createAgentHumanReportConfig } from "./human-report.js";
14
+ import { cleanupManagedAgentOutputs, formatAgentCommand, formatAgentInspectCommand, persistAgentRunState, printAgentOutputLinks, resolveAgentOutputDir, } from "./lifecycle.js";
28
15
  export const printAgentRunStart = (outputDir, commandString) => {
29
16
  console.log(`agent output: ${outputDir}`);
30
17
  console.log(commandString);
@@ -48,48 +35,6 @@ export const printAgentRunSummary = async (outputDir, options = {}) => {
48
35
  printAgentOutputLinks(outputDir);
49
36
  }
50
37
  };
51
- export const persistAgentRunState = async (value) => {
52
- try {
53
- await writeAgentRunState(value);
54
- }
55
- catch (error) {
56
- console.error(`Could not update agent state in ${resolveAgentStateDir(value.cwd)}: ${error.message}`);
57
- }
58
- };
59
- const logAgentCleanupFailures = (failures) => {
60
- for (const failure of failures) {
61
- console.error(`Could not clean stale agent output ${failure.state.outputDir}: ${failure.error.message}`);
62
- }
63
- };
64
- const logAgentOrphanCleanupFailures = (failures) => {
65
- for (const failure of failures) {
66
- console.error(`Could not clean stale agent output ${failure.outputDir}: ${failure.error.message}`);
67
- }
68
- };
69
- export const cleanupManagedAgentOutputs = async (params) => {
70
- try {
71
- const result = await cleanupAgentRunState({
72
- cwd: params.cwd,
73
- currentRunId: params.runId,
74
- keepManagedRuns: params.managedOutput ? 1 : 0,
75
- });
76
- logAgentCleanupFailures(result.failed);
77
- }
78
- catch (error) {
79
- console.error(`Could not clean agent state in ${resolveAgentStateDir(params.cwd)}: ${error.message}`);
80
- }
81
- try {
82
- const staleResult = await cleanupStaleAgentRunStates({
83
- cwd: params.cwd,
84
- currentRunId: params.runId,
85
- });
86
- logAgentCleanupFailures(staleResult.failed);
87
- logAgentOrphanCleanupFailures(staleResult.orphaned.failed);
88
- }
89
- catch (error) {
90
- console.error(`Could not clean agent state in ${resolveAgentStateDir(params.cwd)}: ${error.message}`);
91
- }
92
- };
93
38
  export const executeAgentMode = async (params) => {
94
39
  const { configPath, cwd: configuredCwd, output, expectations, inlineExpectations, environment, environmentName, silent, rerunFrom, rerunLatest, rerunPreset, rerunEnvironments, rerunLabels, reportMode, args, resultsPatterns: cliResultsPatterns = [], } = params;
95
40
  const command = args[0];
@@ -131,7 +76,7 @@ export const executeAgentMode = async (params) => {
131
76
  }
132
77
  const runId = randomUUID();
133
78
  const managedOutput = !output;
134
- const outputDir = output ? resolve(cwd, output) : await mkdtemp(join(tmpdir(), "allure-agent-"));
79
+ const outputDir = await resolveAgentOutputDir(cwd, output);
135
80
  const expectationsPath = expectations ? resolve(cwd, expectations) : undefined;
136
81
  const environmentOptions = {
137
82
  environment,
@@ -256,7 +201,7 @@ export const executeAgentInspectMode = async (params) => {
256
201
  }
257
202
  const runId = randomUUID();
258
203
  const managedOutput = !output;
259
- const outputDir = output ? resolve(cwd, output) : await mkdtemp(join(tmpdir(), "allure-agent-"));
204
+ const outputDir = await resolveAgentOutputDir(cwd, output);
260
205
  const expectationsPath = expectations ? resolve(cwd, expectations) : undefined;
261
206
  const commandString = formatAgentInspectCommand({ dumps: dumpFiles, resultsDir: resultDirectories });
262
207
  const hiddenLabels = hideLabels?.length ? hideLabels : undefined;
@@ -10,4 +10,5 @@ export declare const generate: (params: {
10
10
  resultsDir?: string[];
11
11
  dump?: string[];
12
12
  collectSummary?: boolean;
13
+ clean?: boolean;
13
14
  }) => Promise<GenerateResult | undefined>;
@@ -1,8 +1,9 @@
1
- import { exit } from "node:process";
1
+ import { cwd as processCwd, exit } from "node:process";
2
2
  import { createReportContext } from "@allurereport/ci";
3
3
  import { AllureReport, filterFailedQualityGateResults, stringifyQualityGateResults } from "@allurereport/core";
4
4
  import { KnownError } from "@allurereport/service";
5
- import { red } from "yoctocolors";
5
+ import { red, yellow } from "yoctocolors";
6
+ import { cleanOutputDirectory, isOutputDirectoryNotEmpty } from "../../utils/cleanOutput.js";
6
7
  import { findFilesByGlobs } from "../../utils/fileSystem.js";
7
8
  import { logError } from "../../utils/logs.js";
8
9
  import { resolveAndFindResultsDirs, resolveResultsPatterns } from "../../utils/resultsPatterns.js";
@@ -19,6 +20,31 @@ export const generate = async (params) => {
19
20
  exit(1);
20
21
  return;
21
22
  }
23
+ if (params.clean) {
24
+ try {
25
+ const globalAttachments = await findFilesByGlobs(params.config.cwd ?? processCwd(), params.config.globalAttachments ?? [], { dot: false, windowsPathsNoEscape: false });
26
+ await cleanOutputDirectory({
27
+ output: params.config.output,
28
+ cwd: params.cwd,
29
+ resultsDirs: resultDirectories,
30
+ inputs: [
31
+ ...dumpFiles,
32
+ ...globalAttachments,
33
+ params.config.historyPath,
34
+ params.config.resolutions?.knownIssuesPath,
35
+ ].filter((path) => !!path),
36
+ });
37
+ }
38
+ catch (error) {
39
+ console.error(red(error instanceof Error ? error.message : String(error)));
40
+ exit(1);
41
+ return;
42
+ }
43
+ }
44
+ if (!params.clean && params.config.output && (await isOutputDirectoryNotEmpty(params.cwd, params.config.output))) {
45
+ console.warn(yellow(`The output directory "${params.config.output}" is not empty. Files of a previous report may break the layout ` +
46
+ "of the new one. Use the --clean option to remove the directory before generating."));
47
+ }
22
48
  try {
23
49
  const allureReport = new AllureReport(params.config);
24
50
  await allureReport.restoreState(Array.from(dumpFiles));
@@ -8,8 +8,10 @@ export declare class GenerateCommand extends Command {
8
8
  cwd: string | undefined;
9
9
  reportName: string | undefined;
10
10
  dump: string[] | undefined;
11
+ clean: boolean | undefined;
11
12
  open: boolean | undefined;
12
13
  port: string | undefined;
14
+ host: string | undefined;
13
15
  historyLimit: string | undefined;
14
16
  historyBaseUrl: string | undefined;
15
17
  hideLabels: string[] | undefined;