allure 3.10.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 +36 -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 +49 -0
- package/dist/commands/agent-run.js +335 -0
- package/dist/commands/agent.d.ts +70 -15
- package/dist/commands/agent.js +564 -225
- package/dist/commands/run.js +0 -18
- package/dist/index.js +17 -3
- package/dist/utils/index.d.ts +0 -2
- package/dist/utils/index.js +0 -2
- package/dist/utils/process.js +46 -1
- package/package.json +21 -21
- package/dist/utils/agent-select.d.ts +0 -41
- package/dist/utils/agent-select.js +0 -141
- package/dist/utils/agent-state.d.ts +0 -15
- package/dist/utils/agent-state.js +0 -83
package/README.md
CHANGED
|
@@ -74,7 +74,41 @@ 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.
|
|
98
|
+
|
|
99
|
+
Agents and setup tools can inspect the local structured capability contract without scraping help text:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
npx allure agent capabilities --json
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
After a run, agents can query the output directory without manually reading every manifest:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
npx allure agent query --latest summary
|
|
109
|
+
npx allure agent query --latest tests --status failed
|
|
110
|
+
npx allure agent query --from ./agent-output findings --severity high
|
|
111
|
+
```
|
|
78
112
|
|
|
79
113
|
### Generating Reports Manually
|
|
80
114
|
|
|
@@ -118,6 +152,7 @@ The Allure CLI includes several helpful global options. Use `--help` to explore
|
|
|
118
152
|
|
|
119
153
|
```bash
|
|
120
154
|
npx allure run --help
|
|
155
|
+
npx allure agent capabilities --json
|
|
121
156
|
npx allure agent --help
|
|
122
157
|
npx allure watch --help
|
|
123
158
|
```
|
|
@@ -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
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
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;
|
|
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>;
|
|
14
|
+
export type ExecuteAgentModeParams = {
|
|
15
|
+
configPath?: string;
|
|
16
|
+
cwd?: string;
|
|
17
|
+
output?: string;
|
|
18
|
+
expectations?: string;
|
|
19
|
+
inlineExpectations?: AgentExpectationsInput;
|
|
20
|
+
environment?: string;
|
|
21
|
+
environmentName?: string;
|
|
22
|
+
silent?: boolean;
|
|
23
|
+
rerunFrom?: string;
|
|
24
|
+
rerunLatest?: boolean;
|
|
25
|
+
rerunPreset?: string;
|
|
26
|
+
rerunEnvironments?: string[];
|
|
27
|
+
rerunLabels?: string[];
|
|
28
|
+
reportMode: AgentHumanReportMode;
|
|
29
|
+
args: string[];
|
|
30
|
+
};
|
|
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>;
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
import * as console from "node:console";
|
|
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";
|
|
6
|
+
import process, { exit } from "node:process";
|
|
7
|
+
import { AllureReport, isFileNotFoundError, readConfig } from "@allurereport/core";
|
|
8
|
+
import { createAgentTestPlanContext, AgentUsageError, formatAgentOutputLinks, isPathInside, 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 { findAllureResultDirectories, findFilesByGlobs } from "../utils/fileSystem.js";
|
|
12
|
+
import { createAgentHumanReportConfig } from "./agent-human-report.js";
|
|
13
|
+
import { executeAllureRun, executeNestedAllureCommand } from "./commons/run.js";
|
|
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(" ");
|
|
22
|
+
export const printAgentOutputLinks = (outputDir) => {
|
|
23
|
+
for (const line of formatAgentOutputLinks(outputDir)) {
|
|
24
|
+
console.log(line);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
export const persistAgentRunState = async (value) => {
|
|
28
|
+
try {
|
|
29
|
+
await writeAgentRunState(value);
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
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}`);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
export const executeAgentMode = async (params) => {
|
|
70
|
+
const { configPath, cwd: configuredCwd, output, expectations, inlineExpectations, environment, environmentName, silent, rerunFrom, rerunLatest, rerunPreset, rerunEnvironments, rerunLabels, reportMode, args, } = params;
|
|
71
|
+
const command = args[0];
|
|
72
|
+
const commandArgs = args.slice(1);
|
|
73
|
+
const cwd = await realpath(configuredCwd ?? process.cwd());
|
|
74
|
+
const commandString = formatAgentCommand(args);
|
|
75
|
+
const hasRerunSource = !!rerunFrom || !!rerunLatest;
|
|
76
|
+
const hasRerunFilters = !!rerunPreset || !!rerunEnvironments?.length || !!rerunLabels?.length;
|
|
77
|
+
if (!hasRerunSource && hasRerunFilters) {
|
|
78
|
+
throw new AgentUsageError("Use rerun filters only together with --rerun-from <path> or --rerun-latest");
|
|
79
|
+
}
|
|
80
|
+
const rerunContext = await createAgentTestPlanContext({
|
|
81
|
+
cwd,
|
|
82
|
+
from: rerunFrom,
|
|
83
|
+
latest: rerunLatest,
|
|
84
|
+
preset: normalizeAgentRerunPreset(rerunPreset),
|
|
85
|
+
environments: rerunEnvironments?.length ? rerunEnvironments : undefined,
|
|
86
|
+
labelFilters: parseAgentLabelFilters(rerunLabels),
|
|
87
|
+
});
|
|
88
|
+
const childEnvironmentVariables = {
|
|
89
|
+
...createChildAllureCliEnvironment("agent"),
|
|
90
|
+
...(rerunContext ? { ALLURE_TESTPLAN_PATH: rerunContext.testPlanPath } : {}),
|
|
91
|
+
};
|
|
92
|
+
try {
|
|
93
|
+
if (getActiveAllureCliCommand()) {
|
|
94
|
+
console.log(commandString);
|
|
95
|
+
const exitCode = await executeNestedAllureCommand({
|
|
96
|
+
command,
|
|
97
|
+
commandArgs,
|
|
98
|
+
cwd,
|
|
99
|
+
...(rerunContext ? { environmentVariables: { ALLURE_TESTPLAN_PATH: rerunContext.testPlanPath } } : {}),
|
|
100
|
+
silent,
|
|
101
|
+
});
|
|
102
|
+
exit(exitCode ?? -1);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const runId = randomUUID();
|
|
106
|
+
const managedOutput = !output;
|
|
107
|
+
const outputDir = output ? resolve(cwd, output) : await mkdtemp(join(tmpdir(), "allure-agent-"));
|
|
108
|
+
const expectationsPath = expectations ? resolve(cwd, expectations) : undefined;
|
|
109
|
+
const environmentOptions = {
|
|
110
|
+
environment,
|
|
111
|
+
environmentName,
|
|
112
|
+
};
|
|
113
|
+
normalizeCommandEnvironmentOptions(environmentOptions);
|
|
114
|
+
if (expectationsPath && isPathInside(outputDir, expectationsPath)) {
|
|
115
|
+
throw new AgentUsageError(`--expectations path ${JSON.stringify(expectationsPath)} must not be inside the agent output directory ${JSON.stringify(outputDir)}`);
|
|
116
|
+
}
|
|
117
|
+
const humanReport = await createAgentHumanReportConfig({
|
|
118
|
+
mode: reportMode,
|
|
119
|
+
cwd,
|
|
120
|
+
configPath,
|
|
121
|
+
outputDir,
|
|
122
|
+
});
|
|
123
|
+
const config = await readConfig(cwd, configPath, {
|
|
124
|
+
output: outputDir,
|
|
125
|
+
plugins: {
|
|
126
|
+
agent: {
|
|
127
|
+
options: {
|
|
128
|
+
outputDir,
|
|
129
|
+
command: commandString,
|
|
130
|
+
humanReport: humanReport.statusProvider,
|
|
131
|
+
...(expectationsPath ? { expectationsPath } : {}),
|
|
132
|
+
...(inlineExpectations ? { expectations: inlineExpectations } : {}),
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
const resolvedEnvironment = resolveCommandEnvironment(config, environmentOptions);
|
|
138
|
+
try {
|
|
139
|
+
await rm(outputDir, { recursive: true });
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
if (!isFileNotFoundError(error)) {
|
|
143
|
+
console.error("could not clean output directory", error);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
const startedAt = Date.now();
|
|
147
|
+
await persistAgentRunState({
|
|
148
|
+
runId,
|
|
149
|
+
cwd,
|
|
150
|
+
outputDir,
|
|
151
|
+
managedOutput,
|
|
152
|
+
expectationsPath,
|
|
153
|
+
command: commandString,
|
|
154
|
+
startedAt,
|
|
155
|
+
status: "running",
|
|
156
|
+
pid: process.pid,
|
|
157
|
+
});
|
|
158
|
+
printAgentOutputLinks(outputDir);
|
|
159
|
+
if (expectationsPath) {
|
|
160
|
+
console.log(`agent expectations: ${expectationsPath}`);
|
|
161
|
+
}
|
|
162
|
+
else if (inlineExpectations) {
|
|
163
|
+
console.log("agent expectations: CLI options");
|
|
164
|
+
}
|
|
165
|
+
console.log(commandString);
|
|
166
|
+
const allureReport = new AllureReport({
|
|
167
|
+
...config,
|
|
168
|
+
output: outputDir,
|
|
169
|
+
environment: resolvedEnvironment?.id,
|
|
170
|
+
open: false,
|
|
171
|
+
port: undefined,
|
|
172
|
+
qualityGate: undefined,
|
|
173
|
+
allureService: undefined,
|
|
174
|
+
realTime: false,
|
|
175
|
+
plugins: [...humanReport.plugins, ...(config.plugins ?? [])],
|
|
176
|
+
});
|
|
177
|
+
const knownIssues = await allureReport.store.allKnownIssues();
|
|
178
|
+
const { globalExitCode } = await executeAllureRun({
|
|
179
|
+
allureReport,
|
|
180
|
+
knownIssues,
|
|
181
|
+
cwd,
|
|
182
|
+
command,
|
|
183
|
+
commandArgs,
|
|
184
|
+
environmentVariables: childEnvironmentVariables,
|
|
185
|
+
environment: resolvedEnvironment?.id,
|
|
186
|
+
withQualityGate: false,
|
|
187
|
+
logs: "pipe",
|
|
188
|
+
silent,
|
|
189
|
+
ignoreLogs: false,
|
|
190
|
+
logProcessExit: false,
|
|
191
|
+
});
|
|
192
|
+
await persistAgentRunState({
|
|
193
|
+
runId,
|
|
194
|
+
cwd,
|
|
195
|
+
outputDir,
|
|
196
|
+
managedOutput,
|
|
197
|
+
expectationsPath,
|
|
198
|
+
command: commandString,
|
|
199
|
+
startedAt,
|
|
200
|
+
finishedAt: Date.now(),
|
|
201
|
+
status: "finished",
|
|
202
|
+
exitCode: globalExitCode.actual ?? globalExitCode.original,
|
|
203
|
+
pid: process.pid,
|
|
204
|
+
});
|
|
205
|
+
await cleanupManagedAgentOutputs({ cwd, runId, managedOutput });
|
|
206
|
+
exit(globalExitCode.actual ?? globalExitCode.original);
|
|
207
|
+
}
|
|
208
|
+
finally {
|
|
209
|
+
await rerunContext?.cleanup();
|
|
210
|
+
}
|
|
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
|
+
};
|