allure 3.11.0 → 3.12.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 +21 -1
- package/dist/commands/agent-human-report.d.ts +23 -0
- package/dist/commands/agent-human-report.js +171 -0
- package/dist/commands/agent-run.d.ts +30 -2
- package/dist/commands/agent-run.js +194 -10
- package/dist/commands/agent.d.ts +32 -0
- package/dist/commands/agent.js +294 -24
- package/dist/index.js +2 -1
- package/dist/utils/process.js +46 -1
- package/package.json +21 -21
package/README.md
CHANGED
|
@@ -74,7 +74,27 @@ For example:
|
|
|
74
74
|
npx allure agent -- npm test
|
|
75
75
|
```
|
|
76
76
|
|
|
77
|
-
|
|
77
|
+
To analyze existing Allure results or dump archives downloaded from CI without
|
|
78
|
+
rerunning tests, use `agent inspect`:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
npx allure agent inspect path/to/allure-results
|
|
82
|
+
npx allure agent inspect --dump allure-results-linux.zip --dump allure-results-macos.zip
|
|
83
|
+
npx allure agent inspect --config ./allurerc.mjs --output ./agent-output path/to/allure-results
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
`agent inspect` accepts the same result inputs and configuration-style options as
|
|
87
|
+
`allure generate`, including result directory globs, `--dump`, `--config`,
|
|
88
|
+
`--cwd`, `--report-name`, `--history-limit`, and `--hide-labels`. Its `--output`
|
|
89
|
+
option writes the agentic output directory.
|
|
90
|
+
|
|
91
|
+
`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`.
|
|
92
|
+
|
|
93
|
+
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.
|
|
94
|
+
|
|
95
|
+
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`.
|
|
96
|
+
|
|
97
|
+
`allure agent` creates a fresh output directory automatically, accepts compact inline expectations such as `--goal`, `--expect-tests`, `--expect-test`, `--expect-label`, and `--expect-step-containing`, and can still load an expectations file with `--expectations` when needed.
|
|
78
98
|
|
|
79
99
|
Agents and setup tools can inspect the local structured capability contract without scraping help text:
|
|
80
100
|
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type FullConfig, type PluginInstance } from "@allurereport/core";
|
|
2
|
+
import type { AgentHumanReportMode, AgentHumanReportStatus, AgentHumanReportStatusProvider } from "@allurereport/plugin-agent";
|
|
3
|
+
export declare const AGENT_HUMAN_REPORT_THRESHOLD = 1000;
|
|
4
|
+
type AgentHumanReportConfigOverride = {
|
|
5
|
+
name?: FullConfig["name"];
|
|
6
|
+
open?: FullConfig["open"];
|
|
7
|
+
port?: FullConfig["port"];
|
|
8
|
+
hideLabels?: FullConfig["hideLabels"];
|
|
9
|
+
historyLimit?: FullConfig["historyLimit"];
|
|
10
|
+
};
|
|
11
|
+
type AgentHumanReportBuildParams = {
|
|
12
|
+
mode: AgentHumanReportMode;
|
|
13
|
+
cwd: string;
|
|
14
|
+
configPath?: string;
|
|
15
|
+
outputDir: string;
|
|
16
|
+
configOverride?: AgentHumanReportConfigOverride;
|
|
17
|
+
};
|
|
18
|
+
export declare const createAgentHumanReportConfig: ({ mode, cwd, configPath, outputDir, configOverride, }: AgentHumanReportBuildParams) => Promise<{
|
|
19
|
+
status: AgentHumanReportStatus;
|
|
20
|
+
statusProvider: AgentHumanReportStatusProvider;
|
|
21
|
+
plugins: PluginInstance[];
|
|
22
|
+
}>;
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
2
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
3
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
4
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
5
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
6
|
+
};
|
|
7
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
8
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
9
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
10
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
|
+
};
|
|
12
|
+
var _AgentHumanReportPlugin_realtime, _AgentHumanReportPlugin_generated;
|
|
13
|
+
import * as console from "node:console";
|
|
14
|
+
import { readConfig, readRawConfig } from "@allurereport/core";
|
|
15
|
+
import AwesomePlugin from "@allurereport/plugin-awesome";
|
|
16
|
+
export const AGENT_HUMAN_REPORT_THRESHOLD = 1000;
|
|
17
|
+
const createInitialHumanReportStatus = (mode) => ({
|
|
18
|
+
schema_version: "allure-agent-human-report/v1",
|
|
19
|
+
mode,
|
|
20
|
+
status: mode === "off" ? "disabled" : "pending",
|
|
21
|
+
result_count: null,
|
|
22
|
+
threshold: AGENT_HUMAN_REPORT_THRESHOLD,
|
|
23
|
+
path: null,
|
|
24
|
+
reports: [],
|
|
25
|
+
reason: mode === "off" ? "disabled by --report off" : null,
|
|
26
|
+
error: null,
|
|
27
|
+
});
|
|
28
|
+
const isAgentPluginId = (id) => id === "agent" || id === "plugin-agent";
|
|
29
|
+
const isAwesomeDescriptor = (id, descriptor) => id === "awesome" ||
|
|
30
|
+
id === "@allurereport/plugin-awesome" ||
|
|
31
|
+
descriptor.import === "awesome" ||
|
|
32
|
+
descriptor.import === "@allurereport/plugin-awesome";
|
|
33
|
+
const configuredAwesomeOptions = async (cwd, configPath) => {
|
|
34
|
+
const rawConfig = await readRawConfig(cwd, configPath);
|
|
35
|
+
const plugins = rawConfig.plugins ?? {};
|
|
36
|
+
for (const id of Object.keys(plugins)) {
|
|
37
|
+
const descriptor = plugins[id];
|
|
38
|
+
if (isAwesomeDescriptor(id, descriptor)) {
|
|
39
|
+
return (descriptor.options ?? {});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return {};
|
|
43
|
+
};
|
|
44
|
+
const recordGeneratedReport = (status, pluginId, path) => {
|
|
45
|
+
const alreadyRecorded = status.reports.some((report) => report.plugin_id === pluginId && report.path === path);
|
|
46
|
+
if (!alreadyRecorded) {
|
|
47
|
+
status.reports.push({
|
|
48
|
+
plugin_id: pluginId,
|
|
49
|
+
path,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
status.status = status.error ? "failed" : "generated";
|
|
53
|
+
status.path ?? (status.path = path);
|
|
54
|
+
status.reason = null;
|
|
55
|
+
status.generated_at = new Date().toISOString();
|
|
56
|
+
};
|
|
57
|
+
const recordFailedReport = (status, pluginId, error) => {
|
|
58
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
59
|
+
status.status = "failed";
|
|
60
|
+
status.error ?? (status.error = message);
|
|
61
|
+
status.errors = [...(status.errors ?? []), { plugin_id: pluginId, message }];
|
|
62
|
+
};
|
|
63
|
+
class AgentHumanReportPlugin {
|
|
64
|
+
constructor(params) {
|
|
65
|
+
this.params = params;
|
|
66
|
+
_AgentHumanReportPlugin_realtime.set(this, void 0);
|
|
67
|
+
_AgentHumanReportPlugin_generated.set(this, false);
|
|
68
|
+
this.start = async (_context, _store, realtime) => {
|
|
69
|
+
__classPrivateFieldSet(this, _AgentHumanReportPlugin_realtime, realtime, "f");
|
|
70
|
+
};
|
|
71
|
+
this.done = async (context, store) => {
|
|
72
|
+
const { mode, status, threshold, pluginId, plugin, reportPath } = this.params;
|
|
73
|
+
const resultCount = (await store.allTestResults()).length;
|
|
74
|
+
status.result_count = resultCount;
|
|
75
|
+
if (status.status === "skipped") {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
if (mode === "auto" && resultCount > threshold) {
|
|
79
|
+
status.status = "skipped";
|
|
80
|
+
status.reason = `result count ${resultCount} exceeds threshold ${threshold}`;
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
await plugin.start?.(context, store, __classPrivateFieldGet(this, _AgentHumanReportPlugin_realtime, "f"));
|
|
85
|
+
await plugin.done?.(context, store);
|
|
86
|
+
__classPrivateFieldSet(this, _AgentHumanReportPlugin_generated, true, "f");
|
|
87
|
+
recordGeneratedReport(status, pluginId, reportPath);
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
recordFailedReport(status, pluginId, error);
|
|
91
|
+
console.error(`agent human report ${pluginId} error`, error);
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
this.info = async (context, store) => {
|
|
95
|
+
if (!__classPrivateFieldGet(this, _AgentHumanReportPlugin_generated, "f")) {
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
98
|
+
return this.params.plugin.info?.(context, store);
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
_AgentHumanReportPlugin_realtime = new WeakMap(), _AgentHumanReportPlugin_generated = new WeakMap();
|
|
103
|
+
const wrapHumanReportPlugin = (params) => {
|
|
104
|
+
const { mode, status, plugin, reportPath = `${plugin.id}/` } = params;
|
|
105
|
+
return {
|
|
106
|
+
...plugin,
|
|
107
|
+
plugin: new AgentHumanReportPlugin({
|
|
108
|
+
mode,
|
|
109
|
+
status,
|
|
110
|
+
threshold: AGENT_HUMAN_REPORT_THRESHOLD,
|
|
111
|
+
pluginId: plugin.id,
|
|
112
|
+
plugin: plugin.plugin,
|
|
113
|
+
reportPath,
|
|
114
|
+
}),
|
|
115
|
+
};
|
|
116
|
+
};
|
|
117
|
+
export const createAgentHumanReportConfig = async ({ mode, cwd, configPath, outputDir, configOverride, }) => {
|
|
118
|
+
const status = createInitialHumanReportStatus(mode);
|
|
119
|
+
const statusProvider = () => status;
|
|
120
|
+
if (mode === "off") {
|
|
121
|
+
return {
|
|
122
|
+
status,
|
|
123
|
+
statusProvider,
|
|
124
|
+
plugins: [],
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
if (mode === "auto" || mode === "awesome") {
|
|
128
|
+
const awesomeOptions = {
|
|
129
|
+
...(await configuredAwesomeOptions(cwd, configPath)),
|
|
130
|
+
singleFile: true,
|
|
131
|
+
};
|
|
132
|
+
const awesomePlugin = {
|
|
133
|
+
id: "awesome",
|
|
134
|
+
enabled: true,
|
|
135
|
+
options: awesomeOptions,
|
|
136
|
+
plugin: new AwesomePlugin(awesomeOptions),
|
|
137
|
+
};
|
|
138
|
+
return {
|
|
139
|
+
status,
|
|
140
|
+
statusProvider,
|
|
141
|
+
plugins: [
|
|
142
|
+
wrapHumanReportPlugin({
|
|
143
|
+
mode,
|
|
144
|
+
status,
|
|
145
|
+
plugin: awesomePlugin,
|
|
146
|
+
reportPath: "awesome/index.html",
|
|
147
|
+
}),
|
|
148
|
+
],
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
const reportConfig = await readConfig(cwd, configPath, {
|
|
152
|
+
...configOverride,
|
|
153
|
+
output: outputDir,
|
|
154
|
+
});
|
|
155
|
+
const plugins = reportConfig.plugins
|
|
156
|
+
?.filter((plugin) => !isAgentPluginId(plugin.id))
|
|
157
|
+
.map((plugin) => wrapHumanReportPlugin({
|
|
158
|
+
mode,
|
|
159
|
+
status,
|
|
160
|
+
plugin,
|
|
161
|
+
})) ?? [];
|
|
162
|
+
if (!plugins.length) {
|
|
163
|
+
status.status = "disabled";
|
|
164
|
+
status.reason = "no configured non-agent report plugins";
|
|
165
|
+
}
|
|
166
|
+
return {
|
|
167
|
+
status,
|
|
168
|
+
statusProvider,
|
|
169
|
+
plugins,
|
|
170
|
+
};
|
|
171
|
+
};
|
|
@@ -1,7 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { writeAgentRunState, type AgentExpectationsInput, type AgentHumanReportMode } from "@allurereport/plugin-agent";
|
|
2
2
|
export declare const formatAgentCommand: (args: string[]) => string;
|
|
3
|
+
export declare const formatAgentInspectCommand: (params: {
|
|
4
|
+
dumps?: string[];
|
|
5
|
+
resultsDir?: string[];
|
|
6
|
+
}) => string;
|
|
3
7
|
export declare const printAgentOutputLinks: (outputDir: string) => void;
|
|
4
|
-
export declare const
|
|
8
|
+
export declare const persistAgentRunState: (value: Parameters<typeof writeAgentRunState>[0]) => Promise<void>;
|
|
9
|
+
export declare const cleanupManagedAgentOutputs: (params: {
|
|
10
|
+
cwd: string;
|
|
11
|
+
runId: string;
|
|
12
|
+
managedOutput: boolean;
|
|
13
|
+
}) => Promise<void>;
|
|
5
14
|
export type ExecuteAgentModeParams = {
|
|
6
15
|
configPath?: string;
|
|
7
16
|
cwd?: string;
|
|
@@ -16,6 +25,25 @@ export type ExecuteAgentModeParams = {
|
|
|
16
25
|
rerunPreset?: string;
|
|
17
26
|
rerunEnvironments?: string[];
|
|
18
27
|
rerunLabels?: string[];
|
|
28
|
+
reportMode: AgentHumanReportMode;
|
|
19
29
|
args: string[];
|
|
20
30
|
};
|
|
21
31
|
export declare const executeAgentMode: (params: ExecuteAgentModeParams) => Promise<void>;
|
|
32
|
+
export type ExecuteAgentInspectModeParams = {
|
|
33
|
+
configPath?: string;
|
|
34
|
+
cwd?: string;
|
|
35
|
+
output?: string;
|
|
36
|
+
expectations?: string;
|
|
37
|
+
inlineExpectations?: AgentExpectationsInput;
|
|
38
|
+
environment?: string;
|
|
39
|
+
environmentName?: string;
|
|
40
|
+
reportName?: string;
|
|
41
|
+
open?: boolean;
|
|
42
|
+
port?: string;
|
|
43
|
+
historyLimit?: string;
|
|
44
|
+
hideLabels?: string[];
|
|
45
|
+
dumps?: string[];
|
|
46
|
+
reportMode: AgentHumanReportMode;
|
|
47
|
+
resultsDir: string[];
|
|
48
|
+
};
|
|
49
|
+
export declare const executeAgentInspectMode: (params: ExecuteAgentInspectModeParams) => Promise<never>;
|
|
@@ -1,29 +1,73 @@
|
|
|
1
1
|
import * as console from "node:console";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
2
3
|
import { mkdtemp, realpath, rm } from "node:fs/promises";
|
|
3
4
|
import { tmpdir } from "node:os";
|
|
4
5
|
import { join, resolve } from "node:path";
|
|
5
6
|
import process, { exit } from "node:process";
|
|
6
7
|
import { AllureReport, isFileNotFoundError, readConfig } from "@allurereport/core";
|
|
7
|
-
import { createAgentTestPlanContext, AgentUsageError, formatAgentOutputLinks, isPathInside, normalizeAgentRerunPreset, parseAgentLabelFilters, resolveAgentStateDir,
|
|
8
|
+
import { createAgentTestPlanContext, AgentUsageError, formatAgentOutputLinks, isPathInside, normalizeAgentRerunPreset, parseAgentLabelFilters, cleanupAgentRunState, cleanupStaleAgentRunStates, resolveAgentStateDir, writeAgentRunState, } from "@allurereport/plugin-agent";
|
|
8
9
|
import { normalizeCommandEnvironmentOptions, resolveCommandEnvironment } from "../utils/environment.js";
|
|
9
10
|
import { createChildAllureCliEnvironment, getActiveAllureCliCommand } from "../utils/execution-context.js";
|
|
11
|
+
import { findAllureResultDirectories, findFilesByGlobs } from "../utils/fileSystem.js";
|
|
12
|
+
import { createAgentHumanReportConfig } from "./agent-human-report.js";
|
|
10
13
|
import { executeAllureRun, executeNestedAllureCommand } from "./commons/run.js";
|
|
11
14
|
export const formatAgentCommand = (args) => args.join(" ");
|
|
15
|
+
export const formatAgentInspectCommand = (params) => [
|
|
16
|
+
"allure",
|
|
17
|
+
"agent",
|
|
18
|
+
"inspect",
|
|
19
|
+
...(params.dumps ?? []).flatMap((dump) => ["--dump", dump]),
|
|
20
|
+
...(params.resultsDir ?? []),
|
|
21
|
+
].join(" ");
|
|
12
22
|
export const printAgentOutputLinks = (outputDir) => {
|
|
13
23
|
for (const line of formatAgentOutputLinks(outputDir)) {
|
|
14
24
|
console.log(line);
|
|
15
25
|
}
|
|
16
26
|
};
|
|
17
|
-
export const
|
|
27
|
+
export const persistAgentRunState = async (value) => {
|
|
18
28
|
try {
|
|
19
|
-
await
|
|
29
|
+
await writeAgentRunState(value);
|
|
20
30
|
}
|
|
21
31
|
catch (error) {
|
|
22
|
-
console.error(`Could not update
|
|
32
|
+
console.error(`Could not update agent state in ${resolveAgentStateDir(value.cwd)}: ${error.message}`);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
const logAgentCleanupFailures = (failures) => {
|
|
36
|
+
for (const failure of failures) {
|
|
37
|
+
console.error(`Could not clean stale agent output ${failure.state.outputDir}: ${failure.error.message}`);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
const logAgentOrphanCleanupFailures = (failures) => {
|
|
41
|
+
for (const failure of failures) {
|
|
42
|
+
console.error(`Could not clean stale agent output ${failure.outputDir}: ${failure.error.message}`);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
export const cleanupManagedAgentOutputs = async (params) => {
|
|
46
|
+
try {
|
|
47
|
+
const result = await cleanupAgentRunState({
|
|
48
|
+
cwd: params.cwd,
|
|
49
|
+
currentRunId: params.runId,
|
|
50
|
+
keepManagedRuns: params.managedOutput ? 1 : 0,
|
|
51
|
+
});
|
|
52
|
+
logAgentCleanupFailures(result.failed);
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
console.error(`Could not clean agent state in ${resolveAgentStateDir(params.cwd)}: ${error.message}`);
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
const staleResult = await cleanupStaleAgentRunStates({
|
|
59
|
+
cwd: params.cwd,
|
|
60
|
+
currentRunId: params.runId,
|
|
61
|
+
});
|
|
62
|
+
logAgentCleanupFailures(staleResult.failed);
|
|
63
|
+
logAgentOrphanCleanupFailures(staleResult.orphaned.failed);
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
console.error(`Could not clean agent state in ${resolveAgentStateDir(params.cwd)}: ${error.message}`);
|
|
23
67
|
}
|
|
24
68
|
};
|
|
25
69
|
export const executeAgentMode = async (params) => {
|
|
26
|
-
const { configPath, cwd: configuredCwd, output, expectations, inlineExpectations, environment, environmentName, silent, rerunFrom, rerunLatest, rerunPreset, rerunEnvironments, rerunLabels, args, } = params;
|
|
70
|
+
const { configPath, cwd: configuredCwd, output, expectations, inlineExpectations, environment, environmentName, silent, rerunFrom, rerunLatest, rerunPreset, rerunEnvironments, rerunLabels, reportMode, args, } = params;
|
|
27
71
|
const command = args[0];
|
|
28
72
|
const commandArgs = args.slice(1);
|
|
29
73
|
const cwd = await realpath(configuredCwd ?? process.cwd());
|
|
@@ -58,6 +102,8 @@ export const executeAgentMode = async (params) => {
|
|
|
58
102
|
exit(exitCode ?? -1);
|
|
59
103
|
return;
|
|
60
104
|
}
|
|
105
|
+
const runId = randomUUID();
|
|
106
|
+
const managedOutput = !output;
|
|
61
107
|
const outputDir = output ? resolve(cwd, output) : await mkdtemp(join(tmpdir(), "allure-agent-"));
|
|
62
108
|
const expectationsPath = expectations ? resolve(cwd, expectations) : undefined;
|
|
63
109
|
const environmentOptions = {
|
|
@@ -68,6 +114,12 @@ export const executeAgentMode = async (params) => {
|
|
|
68
114
|
if (expectationsPath && isPathInside(outputDir, expectationsPath)) {
|
|
69
115
|
throw new AgentUsageError(`--expectations path ${JSON.stringify(expectationsPath)} must not be inside the agent output directory ${JSON.stringify(outputDir)}`);
|
|
70
116
|
}
|
|
117
|
+
const humanReport = await createAgentHumanReportConfig({
|
|
118
|
+
mode: reportMode,
|
|
119
|
+
cwd,
|
|
120
|
+
configPath,
|
|
121
|
+
outputDir,
|
|
122
|
+
});
|
|
71
123
|
const config = await readConfig(cwd, configPath, {
|
|
72
124
|
output: outputDir,
|
|
73
125
|
plugins: {
|
|
@@ -75,6 +127,7 @@ export const executeAgentMode = async (params) => {
|
|
|
75
127
|
options: {
|
|
76
128
|
outputDir,
|
|
77
129
|
command: commandString,
|
|
130
|
+
humanReport: humanReport.statusProvider,
|
|
78
131
|
...(expectationsPath ? { expectationsPath } : {}),
|
|
79
132
|
...(inlineExpectations ? { expectations: inlineExpectations } : {}),
|
|
80
133
|
},
|
|
@@ -90,14 +143,17 @@ export const executeAgentMode = async (params) => {
|
|
|
90
143
|
console.error("could not clean output directory", error);
|
|
91
144
|
}
|
|
92
145
|
}
|
|
93
|
-
const startedAt =
|
|
94
|
-
await
|
|
146
|
+
const startedAt = Date.now();
|
|
147
|
+
await persistAgentRunState({
|
|
148
|
+
runId,
|
|
95
149
|
cwd,
|
|
96
150
|
outputDir,
|
|
151
|
+
managedOutput,
|
|
97
152
|
expectationsPath,
|
|
98
153
|
command: commandString,
|
|
99
154
|
startedAt,
|
|
100
155
|
status: "running",
|
|
156
|
+
pid: process.pid,
|
|
101
157
|
});
|
|
102
158
|
printAgentOutputLinks(outputDir);
|
|
103
159
|
if (expectationsPath) {
|
|
@@ -116,7 +172,7 @@ export const executeAgentMode = async (params) => {
|
|
|
116
172
|
qualityGate: undefined,
|
|
117
173
|
allureService: undefined,
|
|
118
174
|
realTime: false,
|
|
119
|
-
plugins: config.plugins,
|
|
175
|
+
plugins: [...humanReport.plugins, ...(config.plugins ?? [])],
|
|
120
176
|
});
|
|
121
177
|
const knownIssues = await allureReport.store.allKnownIssues();
|
|
122
178
|
const { globalExitCode } = await executeAllureRun({
|
|
@@ -133,19 +189,147 @@ export const executeAgentMode = async (params) => {
|
|
|
133
189
|
ignoreLogs: false,
|
|
134
190
|
logProcessExit: false,
|
|
135
191
|
});
|
|
136
|
-
await
|
|
192
|
+
await persistAgentRunState({
|
|
193
|
+
runId,
|
|
137
194
|
cwd,
|
|
138
195
|
outputDir,
|
|
196
|
+
managedOutput,
|
|
139
197
|
expectationsPath,
|
|
140
198
|
command: commandString,
|
|
141
199
|
startedAt,
|
|
142
|
-
finishedAt:
|
|
200
|
+
finishedAt: Date.now(),
|
|
143
201
|
status: "finished",
|
|
144
202
|
exitCode: globalExitCode.actual ?? globalExitCode.original,
|
|
203
|
+
pid: process.pid,
|
|
145
204
|
});
|
|
205
|
+
await cleanupManagedAgentOutputs({ cwd, runId, managedOutput });
|
|
146
206
|
exit(globalExitCode.actual ?? globalExitCode.original);
|
|
147
207
|
}
|
|
148
208
|
finally {
|
|
149
209
|
await rerunContext?.cleanup();
|
|
150
210
|
}
|
|
151
211
|
};
|
|
212
|
+
export const executeAgentInspectMode = async (params) => {
|
|
213
|
+
const { configPath, cwd: configuredCwd, output, expectations, inlineExpectations, environment, environmentName, reportName, open, port, historyLimit, hideLabels, dumps = [], reportMode, resultsDir, } = params;
|
|
214
|
+
const cwd = await realpath(configuredCwd ?? process.cwd());
|
|
215
|
+
const dumpFiles = dumps.length ? await findFilesByGlobs(cwd, dumps) : [];
|
|
216
|
+
const shouldReadResults = resultsDir.length > 0 || dumps.length === 0;
|
|
217
|
+
const { resultDirectories = [], patterns = resultsDir } = shouldReadResults
|
|
218
|
+
? await findAllureResultDirectories(cwd, resultsDir)
|
|
219
|
+
: {};
|
|
220
|
+
if (dumps.length > 0 && dumpFiles.length === 0) {
|
|
221
|
+
throw new AgentUsageError(`No dump files found matching pattern: ${dumps.join(", ")}`);
|
|
222
|
+
}
|
|
223
|
+
if (resultDirectories.length === 0 && dumpFiles.length === 0) {
|
|
224
|
+
const inspectedPatterns = [...(patterns ?? []), ...dumps];
|
|
225
|
+
throw new AgentUsageError(`No test results directories or dump files found matching pattern: ${inspectedPatterns}`);
|
|
226
|
+
}
|
|
227
|
+
const runId = randomUUID();
|
|
228
|
+
const managedOutput = !output;
|
|
229
|
+
const outputDir = output ? resolve(cwd, output) : await mkdtemp(join(tmpdir(), "allure-agent-"));
|
|
230
|
+
const expectationsPath = expectations ? resolve(cwd, expectations) : undefined;
|
|
231
|
+
const commandString = formatAgentInspectCommand({ dumps: dumpFiles, resultsDir: resultDirectories });
|
|
232
|
+
const hiddenLabels = hideLabels?.length ? hideLabels : undefined;
|
|
233
|
+
const environmentOptions = {
|
|
234
|
+
environment,
|
|
235
|
+
environmentName,
|
|
236
|
+
};
|
|
237
|
+
normalizeCommandEnvironmentOptions(environmentOptions);
|
|
238
|
+
if (expectationsPath && isPathInside(outputDir, expectationsPath)) {
|
|
239
|
+
throw new AgentUsageError(`--expectations path ${JSON.stringify(expectationsPath)} must not be inside the agent output directory ${JSON.stringify(outputDir)}`);
|
|
240
|
+
}
|
|
241
|
+
const historyLimitValue = historyLimit ? parseInt(historyLimit, 10) : undefined;
|
|
242
|
+
const humanReport = await createAgentHumanReportConfig({
|
|
243
|
+
mode: reportMode,
|
|
244
|
+
cwd,
|
|
245
|
+
configPath,
|
|
246
|
+
outputDir,
|
|
247
|
+
configOverride: {
|
|
248
|
+
name: reportName,
|
|
249
|
+
open,
|
|
250
|
+
port,
|
|
251
|
+
hideLabels: hiddenLabels,
|
|
252
|
+
historyLimit: historyLimitValue,
|
|
253
|
+
},
|
|
254
|
+
});
|
|
255
|
+
const config = await readConfig(cwd, configPath, {
|
|
256
|
+
name: reportName,
|
|
257
|
+
output: outputDir,
|
|
258
|
+
open,
|
|
259
|
+
port,
|
|
260
|
+
hideLabels: hiddenLabels,
|
|
261
|
+
historyLimit: historyLimitValue,
|
|
262
|
+
plugins: {
|
|
263
|
+
agent: {
|
|
264
|
+
options: {
|
|
265
|
+
outputDir,
|
|
266
|
+
command: commandString,
|
|
267
|
+
humanReport: humanReport.statusProvider,
|
|
268
|
+
...(expectationsPath ? { expectationsPath } : {}),
|
|
269
|
+
...(inlineExpectations ? { expectations: inlineExpectations } : {}),
|
|
270
|
+
},
|
|
271
|
+
},
|
|
272
|
+
},
|
|
273
|
+
});
|
|
274
|
+
const resolvedEnvironment = resolveCommandEnvironment(config, environmentOptions);
|
|
275
|
+
try {
|
|
276
|
+
await rm(outputDir, { recursive: true });
|
|
277
|
+
}
|
|
278
|
+
catch (error) {
|
|
279
|
+
if (!isFileNotFoundError(error)) {
|
|
280
|
+
console.error("could not clean output directory", error);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
const startedAt = Date.now();
|
|
284
|
+
await persistAgentRunState({
|
|
285
|
+
runId,
|
|
286
|
+
cwd,
|
|
287
|
+
outputDir,
|
|
288
|
+
managedOutput,
|
|
289
|
+
expectationsPath,
|
|
290
|
+
command: commandString,
|
|
291
|
+
startedAt,
|
|
292
|
+
status: "running",
|
|
293
|
+
pid: process.pid,
|
|
294
|
+
});
|
|
295
|
+
printAgentOutputLinks(outputDir);
|
|
296
|
+
if (expectationsPath) {
|
|
297
|
+
console.log(`agent expectations: ${expectationsPath}`);
|
|
298
|
+
}
|
|
299
|
+
else if (inlineExpectations) {
|
|
300
|
+
console.log("agent expectations: CLI options");
|
|
301
|
+
}
|
|
302
|
+
console.log(commandString);
|
|
303
|
+
const allureReport = new AllureReport({
|
|
304
|
+
...config,
|
|
305
|
+
output: outputDir,
|
|
306
|
+
environment: resolvedEnvironment?.id,
|
|
307
|
+
open: false,
|
|
308
|
+
port: undefined,
|
|
309
|
+
qualityGate: undefined,
|
|
310
|
+
allureService: undefined,
|
|
311
|
+
realTime: false,
|
|
312
|
+
plugins: [...humanReport.plugins, ...(config.plugins ?? [])],
|
|
313
|
+
});
|
|
314
|
+
await allureReport.restoreState(Array.from(dumpFiles));
|
|
315
|
+
await allureReport.start();
|
|
316
|
+
for (const dir of resultDirectories) {
|
|
317
|
+
await allureReport.readDirectory(dir);
|
|
318
|
+
}
|
|
319
|
+
await allureReport.done();
|
|
320
|
+
await persistAgentRunState({
|
|
321
|
+
runId,
|
|
322
|
+
cwd,
|
|
323
|
+
outputDir,
|
|
324
|
+
managedOutput,
|
|
325
|
+
expectationsPath,
|
|
326
|
+
command: commandString,
|
|
327
|
+
startedAt,
|
|
328
|
+
finishedAt: Date.now(),
|
|
329
|
+
status: "finished",
|
|
330
|
+
exitCode: 0,
|
|
331
|
+
pid: process.pid,
|
|
332
|
+
});
|
|
333
|
+
await cleanupManagedAgentOutputs({ cwd, runId, managedOutput });
|
|
334
|
+
exit(0);
|
|
335
|
+
};
|
package/dist/commands/agent.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ export declare class AgentCommand extends Command {
|
|
|
13
13
|
config: string | undefined;
|
|
14
14
|
cwd: string | undefined;
|
|
15
15
|
output: string | undefined;
|
|
16
|
+
report: string | undefined;
|
|
16
17
|
expectations: string | undefined;
|
|
17
18
|
goal: string[] | undefined;
|
|
18
19
|
taskId: string[] | undefined;
|
|
@@ -37,6 +38,37 @@ export declare class AgentCommand extends Command {
|
|
|
37
38
|
commandToRun: string[];
|
|
38
39
|
execute(): Promise<void>;
|
|
39
40
|
}
|
|
41
|
+
export declare class AgentInspectCommand extends Command {
|
|
42
|
+
static paths: string[][];
|
|
43
|
+
static usage: import("clipanion").Usage;
|
|
44
|
+
resultsDir: string[];
|
|
45
|
+
config: string | undefined;
|
|
46
|
+
cwd: string | undefined;
|
|
47
|
+
output: string | undefined;
|
|
48
|
+
report: string | undefined;
|
|
49
|
+
reportName: string | undefined;
|
|
50
|
+
dump: string[] | undefined;
|
|
51
|
+
open: boolean | undefined;
|
|
52
|
+
port: string | undefined;
|
|
53
|
+
historyLimit: string | undefined;
|
|
54
|
+
hideLabels: string[] | undefined;
|
|
55
|
+
expectations: string | undefined;
|
|
56
|
+
goal: string[] | undefined;
|
|
57
|
+
taskId: string[] | undefined;
|
|
58
|
+
expectTests: string[] | undefined;
|
|
59
|
+
expectLabels: string[] | undefined;
|
|
60
|
+
expectEnvironments: string[] | undefined;
|
|
61
|
+
expectFullNames: string[] | undefined;
|
|
62
|
+
expectPrefixes: string[] | undefined;
|
|
63
|
+
forbidLabels: string[] | undefined;
|
|
64
|
+
expectStepContains: string[] | undefined;
|
|
65
|
+
expectSteps: string[] | undefined;
|
|
66
|
+
expectAttachments: string[] | undefined;
|
|
67
|
+
expectAttachmentFilters: string[] | undefined;
|
|
68
|
+
environment: string | undefined;
|
|
69
|
+
environmentName: string | undefined;
|
|
70
|
+
execute(): Promise<void>;
|
|
71
|
+
}
|
|
40
72
|
export declare class AgentLatestCommand extends Command {
|
|
41
73
|
static paths: string[][];
|
|
42
74
|
static usage: import("clipanion").Usage;
|
package/dist/commands/agent.js
CHANGED
|
@@ -1,26 +1,83 @@
|
|
|
1
1
|
import * as console from "node:console";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
2
3
|
import { mkdir, mkdtemp, realpath, writeFile } from "node:fs/promises";
|
|
3
4
|
import { tmpdir } from "node:os";
|
|
4
5
|
import { dirname, join, resolve } from "node:path";
|
|
5
6
|
import process, { exit } from "node:process";
|
|
6
|
-
import { AGENT_FINDING_CATEGORIES, AGENT_FINDING_SEVERITIES, AGENT_TASK_MAP_HELP, AGENT_TEST_STATUSES, AgentExpectationUsageError, buildAgentInlineExpectations, buildAgentQueryPayload, createAgentCapabilities, formatAgentOutputLinks, isAgentExpectationUsageError, isAgentTaskMapHelpRequest, isAgentUsageError, loadAgentOutput, normalizeAgentQueryLimit, normalizeAgentQueryView, normalizeAgentRerunPreset, normalizeRepeatedEnumValues, normalizeRepeatedStringValues, parseAgentLabelFilters, readLatestAgentState, resolveAgentSelectionOutputDir, resolveAgentStateDir, selectAgentTestPlan, validateAgentExpectationsFile,
|
|
7
|
+
import { AGENT_FINDING_CATEGORIES, AGENT_FINDING_SEVERITIES, AGENT_TASK_MAP_HELP, AGENT_TEST_STATUSES, AgentExpectationUsageError, 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";
|
|
7
8
|
import { Command, Option, UsageError } from "clipanion";
|
|
8
9
|
export { AGENT_TASK_MAP_HELP, createAgentCapabilities, isAgentTaskMapHelpRequest };
|
|
9
10
|
const readOptionalString = (value) => (typeof value === "string" ? value : undefined);
|
|
10
11
|
const readOptionalBoolean = (value) => value === true;
|
|
11
12
|
const readOptionalStringArray = (value) => (Array.isArray(value) ? value : undefined);
|
|
12
13
|
const formatAgentCommand = (args) => args.join(" ");
|
|
14
|
+
const AGENT_HUMAN_REPORT_MODES = ["auto", "off", "awesome", "config"];
|
|
15
|
+
const formatAgentInspectCommand = (params) => [
|
|
16
|
+
"allure",
|
|
17
|
+
"agent",
|
|
18
|
+
"inspect",
|
|
19
|
+
...(params.dumps ?? []).flatMap((dump) => ["--dump", dump]),
|
|
20
|
+
...(params.resultsDir ?? []),
|
|
21
|
+
].join(" ");
|
|
22
|
+
const buildInlineExpectationsFromOptions = (options) => buildAgentInlineExpectations({
|
|
23
|
+
goal: options.goal,
|
|
24
|
+
taskId: options.taskId,
|
|
25
|
+
expectTests: options.expectTests,
|
|
26
|
+
expectLabels: readOptionalStringArray(options.expectLabels),
|
|
27
|
+
expectEnvironments: readOptionalStringArray(options.expectEnvironments),
|
|
28
|
+
expectFullNames: readOptionalStringArray(options.expectFullNames),
|
|
29
|
+
expectPrefixes: readOptionalStringArray(options.expectPrefixes),
|
|
30
|
+
forbidLabels: readOptionalStringArray(options.forbidLabels),
|
|
31
|
+
expectStepContains: readOptionalStringArray(options.expectStepContains),
|
|
32
|
+
expectSteps: options.expectSteps,
|
|
33
|
+
expectAttachments: options.expectAttachments,
|
|
34
|
+
expectAttachmentFilters: readOptionalStringArray(options.expectAttachmentFilters),
|
|
35
|
+
});
|
|
13
36
|
const printAgentOutputLinks = (outputDir) => {
|
|
14
37
|
for (const line of formatAgentOutputLinks(outputDir)) {
|
|
15
38
|
console.log(line);
|
|
16
39
|
}
|
|
17
40
|
};
|
|
18
|
-
const
|
|
41
|
+
const persistAgentRunState = async (value) => {
|
|
42
|
+
try {
|
|
43
|
+
await writeAgentRunState(value);
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
console.error(`Could not update agent state in ${resolveAgentStateDir(value.cwd)}: ${error.message}`);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
const logAgentCleanupFailures = (failures) => {
|
|
50
|
+
for (const failure of failures) {
|
|
51
|
+
console.error(`Could not clean stale agent output ${failure.state.outputDir}: ${failure.error.message}`);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
const logAgentOrphanCleanupFailures = (failures) => {
|
|
55
|
+
for (const failure of failures) {
|
|
56
|
+
console.error(`Could not clean stale agent output ${failure.outputDir}: ${failure.error.message}`);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
const cleanupManagedAgentOutputs = async (params) => {
|
|
19
60
|
try {
|
|
20
|
-
await
|
|
61
|
+
const result = await cleanupAgentRunState({
|
|
62
|
+
cwd: params.cwd,
|
|
63
|
+
currentRunId: params.runId,
|
|
64
|
+
keepManagedRuns: params.managedOutput ? 1 : 0,
|
|
65
|
+
});
|
|
66
|
+
logAgentCleanupFailures(result.failed);
|
|
21
67
|
}
|
|
22
68
|
catch (error) {
|
|
23
|
-
console.error(`Could not
|
|
69
|
+
console.error(`Could not clean agent state in ${resolveAgentStateDir(params.cwd)}: ${error.message}`);
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
const staleResult = await cleanupStaleAgentRunStates({
|
|
73
|
+
cwd: params.cwd,
|
|
74
|
+
currentRunId: params.runId,
|
|
75
|
+
});
|
|
76
|
+
logAgentCleanupFailures(staleResult.failed);
|
|
77
|
+
logAgentOrphanCleanupFailures(staleResult.orphaned.failed);
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
console.error(`Could not clean agent state in ${resolveAgentStateDir(params.cwd)}: ${error.message}`);
|
|
24
81
|
}
|
|
25
82
|
};
|
|
26
83
|
const agentEnvironmentOption = () => Option.String("--environment,--env", {
|
|
@@ -35,6 +92,15 @@ const throwCliUsageError = (error) => {
|
|
|
35
92
|
}
|
|
36
93
|
throw error;
|
|
37
94
|
};
|
|
95
|
+
const normalizeAgentHumanReportMode = (value) => {
|
|
96
|
+
if (value === undefined) {
|
|
97
|
+
return "auto";
|
|
98
|
+
}
|
|
99
|
+
if (AGENT_HUMAN_REPORT_MODES.includes(value)) {
|
|
100
|
+
return value;
|
|
101
|
+
}
|
|
102
|
+
throw new UsageError(`Unsupported --report mode ${JSON.stringify(value)}. Use auto, off, awesome, or config.`);
|
|
103
|
+
};
|
|
38
104
|
export class AgentCapabilitiesCommand extends Command {
|
|
39
105
|
constructor() {
|
|
40
106
|
super(...arguments);
|
|
@@ -65,7 +131,10 @@ export class AgentCommand extends Command {
|
|
|
65
131
|
description: "The working directory for the command to run (default: current working directory)",
|
|
66
132
|
});
|
|
67
133
|
this.output = Option.String("--output,-o", {
|
|
68
|
-
description: "The output directory for agent artifacts. Absolute paths are accepted
|
|
134
|
+
description: "The output directory for agent artifacts. Absolute paths are accepted. Explicit output is caller-managed; remove or archive it when no longer needed so later state compaction can drop its record",
|
|
135
|
+
});
|
|
136
|
+
this.report = Option.String("--report", {
|
|
137
|
+
description: "Human report mode: auto, off, awesome, or config. Auto writes awesome/index.html for 1000 or fewer stored visible results and records manifest/human-report.json (default: auto)",
|
|
69
138
|
});
|
|
70
139
|
this.expectations = Option.String("--expectations", {
|
|
71
140
|
description: "The path to a YAML or JSON expectations file",
|
|
@@ -133,24 +202,25 @@ export class AgentCommand extends Command {
|
|
|
133
202
|
const configPath = readOptionalString(this.config);
|
|
134
203
|
const configuredCwd = readOptionalString(this.cwd);
|
|
135
204
|
const output = readOptionalString(this.output);
|
|
205
|
+
const reportMode = normalizeAgentHumanReportMode(readOptionalString(this.report));
|
|
136
206
|
const expectations = readOptionalString(this.expectations);
|
|
137
|
-
if (!args
|
|
207
|
+
if (!args.length) {
|
|
138
208
|
throw new UsageError("expecting command to be specified after --, e.g. allure agent -- npm run test");
|
|
139
209
|
}
|
|
140
210
|
try {
|
|
141
|
-
const inlineExpectations =
|
|
211
|
+
const inlineExpectations = buildInlineExpectationsFromOptions({
|
|
142
212
|
goal: this.goal,
|
|
143
213
|
taskId: this.taskId,
|
|
144
214
|
expectTests: this.expectTests,
|
|
145
|
-
expectLabels:
|
|
146
|
-
expectEnvironments:
|
|
147
|
-
expectFullNames:
|
|
148
|
-
expectPrefixes:
|
|
149
|
-
forbidLabels:
|
|
150
|
-
expectStepContains:
|
|
215
|
+
expectLabels: this.expectLabels,
|
|
216
|
+
expectEnvironments: this.expectEnvironments,
|
|
217
|
+
expectFullNames: this.expectFullNames,
|
|
218
|
+
expectPrefixes: this.expectPrefixes,
|
|
219
|
+
forbidLabels: this.forbidLabels,
|
|
220
|
+
expectStepContains: this.expectStepContains,
|
|
151
221
|
expectSteps: this.expectSteps,
|
|
152
222
|
expectAttachments: this.expectAttachments,
|
|
153
|
-
expectAttachmentFilters:
|
|
223
|
+
expectAttachmentFilters: this.expectAttachmentFilters,
|
|
154
224
|
});
|
|
155
225
|
if (expectations && inlineExpectations) {
|
|
156
226
|
throw new AgentExpectationUsageError("Use either --expectations <file> or inline expectation flags, not both", "--expectations");
|
|
@@ -175,6 +245,7 @@ export class AgentCommand extends Command {
|
|
|
175
245
|
rerunPreset: readOptionalString(this.rerunPreset),
|
|
176
246
|
rerunEnvironments: readOptionalStringArray(this.rerunEnvironments),
|
|
177
247
|
rerunLabels: readOptionalStringArray(this.rerunLabels),
|
|
248
|
+
reportMode,
|
|
178
249
|
args,
|
|
179
250
|
});
|
|
180
251
|
}
|
|
@@ -184,6 +255,8 @@ export class AgentCommand extends Command {
|
|
|
184
255
|
}
|
|
185
256
|
const expectationError = error;
|
|
186
257
|
const cwd = await realpath(configuredCwd ?? process.cwd());
|
|
258
|
+
const runId = randomUUID();
|
|
259
|
+
const managedOutput = !output;
|
|
187
260
|
const outputDir = output ? resolve(cwd, output) : await mkdtemp(join(tmpdir(), "allure-agent-"));
|
|
188
261
|
const commandString = formatAgentCommand(args);
|
|
189
262
|
const { generatedAt } = await writeInvalidAgentExpectationOutput({
|
|
@@ -191,15 +264,21 @@ export class AgentCommand extends Command {
|
|
|
191
264
|
command: commandString,
|
|
192
265
|
error: expectationError,
|
|
193
266
|
});
|
|
194
|
-
|
|
267
|
+
const generatedAtMs = Date.parse(generatedAt);
|
|
268
|
+
const generatedAtTimestamp = Number.isFinite(generatedAtMs) ? generatedAtMs : Date.now();
|
|
269
|
+
await persistAgentRunState({
|
|
270
|
+
runId,
|
|
195
271
|
cwd,
|
|
196
272
|
outputDir,
|
|
273
|
+
managedOutput,
|
|
197
274
|
command: commandString,
|
|
198
|
-
startedAt:
|
|
199
|
-
finishedAt:
|
|
275
|
+
startedAt: generatedAtTimestamp,
|
|
276
|
+
finishedAt: generatedAtTimestamp,
|
|
200
277
|
status: "finished",
|
|
201
278
|
exitCode: 1,
|
|
279
|
+
pid: process.pid,
|
|
202
280
|
});
|
|
281
|
+
await cleanupManagedAgentOutputs({ cwd, runId, managedOutput });
|
|
203
282
|
printAgentOutputLinks(outputDir);
|
|
204
283
|
console.error(expectationError.message);
|
|
205
284
|
exit(1);
|
|
@@ -209,15 +288,206 @@ export class AgentCommand extends Command {
|
|
|
209
288
|
AgentCommand.paths = [["agent"]];
|
|
210
289
|
AgentCommand.usage = Command.Usage({
|
|
211
290
|
description: "Run specified command in Allure agent mode",
|
|
212
|
-
details: "This command runs the specified command with an agent-
|
|
291
|
+
details: "This command runs the specified command with an agent-mode Allure profile and optional human-readable report output. With the default --report auto mode, runs with 1000 or fewer stored visible logical results also write a single-file Awesome report at <output>/awesome/index.html and record its status in <output>/manifest/human-report.json. If a user asks for the report after a run, use `allure agent latest` to recover the output directory and check the human-report manifest before regenerating anything.",
|
|
213
292
|
examples: [
|
|
214
|
-
["agent -- npm test", "Run npm test and capture
|
|
293
|
+
["agent -- npm test", "Run npm test and capture agent-mode output with the default human report policy"],
|
|
294
|
+
["agent --report off -- npm test", "Run npm test and capture only the agent-mode artifacts"],
|
|
215
295
|
[
|
|
216
296
|
"agent --expectations ./expected.yaml -- npm test",
|
|
217
297
|
"Run npm test with agent-mode expectations loaded from ./expected.yaml",
|
|
218
298
|
],
|
|
219
299
|
],
|
|
220
300
|
});
|
|
301
|
+
export class AgentInspectCommand extends Command {
|
|
302
|
+
constructor() {
|
|
303
|
+
super(...arguments);
|
|
304
|
+
this.resultsDir = Option.Rest({
|
|
305
|
+
name: "Patterns to match test results directories in the current working directory (default: ./**/allure-results)",
|
|
306
|
+
});
|
|
307
|
+
this.config = Option.String("--config,-c", {
|
|
308
|
+
description: "The path Allure config file",
|
|
309
|
+
});
|
|
310
|
+
this.cwd = Option.String("--cwd", {
|
|
311
|
+
description: "The working directory for resolving results and dumps (default: current working directory)",
|
|
312
|
+
});
|
|
313
|
+
this.output = Option.String("--output,-o", {
|
|
314
|
+
description: "The output directory for agent artifacts. Absolute paths are accepted. Explicit output is caller-managed; remove or archive it when no longer needed so later state compaction can drop its record",
|
|
315
|
+
});
|
|
316
|
+
this.report = Option.String("--report", {
|
|
317
|
+
description: "Human report mode: auto, off, awesome, or config. Auto writes awesome/index.html for 1000 or fewer stored visible results and records manifest/human-report.json (default: auto)",
|
|
318
|
+
});
|
|
319
|
+
this.reportName = Option.String("--report-name,--name", {
|
|
320
|
+
description: "The report name to pass through configuration defaults (default: Allure Report)",
|
|
321
|
+
});
|
|
322
|
+
this.dump = Option.Array("--dump", {
|
|
323
|
+
description: "Path or pattern that matches one or more archives created by `allure run --dump ...`. " +
|
|
324
|
+
"This option can be specified multiple times.",
|
|
325
|
+
});
|
|
326
|
+
this.open = Option.Boolean("--open", {
|
|
327
|
+
description: "Accepted for parity with generate. Agent inspect writes agent artifacts and prints their paths",
|
|
328
|
+
});
|
|
329
|
+
this.port = Option.String("--port", {
|
|
330
|
+
description: "Accepted for parity with generate. Agent inspect doesn't serve the output",
|
|
331
|
+
});
|
|
332
|
+
this.historyLimit = Option.String("--history-limit", {
|
|
333
|
+
description: "Limits the number of history entries to keep (default: unlimited)",
|
|
334
|
+
});
|
|
335
|
+
this.hideLabels = Option.Array("--hide-labels", {
|
|
336
|
+
description: "Hide labels by exact name in generated data. Repeat the option for multiple labels",
|
|
337
|
+
});
|
|
338
|
+
this.expectations = Option.String("--expectations", {
|
|
339
|
+
description: "The path to a YAML or JSON expectations file",
|
|
340
|
+
});
|
|
341
|
+
this.goal = Option.Array("--goal", {
|
|
342
|
+
description: "The review goal to record in inline agent expectations",
|
|
343
|
+
});
|
|
344
|
+
this.taskId = Option.Array("--task-id", {
|
|
345
|
+
description: "The task or feature id to record in inline agent expectations",
|
|
346
|
+
});
|
|
347
|
+
this.expectTests = Option.Array("--expect-tests", {
|
|
348
|
+
description: "The expected number of visible logical tests in the intended scope",
|
|
349
|
+
});
|
|
350
|
+
this.expectLabels = Option.Array("--expect-label", {
|
|
351
|
+
description: "Expected label selector in name=value form. Repeat the option for multiple selectors",
|
|
352
|
+
});
|
|
353
|
+
this.expectEnvironments = Option.Array("--expect-env", {
|
|
354
|
+
description: "Expected environment id. Repeat the option for multiple environments",
|
|
355
|
+
});
|
|
356
|
+
this.expectFullNames = Option.Array("--expect-test", {
|
|
357
|
+
description: "Expected full test name. Repeat the option for multiple tests",
|
|
358
|
+
});
|
|
359
|
+
this.expectPrefixes = Option.Array("--expect-prefix", {
|
|
360
|
+
description: "Expected full-name prefix. Repeat the option for multiple prefixes",
|
|
361
|
+
});
|
|
362
|
+
this.forbidLabels = Option.Array("--forbid-label", {
|
|
363
|
+
description: "Forbidden label selector in name=value form. Repeat the option for multiple selectors",
|
|
364
|
+
});
|
|
365
|
+
this.expectStepContains = Option.Array("--expect-step-containing", {
|
|
366
|
+
description: "Require a test-scoped step name containing this text per evidence-target logical test",
|
|
367
|
+
});
|
|
368
|
+
this.expectSteps = Option.Array("--expect-steps", {
|
|
369
|
+
description: "Require at least this many meaningful steps per expected logical test",
|
|
370
|
+
});
|
|
371
|
+
this.expectAttachments = Option.Array("--expect-attachments", {
|
|
372
|
+
description: "Require at least this many non-missing attachments per expected logical test",
|
|
373
|
+
});
|
|
374
|
+
this.expectAttachmentFilters = Option.Array("--expect-attachment", {
|
|
375
|
+
description: "Require a matching non-missing attachment per expected logical test. Use a file name or name=value/content-type=value",
|
|
376
|
+
});
|
|
377
|
+
this.environment = agentEnvironmentOption();
|
|
378
|
+
this.environmentName = agentEnvironmentNameOption();
|
|
379
|
+
}
|
|
380
|
+
async execute() {
|
|
381
|
+
const configPath = readOptionalString(this.config);
|
|
382
|
+
const configuredCwd = readOptionalString(this.cwd);
|
|
383
|
+
const output = readOptionalString(this.output);
|
|
384
|
+
const reportMode = normalizeAgentHumanReportMode(readOptionalString(this.report));
|
|
385
|
+
const dumps = readOptionalStringArray(this.dump) ?? [];
|
|
386
|
+
const resultsDir = this.resultsDir;
|
|
387
|
+
const expectations = readOptionalString(this.expectations);
|
|
388
|
+
if (resultsDir.includes("--")) {
|
|
389
|
+
throw new UsageError("agent inspect does not run commands; pass result directories directly, e.g. allure agent inspect ./allure-results");
|
|
390
|
+
}
|
|
391
|
+
try {
|
|
392
|
+
const inlineExpectations = buildInlineExpectationsFromOptions({
|
|
393
|
+
goal: this.goal,
|
|
394
|
+
taskId: this.taskId,
|
|
395
|
+
expectTests: this.expectTests,
|
|
396
|
+
expectLabels: this.expectLabels,
|
|
397
|
+
expectEnvironments: this.expectEnvironments,
|
|
398
|
+
expectFullNames: this.expectFullNames,
|
|
399
|
+
expectPrefixes: this.expectPrefixes,
|
|
400
|
+
forbidLabels: this.forbidLabels,
|
|
401
|
+
expectStepContains: this.expectStepContains,
|
|
402
|
+
expectSteps: this.expectSteps,
|
|
403
|
+
expectAttachments: this.expectAttachments,
|
|
404
|
+
expectAttachmentFilters: this.expectAttachmentFilters,
|
|
405
|
+
});
|
|
406
|
+
if (expectations && inlineExpectations) {
|
|
407
|
+
throw new AgentExpectationUsageError("Use either --expectations <file> or inline expectation flags, not both", "--expectations");
|
|
408
|
+
}
|
|
409
|
+
await validateAgentExpectationsFile({
|
|
410
|
+
cwd: await realpath(configuredCwd ?? process.cwd()),
|
|
411
|
+
output,
|
|
412
|
+
expectations,
|
|
413
|
+
});
|
|
414
|
+
const { executeAgentInspectMode } = await import("./agent-run.js");
|
|
415
|
+
await executeAgentInspectMode({
|
|
416
|
+
configPath,
|
|
417
|
+
cwd: configuredCwd,
|
|
418
|
+
output,
|
|
419
|
+
expectations,
|
|
420
|
+
inlineExpectations: inlineExpectations,
|
|
421
|
+
environment: readOptionalString(this.environment),
|
|
422
|
+
environmentName: readOptionalString(this.environmentName),
|
|
423
|
+
reportName: readOptionalString(this.reportName),
|
|
424
|
+
open: readOptionalBoolean(this.open),
|
|
425
|
+
port: readOptionalString(this.port),
|
|
426
|
+
historyLimit: readOptionalString(this.historyLimit),
|
|
427
|
+
hideLabels: readOptionalStringArray(this.hideLabels),
|
|
428
|
+
dumps,
|
|
429
|
+
reportMode,
|
|
430
|
+
resultsDir,
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
catch (error) {
|
|
434
|
+
if (!isAgentExpectationUsageError(error)) {
|
|
435
|
+
throwCliUsageError(error);
|
|
436
|
+
}
|
|
437
|
+
const expectationError = error;
|
|
438
|
+
const cwd = await realpath(configuredCwd ?? process.cwd());
|
|
439
|
+
const runId = randomUUID();
|
|
440
|
+
const managedOutput = !output;
|
|
441
|
+
const outputDir = output ? resolve(cwd, output) : await mkdtemp(join(tmpdir(), "allure-agent-"));
|
|
442
|
+
const commandString = formatAgentInspectCommand({ dumps, resultsDir });
|
|
443
|
+
const { generatedAt } = await writeInvalidAgentExpectationOutput({
|
|
444
|
+
outputDir,
|
|
445
|
+
command: commandString,
|
|
446
|
+
error: expectationError,
|
|
447
|
+
});
|
|
448
|
+
const generatedAtMs = Date.parse(generatedAt);
|
|
449
|
+
const generatedAtTimestamp = Number.isFinite(generatedAtMs) ? generatedAtMs : Date.now();
|
|
450
|
+
await persistAgentRunState({
|
|
451
|
+
runId,
|
|
452
|
+
cwd,
|
|
453
|
+
outputDir,
|
|
454
|
+
managedOutput,
|
|
455
|
+
command: commandString,
|
|
456
|
+
startedAt: generatedAtTimestamp,
|
|
457
|
+
finishedAt: generatedAtTimestamp,
|
|
458
|
+
status: "finished",
|
|
459
|
+
exitCode: 1,
|
|
460
|
+
pid: process.pid,
|
|
461
|
+
});
|
|
462
|
+
await cleanupManagedAgentOutputs({ cwd, runId, managedOutput });
|
|
463
|
+
printAgentOutputLinks(outputDir);
|
|
464
|
+
console.error(expectationError.message);
|
|
465
|
+
exit(1);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
AgentInspectCommand.paths = [["agent", "inspect"]];
|
|
470
|
+
AgentInspectCommand.usage = Command.Usage({
|
|
471
|
+
description: "Inspect existing Allure results or dump archives in Allure agent mode",
|
|
472
|
+
details: "This command restores dump archives and reads existing Allure results directories, then writes agent-mode output and optional human-readable report output without running a test command. With the default --report auto mode, inputs with 1000 or fewer stored visible logical results also write a single-file Awesome report at <output>/awesome/index.html and record its status in <output>/manifest/human-report.json. If a user asks for the report after an inspect run, use `allure agent latest` to recover the output directory and check the human-report manifest before regenerating anything.",
|
|
473
|
+
examples: [
|
|
474
|
+
["agent inspect ./allure-results", "Inspect an existing Allure results directory"],
|
|
475
|
+
["agent inspect ./packages/*/out/allure-results", "Inspect matching Allure results directories"],
|
|
476
|
+
["agent inspect --dump allure-results-linux.zip", "Inspect a dump archive downloaded from CI"],
|
|
477
|
+
[
|
|
478
|
+
"agent inspect --dump allure-results-linux.zip --dump allure-results-macos.zip",
|
|
479
|
+
"Inspect multiple CI dump archives together",
|
|
480
|
+
],
|
|
481
|
+
[
|
|
482
|
+
"agent inspect --dump allure-results-linux.zip ./local/allure-results",
|
|
483
|
+
"Inspect dump archives together with local results directories",
|
|
484
|
+
],
|
|
485
|
+
[
|
|
486
|
+
"agent inspect --config ./allurerc.mjs --output ./agent-output ./allure-results",
|
|
487
|
+
"Inspect existing results with a config file and write agent artifacts to ./agent-output",
|
|
488
|
+
],
|
|
489
|
+
],
|
|
490
|
+
});
|
|
221
491
|
export class AgentLatestCommand extends Command {
|
|
222
492
|
constructor() {
|
|
223
493
|
super(...arguments);
|
|
@@ -247,7 +517,7 @@ export class AgentLatestCommand extends Command {
|
|
|
247
517
|
AgentLatestCommand.paths = [["agent", "latest"]];
|
|
248
518
|
AgentLatestCommand.usage = Command.Usage({
|
|
249
519
|
description: "Print the latest Allure agent output directory and index path for the current project",
|
|
250
|
-
details: "This command prints the latest agent output directory and index.md path recorded for the resolved project cwd.",
|
|
520
|
+
details: "This command prints the latest agent output directory and index.md path recorded for the resolved project cwd. When a user asks for a human-readable report from the last run, use this output directory to check manifest/human-report.json; if its status is generated, the report path is relative to that same output directory, usually awesome/index.html.",
|
|
251
521
|
examples: [
|
|
252
522
|
["agent latest", "Print the latest agent output directory and index path for the current project"],
|
|
253
523
|
[
|
|
@@ -270,11 +540,11 @@ export class AgentStateDirCommand extends Command {
|
|
|
270
540
|
}
|
|
271
541
|
AgentStateDirCommand.paths = [["agent", "state-dir"]];
|
|
272
542
|
AgentStateDirCommand.usage = Command.Usage({
|
|
273
|
-
description: "Print the Allure agent state directory
|
|
274
|
-
details: "This command prints the resolved state directory used to persist
|
|
543
|
+
description: "Print the shared Allure agent state directory",
|
|
544
|
+
details: "This command prints the resolved state directory used to persist per-project agent run registries.",
|
|
275
545
|
examples: [
|
|
276
|
-
["agent state-dir", "Print the resolved state directory
|
|
277
|
-
["agent state-dir --cwd ./packages/cli", "Print the
|
|
546
|
+
["agent state-dir", "Print the resolved shared state directory"],
|
|
547
|
+
["agent state-dir --cwd ./packages/cli", "Print the shared state directory using a specific project cwd"],
|
|
278
548
|
],
|
|
279
549
|
});
|
|
280
550
|
export class AgentQueryCommand extends Command {
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
import process, { argv } from "node:process";
|
|
3
3
|
import { Builtins, Cli } from "clipanion";
|
|
4
|
-
import { AgentCommand, AGENT_TASK_MAP_HELP, AgentCapabilitiesCommand, AgentLatestCommand, AgentQueryCommand, AgentSelectCommand, AgentStateDirCommand, Allure2Command, AwesomeCommand, CheckCommand, ClassicCommand, CsvCommand, DashboardCommand, GenerateCommand, HistoryCommand, JiraClearCommand, KnownIssueCommand, LogCommand, OpenCommand, QualityGateCommand, ResultsPackCommand, ResultsUnpackCommand, RunCommand, SlackCommand, TestPlanCommand, WatchCommand, isAgentTaskMapHelpRequest, } from "./commands/index.js";
|
|
4
|
+
import { AgentCommand, AGENT_TASK_MAP_HELP, AgentCapabilitiesCommand, AgentInspectCommand, AgentLatestCommand, AgentQueryCommand, AgentSelectCommand, AgentStateDirCommand, Allure2Command, AwesomeCommand, CheckCommand, ClassicCommand, CsvCommand, DashboardCommand, GenerateCommand, HistoryCommand, JiraClearCommand, KnownIssueCommand, LogCommand, OpenCommand, QualityGateCommand, ResultsPackCommand, ResultsUnpackCommand, RunCommand, SlackCommand, TestPlanCommand, WatchCommand, isAgentTaskMapHelpRequest, } from "./commands/index.js";
|
|
5
5
|
const [node, app, ...args] = argv;
|
|
6
6
|
const pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
|
|
7
7
|
const cli = new Cli({
|
|
@@ -12,6 +12,7 @@ const cli = new Cli({
|
|
|
12
12
|
cli.register(AwesomeCommand);
|
|
13
13
|
cli.register(Allure2Command);
|
|
14
14
|
cli.register(AgentCapabilitiesCommand);
|
|
15
|
+
cli.register(AgentInspectCommand);
|
|
15
16
|
cli.register(AgentLatestCommand);
|
|
16
17
|
cli.register(AgentQueryCommand);
|
|
17
18
|
cli.register(AgentSelectCommand);
|
package/dist/utils/process.js
CHANGED
|
@@ -4,6 +4,50 @@ import { platform } from "process";
|
|
|
4
4
|
import { invokeStdoutCliTool } from "@allurereport/reader-api";
|
|
5
5
|
const IS_WIN = platform === "win32";
|
|
6
6
|
const PS_OUTPUT_PATTERN = /(?<ppid>\d+)\s+(?<pid>\d+)\s+(?<comm>.*)/;
|
|
7
|
+
const quotePosixShellArg = (arg) => {
|
|
8
|
+
if (/^[A-Za-z0-9_/:=.,@%+-]+$/u.test(arg)) {
|
|
9
|
+
return arg;
|
|
10
|
+
}
|
|
11
|
+
return `'${arg.replace(/'/g, "'\\''")}'`;
|
|
12
|
+
};
|
|
13
|
+
const quoteWindowsShellArg = (arg) => {
|
|
14
|
+
if (arg && !/[\s"&()<>^|%]/u.test(arg)) {
|
|
15
|
+
return arg;
|
|
16
|
+
}
|
|
17
|
+
let quoted = '"';
|
|
18
|
+
let backslashes = 0;
|
|
19
|
+
for (const char of arg) {
|
|
20
|
+
if (char === "\\") {
|
|
21
|
+
backslashes += 1;
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
if (char === '"') {
|
|
25
|
+
quoted += "\\".repeat(backslashes * 2 + 1);
|
|
26
|
+
quoted += char;
|
|
27
|
+
backslashes = 0;
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
quoted += "\\".repeat(backslashes);
|
|
31
|
+
backslashes = 0;
|
|
32
|
+
quoted += char;
|
|
33
|
+
}
|
|
34
|
+
quoted += "\\".repeat(backslashes * 2);
|
|
35
|
+
quoted += '"';
|
|
36
|
+
return quoted;
|
|
37
|
+
};
|
|
38
|
+
const quoteShellArg = IS_WIN ? quoteWindowsShellArg : quotePosixShellArg;
|
|
39
|
+
const resolveShellInvocation = (command, commandArgs, shell) => {
|
|
40
|
+
if (!shell || commandArgs.length === 0) {
|
|
41
|
+
return {
|
|
42
|
+
command,
|
|
43
|
+
commandArgs,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
command: [command, ...commandArgs].map(quoteShellArg).join(" "),
|
|
48
|
+
commandArgs: [],
|
|
49
|
+
};
|
|
50
|
+
};
|
|
7
51
|
export const runProcess = (params) => {
|
|
8
52
|
const { command, commandArgs, cwd, environmentVariables = {}, logs = "inherit", shell = IS_WIN } = params;
|
|
9
53
|
const env = {
|
|
@@ -20,7 +64,8 @@ export const runProcess = (params) => {
|
|
|
20
64
|
});
|
|
21
65
|
delete env.NO_COLOR;
|
|
22
66
|
}
|
|
23
|
-
|
|
67
|
+
const processInvocation = resolveShellInvocation(command, commandArgs, shell);
|
|
68
|
+
return spawn(processInvocation.command, processInvocation.commandArgs, {
|
|
24
69
|
env,
|
|
25
70
|
cwd,
|
|
26
71
|
stdio: logs,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "allure",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.12.0",
|
|
4
4
|
"description": "Allure Commandline Tool",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"allure",
|
|
@@ -32,26 +32,26 @@
|
|
|
32
32
|
"lint:fix": "oxlint --import-plugin --fix src test features stories"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@allurereport/charts-api": "3.
|
|
36
|
-
"@allurereport/ci": "3.
|
|
37
|
-
"@allurereport/core": "3.
|
|
38
|
-
"@allurereport/core-api": "3.
|
|
39
|
-
"@allurereport/directory-watcher": "3.
|
|
40
|
-
"@allurereport/plugin-agent": "3.
|
|
41
|
-
"@allurereport/plugin-allure2": "3.
|
|
42
|
-
"@allurereport/plugin-api": "3.
|
|
43
|
-
"@allurereport/plugin-awesome": "3.
|
|
44
|
-
"@allurereport/plugin-classic": "3.
|
|
45
|
-
"@allurereport/plugin-csv": "3.
|
|
46
|
-
"@allurereport/plugin-dashboard": "3.
|
|
47
|
-
"@allurereport/plugin-jira": "3.
|
|
48
|
-
"@allurereport/plugin-log": "3.
|
|
49
|
-
"@allurereport/plugin-progress": "3.
|
|
50
|
-
"@allurereport/plugin-server-reload": "3.
|
|
51
|
-
"@allurereport/plugin-slack": "3.
|
|
52
|
-
"@allurereport/reader-api": "3.
|
|
53
|
-
"@allurereport/service": "3.
|
|
54
|
-
"@allurereport/static-server": "3.
|
|
35
|
+
"@allurereport/charts-api": "3.12.0",
|
|
36
|
+
"@allurereport/ci": "3.12.0",
|
|
37
|
+
"@allurereport/core": "3.12.0",
|
|
38
|
+
"@allurereport/core-api": "3.12.0",
|
|
39
|
+
"@allurereport/directory-watcher": "3.12.0",
|
|
40
|
+
"@allurereport/plugin-agent": "3.12.0",
|
|
41
|
+
"@allurereport/plugin-allure2": "3.12.0",
|
|
42
|
+
"@allurereport/plugin-api": "3.12.0",
|
|
43
|
+
"@allurereport/plugin-awesome": "3.12.0",
|
|
44
|
+
"@allurereport/plugin-classic": "3.12.0",
|
|
45
|
+
"@allurereport/plugin-csv": "3.12.0",
|
|
46
|
+
"@allurereport/plugin-dashboard": "3.12.0",
|
|
47
|
+
"@allurereport/plugin-jira": "3.12.0",
|
|
48
|
+
"@allurereport/plugin-log": "3.12.0",
|
|
49
|
+
"@allurereport/plugin-progress": "3.12.0",
|
|
50
|
+
"@allurereport/plugin-server-reload": "3.12.0",
|
|
51
|
+
"@allurereport/plugin-slack": "3.12.0",
|
|
52
|
+
"@allurereport/reader-api": "3.12.0",
|
|
53
|
+
"@allurereport/service": "3.12.0",
|
|
54
|
+
"@allurereport/static-server": "3.12.0",
|
|
55
55
|
"adm-zip": "^0.5.16",
|
|
56
56
|
"clipanion": "^4.0.0-rc.4",
|
|
57
57
|
"glob": "^13.0.6",
|