@flakiness/junit-xml 1.0.0-alpha.1 → 1.1.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 +18 -17
- package/lib/cli.js +2 -1
- package/lib/parser.js +7 -2
- package/package.json +3 -3
- package/types/src/parser.d.ts.map +1 -1
package/README.md
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
|
+
[](https://flakiness.io/flakiness/junit-xml)
|
|
2
|
+
|
|
1
3
|
# @flakiness/junit-xml
|
|
2
4
|
|
|
3
|
-
Convert JUnit XML test reports into a Flakiness report and upload it to [flakiness.io](https://flakiness.io).
|
|
5
|
+
Convert JUnit XML test reports into a Flakiness report and upload it to [flakiness.io](https://flakiness.io).
|
|
4
6
|
|
|
5
7
|
The recommended way to run it is with `npx` (no install step):
|
|
6
8
|
|
|
7
9
|
```bash
|
|
8
|
-
npx @flakiness/junit-xml
|
|
10
|
+
npx @flakiness/junit-xml --flakiness-project myorg/myproject ./build/reports/junit
|
|
9
11
|
```
|
|
10
12
|
|
|
11
|
-
This combines every XML file under the given path into a single Flakiness report and uploads it to flakiness.io.
|
|
13
|
+
This combines every XML file under the given path into a single Flakiness report and auto-uploads it to flakiness.io. See [authentication](#authentication) on how to configure auto-upload.
|
|
12
14
|
|
|
13
15
|
If your environment has no Node.js, a [standalone binary](#standalone-binary-no-nodejs) is also available.
|
|
14
16
|
|
|
@@ -18,13 +20,13 @@ If your environment has no Node.js, a [standalone binary](#standalone-binary-no-
|
|
|
18
20
|
- [Example: ingesting `bun test` results](#example-ingesting-bun-test-results)
|
|
19
21
|
- [Example: ingesting Rust `cargo-nextest` results](#example-ingesting-rust-cargo-nextest-results)
|
|
20
22
|
- [Standalone binary (no Node.js)](#standalone-binary-no-nodejs)
|
|
21
|
-
- [
|
|
23
|
+
- [Authentication](#authentication)
|
|
22
24
|
- [License](#license)
|
|
23
25
|
|
|
24
26
|
## Usage
|
|
25
27
|
|
|
26
28
|
```
|
|
27
|
-
flakiness-junit-xml <junit-path>
|
|
29
|
+
flakiness-junit-xml [options] <junit-path>
|
|
28
30
|
|
|
29
31
|
<junit-path> JUnit XML file, or a directory of XML files (scanned recursively)
|
|
30
32
|
--env-name <name> Environment name (defaults to --category, or `junit`)
|
|
@@ -48,7 +50,7 @@ Requires Node.js `^20.17.0 || >=22.9.0`.
|
|
|
48
50
|
|
|
49
51
|
```bash
|
|
50
52
|
bun test --reporter=junit --reporter-outfile=./junit.xml
|
|
51
|
-
npx @flakiness/junit-xml
|
|
53
|
+
npx @flakiness/junit-xml --category bun --flakiness-project myorg/myproject ./junit.xml
|
|
52
54
|
```
|
|
53
55
|
|
|
54
56
|
## Example: ingesting Rust `cargo-nextest` results
|
|
@@ -64,12 +66,13 @@ Then run the tests and point at the XML nextest writes under `target/nextest/`:
|
|
|
64
66
|
|
|
65
67
|
```bash
|
|
66
68
|
cargo nextest run --profile ci
|
|
67
|
-
npx @flakiness/junit-xml
|
|
69
|
+
npx @flakiness/junit-xml --category rust --flakiness-project myorg/myproject ./target/nextest/ci/junit.xml
|
|
68
70
|
```
|
|
69
71
|
|
|
70
72
|
## Standalone binary (no Node.js)
|
|
71
73
|
|
|
72
|
-
|
|
74
|
+
This tool is also shipped as a single self-contained executable that bundles
|
|
75
|
+
its own runtime, so it works on machines without Node.js.
|
|
73
76
|
|
|
74
77
|
**macOS / Linux:**
|
|
75
78
|
|
|
@@ -86,18 +89,16 @@ irm https://github.com/flakiness/junit-xml/releases/latest/download/install.ps1
|
|
|
86
89
|
This installs a `flakiness-junit-xml` command on your `PATH`. Then use it exactly as above:
|
|
87
90
|
|
|
88
91
|
```bash
|
|
89
|
-
flakiness-junit-xml
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
The installer detects your OS/architecture (x64 and arm64; Linux glibc and Alpine/musl) and always pulls the latest release. To pin a directory, set `INSTALL_DIR` (default `/usr/local/bin`):
|
|
93
|
-
|
|
94
|
-
```bash
|
|
95
|
-
curl -fsSL https://github.com/flakiness/junit-xml/releases/latest/download/install.sh | INSTALL_DIR="$HOME/.local/bin" sh
|
|
92
|
+
flakiness-junit-xml --flakiness-project myorg/myproject ./build/reports/junit
|
|
96
93
|
```
|
|
97
94
|
|
|
98
|
-
|
|
95
|
+
> [!NOTE]
|
|
96
|
+
> You can set `INSTALL_DIR` to configure custom location for installation.
|
|
97
|
+
> ```bash
|
|
98
|
+
> curl -fsSL https://github.com/flakiness/junit-xml/releases/latest/download/install.sh | INSTALL_DIR="$HOME/.local/bin" sh
|
|
99
|
+
> ```
|
|
99
100
|
|
|
100
|
-
##
|
|
101
|
+
## Authentication
|
|
101
102
|
|
|
102
103
|
The report is uploaded to flakiness.io automatically. Authentication, in priority order:
|
|
103
104
|
|
package/lib/cli.js
CHANGED
|
@@ -3,6 +3,7 @@ import { CIUtils, GitWorktree, ReportUtils, uploadReport, writeReport } from "@f
|
|
|
3
3
|
import { Command, Option } from "commander";
|
|
4
4
|
import fs from "node:fs/promises";
|
|
5
5
|
import path from "node:path";
|
|
6
|
+
import pkg from "../package.json" with { type: "json" };
|
|
6
7
|
import { parseJUnit } from "./parser.js";
|
|
7
8
|
const STDERR_LOGGER = {
|
|
8
9
|
log: (...args) => console.error(...args),
|
|
@@ -12,7 +13,7 @@ const STDERR_LOGGER = {
|
|
|
12
13
|
function envBool(name) {
|
|
13
14
|
return ["1", "true"].includes(process.env[name]?.toLowerCase() ?? "");
|
|
14
15
|
}
|
|
15
|
-
const program = new Command("flakiness-junit-xml").description("Convert JUnit XML report(s) to a Flakiness report and upload it to flakiness.io").argument("<junit-path>", "Path to a JUnit XML file or a directory containing XML files").option("-c, --category <category>", "Report category identifier (e.g. `bun`, `rust`). Defaults to `junit`.").option("--env-name <name>", "Environment name for the report (defaults to --category, or `junit` if neither is set)").option("--commit-id <id>", "Git commit ID (auto-detected from the current working directory if not provided)").addOption(new Option("--title <title>", "Human-readable report title").env("FLAKINESS_TITLE")).option("--output-dir <dir>", "Output directory for the report", "flakiness-report").addOption(new Option("--flakiness-project <project>", "Flakiness project identifier in `org/project` format").env("FLAKINESS_PROJECT")).addOption(new Option("-p, --project <org/project>").hideHelp()).addOption(new Option("--token <token>", "Flakiness.io access token for upload").env("FLAKINESS_ACCESS_TOKEN")).option("--endpoint <url>", "Flakiness.io API endpoint override").addOption(new Option("--disable-upload", "Convert only; do not upload to flakiness.io").env("FLAKINESS_DISABLE_UPLOAD")).action(async (junitPath, options) => {
|
|
16
|
+
const program = new Command("flakiness-junit-xml").description("Convert JUnit XML report(s) to a Flakiness report and upload it to flakiness.io").version(pkg.version, "-v, --version", "Output the version number").argument("<junit-path>", "Path to a JUnit XML file or a directory containing XML files").option("-c, --category <category>", "Report category identifier (e.g. `bun`, `rust`). Defaults to `junit`.").option("--env-name <name>", "Environment name for the report (defaults to --category, or `junit` if neither is set)").option("--commit-id <id>", "Git commit ID (auto-detected from the current working directory if not provided)").addOption(new Option("--title <title>", "Human-readable report title").env("FLAKINESS_TITLE")).option("--output-dir <dir>", "Output directory for the report", "flakiness-report").addOption(new Option("--flakiness-project <project>", "Flakiness project identifier in `org/project` format").env("FLAKINESS_PROJECT")).addOption(new Option("-p, --project <org/project>").hideHelp()).addOption(new Option("--token <token>", "Flakiness.io access token for upload").env("FLAKINESS_ACCESS_TOKEN")).option("--endpoint <url>", "Flakiness.io API endpoint override").addOption(new Option("--disable-upload", "Convert only; do not upload to flakiness.io").env("FLAKINESS_DISABLE_UPLOAD")).action(async (junitPath, options) => {
|
|
16
17
|
await runConvert(junitPath, {
|
|
17
18
|
envName: options.envName ?? options.category ?? "junit",
|
|
18
19
|
outputDir: options.outputDir,
|
package/lib/parser.js
CHANGED
|
@@ -6,6 +6,10 @@ import fs from "fs";
|
|
|
6
6
|
import mime from "mime";
|
|
7
7
|
import path from "path";
|
|
8
8
|
import { Temporal } from "temporal-polyfill";
|
|
9
|
+
import pkg from "../package.json" with { type: "json" };
|
|
10
|
+
function toGitFilePath(file) {
|
|
11
|
+
return file.split("\\").join("/");
|
|
12
|
+
}
|
|
9
13
|
let gTZAbbreviationToIANATimezone;
|
|
10
14
|
function tzAbbreviationToIANA(tz) {
|
|
11
15
|
if (!gTZAbbreviationToIANATimezone) {
|
|
@@ -106,7 +110,7 @@ async function traverseJUnitReport(context, node) {
|
|
|
106
110
|
const newSuite = {
|
|
107
111
|
title: name ?? file,
|
|
108
112
|
location: file && !isNaN(line) ? {
|
|
109
|
-
file,
|
|
113
|
+
file: toGitFilePath(file),
|
|
110
114
|
line,
|
|
111
115
|
column: 1
|
|
112
116
|
} : void 0,
|
|
@@ -158,7 +162,7 @@ async function traverseJUnitReport(context, node) {
|
|
|
158
162
|
const test = {
|
|
159
163
|
title: name,
|
|
160
164
|
location: file && !isNaN(line) ? {
|
|
161
|
-
file,
|
|
165
|
+
file: toGitFilePath(file),
|
|
162
166
|
line,
|
|
163
167
|
column: 1
|
|
164
168
|
} : void 0,
|
|
@@ -206,6 +210,7 @@ async function parseJUnit(xmls, options) {
|
|
|
206
210
|
const report = {
|
|
207
211
|
category: options.category ?? "junit",
|
|
208
212
|
commitId: options.commitId,
|
|
213
|
+
generatedBy: { name: pkg.name, version: pkg.version },
|
|
209
214
|
duration: options.runDuration,
|
|
210
215
|
startTimestamp: options.runStartTimestamp,
|
|
211
216
|
url: options.runUrl,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flakiness/junit-xml",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"temporal-polyfill": "^0.3.2"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
|
-
"@
|
|
37
|
+
"@types/bun": "^1.3.14",
|
|
38
38
|
"@types/node": "^25.0.3",
|
|
39
39
|
"esbuild": "^0.27.2",
|
|
40
40
|
"kubik": "^0.24.0",
|
|
@@ -44,6 +44,6 @@
|
|
|
44
44
|
"scripts": {
|
|
45
45
|
"build": "kubik build.mts",
|
|
46
46
|
"watch": "kubik build.mts -w",
|
|
47
|
-
"test": "
|
|
47
|
+
"test": "bun test tests/ --reporter=junit --reporter-outfile=junit.xml"
|
|
48
48
|
}
|
|
49
49
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/parser.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,eAAe,IAAI,EAAE,EAAE,MAAM,6BAA6B,CAAC;AACpE,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/parser.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,eAAe,IAAI,EAAE,EAAE,MAAM,6BAA6B,CAAC;AACpE,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAoQ7C,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE;IACxD,UAAU,EAAE,EAAE,CAAC,WAAW,CAAC;IAC3B,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC;IACtB,WAAW,EAAE,EAAE,CAAC,UAAU,CAAC;IAC3B,iBAAiB,EAAE,EAAE,CAAC,eAAe,CAAC;IACtC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC;IAAE,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC;IAAC,WAAW,EAAE,WAAW,CAAC,UAAU,EAAE,CAAA;CAAE,CAAC,CAgCxE"}
|