Skip to content

Commit 4f18bfd

Browse files
committed
feat(parse-ci-reports): add support for Sarif files
Signed-off-by: Emilien Escalle <emilien.escalle@escemi.com>
1 parent 3f88783 commit 4f18bfd

15 files changed

Lines changed: 513 additions & 59 deletions

actions/parse-ci-reports/README.md

Lines changed: 58 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,28 @@ It supports multiple common report standards out of the box.
5050

5151
- **ESLint JSON** - JavaScript/TypeScript linting
5252
- **CheckStyle XML** - Java and other language linting
53+
- **SARIF** - Static analysis results in the SARIF 2.1.0 format
5354
- **Prettier Check Logs** - Text output captured from `prettier --check`
5455
- **Astro Check Logs** - Diagnostics emitted by `astro check`
5556

57+
### Expected Auto-Detection Paths
58+
59+
When `report-paths` uses `auto:test`, `auto:coverage`, `auto:lint`, or `auto:all`, the action searches for these glob patterns:
60+
61+
| **Report Type** | **Auto-Detected Paths** |
62+
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
63+
| **JUnit XML** | `**/junit*.xml`, `**/test-results/**/*.xml`, `**/test-reports/**/*.xml`, `**/*test*.xml` |
64+
| **TAP** | `**/*.tap` |
65+
| **Cobertura XML** | `**/coverage/*-coverage.xml`, `**/coverage/*-cobertura.xml`, `**/coverage/coverage.xml`, `**/coverage/cobertura.xml` |
66+
| **LCOV** | `**/coverage/lcov.info`, `**/lcov.info`, `**/coverage/*-lcov.info`, `**/*-lcov.info` |
67+
| **ESLint JSON** | `**/eslint-report.json`, `**/eslint.json`, `**/*-eslint-report.json`, `**/*-eslint.json` |
68+
| **CheckStyle XML** | `**/checkstyle-result.xml`, `**/checkstyle.xml`, `**/*-checkstyle-result.xml`, `**/*-checkstyle.xml` |
69+
| **SARIF** | `**/*.sarif`, `**/*.sarif.json`, `**/sarif-report.json`, `**/*-sarif-report.json` |
70+
| **Prettier Check Logs** | `**/prettier-check.log`, `**/prettier-check.txt`, `**/prettier-report.log`, `**/prettier-report.txt`, `**/*-prettier-check.log`, `**/*-prettier-check.txt`, `**/*-prettier-report.log`, `**/*-prettier-report.txt` |
71+
| **Astro Check Logs** | `**/astro-check.log`, `**/astro-check.txt`, `**/astro-check-report.log`, `**/astro-check-report.txt`, `**/*-astro-check.log`, `**/*-astro-check.txt`, `**/*-astro-check-report.log`, `**/*-astro-check-report.txt` |
72+
73+
If your reports are written elsewhere, pass explicit paths or glob patterns instead of relying on `auto:*` detection.
74+
5675
<!-- usage:start -->
5776

5877
## Usage
@@ -62,7 +81,7 @@ It supports multiple common report standards out of the box.
6281
with:
6382
# Paths to report files (glob patterns supported, one per line or comma-separated).
6483
# Set to `auto:test`, `auto:coverage`, `auto:lint`, or `auto:all` for automatic detection.
65-
# Examples: `**/junit.xml`, `coverage/lcov.info`, `eslint-report.json`, `auto:all`, `auto:test,coverage/lcov.info`, `auto:test,auto:coverage`
84+
# Examples: `**/junit.xml`, `coverage/lcov.info`, `eslint-report.json`, `reports/results.sarif`, `auto:all`, `auto:test,coverage/lcov.info`, `auto:test,auto:coverage`
6685
#
6786
# Default: `auto:all`
6887
report-paths: auto:all
@@ -110,26 +129,26 @@ It supports multiple common report standards out of the box.
110129
111130
## Inputs
112131
113-
| **Input** | **Description** | **Required** | **Default** |
114-
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | ---------------- |
115-
| **`report-paths`** | Paths to report files (glob patterns supported, one per line or comma-separated). | **false** | `auto:all` |
116-
| | Set to `auto:test`, `auto:coverage`, `auto:lint`, or `auto:all` for automatic detection. | | |
117-
| | Examples: `**/junit.xml`, `coverage/lcov.info`, `eslint-report.json`, `auto:all`, `auto:test,coverage/lcov.info`, `auto:test,auto:coverage` | | |
118-
| **`report-name`** | Name to display in the summary (e.g., `Test Results`, `Coverage Report`). | **false** | `Report Summary` |
119-
| **`include-passed`** | Whether to include passed tests in the summary. | **false** | `false` |
120-
| **`output-format`** | Output format: comma-separated list of `summary`, `markdown`, `annotations`, or `all` for everything. | **false** | `all` |
121-
| **`fail-on-error`** | Whether to fail the action if any test failures are detected. | **false** | `false` |
122-
| **`path-mapping`** | Path mapping(s) to rewrite file paths in reports (format: "from_path:to_path"). | **false** | - |
123-
| | Useful when tests/lints run in a different directory or container. | | |
124-
| | Multiple mappings can be provided separated by newlines or commas. | | |
125-
| | Examples: | | |
126-
| | - Single mapping: "/app/src:./src" | | |
127-
| | - Multiple mappings: "/app/src:./src,/app/tests:./tests" | | |
128-
| | - Multi-line: \| | | |
129-
| | /app/src:./src | | |
130-
| | /app/tests:./tests | | |
131-
| **`working-directory`** | Working directory where the action should operate. | **false** | `.` |
132-
| | Can be absolute or relative to the repository root. | | |
132+
| **Input** | **Description** | **Required** | **Default** |
133+
| ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | ---------------- |
134+
| **`report-paths`** | Paths to report files (glob patterns supported, one per line or comma-separated). | **false** | `auto:all` |
135+
| | Set to `auto:test`, `auto:coverage`, `auto:lint`, or `auto:all` for automatic detection. | | |
136+
| | Examples: `**/junit.xml`, `coverage/lcov.info`, `eslint-report.json`, `reports/results.sarif`, `auto:all`, `auto:test,coverage/lcov.info`, `auto:test,auto:coverage` | | |
137+
| **`report-name`** | Name to display in the summary (e.g., `Test Results`, `Coverage Report`). | **false** | `Report Summary` |
138+
| **`include-passed`** | Whether to include passed tests in the summary. | **false** | `false` |
139+
| **`output-format`** | Output format: comma-separated list of `summary`, `markdown`, `annotations`, or `all` for everything. | **false** | `all` |
140+
| **`fail-on-error`** | Whether to fail the action if any test failures are detected. | **false** | `false` |
141+
| **`path-mapping`** | Path mapping(s) to rewrite file paths in reports (format: "from_path:to_path"). | **false** | - |
142+
| | Useful when tests/lints run in a different directory or container. | | |
143+
| | Multiple mappings can be provided separated by newlines or commas. | | |
144+
| | Examples: | | |
145+
| | - Single mapping: "/app/src:./src" | | |
146+
| | - Multiple mappings: "/app/src:./src,/app/tests:./tests" | | |
147+
| | - Multi-line: \| | | |
148+
| | /app/src:./src | | |
149+
| | /app/tests:./tests | | |
150+
| **`working-directory`** | Working directory where the action should operate. | **false** | `.` |
151+
| | Can be absolute or relative to the repository root. | | |
133152

134153
<!-- inputs:end -->
135154
<!-- secrets:start -->
@@ -178,7 +197,7 @@ Auto-detection modes:
178197

179198
- `auto:coverage` - Finds LCOV and Cobertura coverage files
180199

181-
- `auto:lint` - Finds ESLint JSON and CheckStyle XML files
200+
- `auto:lint` - Finds ESLint JSON, CheckStyle XML, SARIF files, Prettier check logs, and Astro check logs
182201

183202
- `auto:all` - Finds all supported report types
184203

@@ -253,6 +272,22 @@ linting tools:
253272
output-format: "summary,annotations"
254273
```
255274

275+
### SARIF Static Analysis
276+
277+
Parse SARIF output from tools such as CodeQL or other static analyzers:
278+
279+
```yaml
280+
- name: Run static analysis
281+
run: codeql database analyze db javascript-security-extended --format=sarif-latest --output=reports/results.sarif
282+
283+
- name: Parse SARIF report
284+
uses: hoverkraft-tech/ci-github-common/actions/parse-ci-reports@66578f5b9aec4ac5558b5dad750c4c74dfcb65c5 # 0.35.5
285+
with:
286+
report-paths: "reports/results.sarif"
287+
report-name: "Static Analysis"
288+
output-format: "summary,annotations"
289+
```
290+
256291
### Fail on Test Failures
257292

258293
```yaml
@@ -464,6 +499,7 @@ src/
464499
│ ├── LCOVParser.js
465500
│ ├── ESLintParser.js
466501
│ ├── CheckStyleParser.js
502+
│ ├── SarifParser.js
467503
│ └── ParserFactory.js # Factory pattern for parser selection
468504
├── formatters/ # Output formatters
469505
│ ├── SummaryFormatter.js

actions/parse-ci-reports/action.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ inputs:
1818
description: |
1919
Paths to report files (glob patterns supported, one per line or comma-separated).
2020
Set to `auto:test`, `auto:coverage`, `auto:lint`, or `auto:all` for automatic detection.
21-
Examples: `**/junit.xml`, `coverage/lcov.info`, `eslint-report.json`, `auto:all`, `auto:test,coverage/lcov.info`, `auto:test,auto:coverage`
21+
Examples: `**/junit.xml`, `coverage/lcov.info`, `eslint-report.json`, `reports/results.sarif`, `auto:all`, `auto:test,coverage/lcov.info`, `auto:test,auto:coverage`
2222
required: false
2323
default: "auto:all"
2424
report-name:

actions/parse-ci-reports/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
"lcov",
1919
"cobertura",
2020
"eslint",
21-
"checkstyle"
21+
"checkstyle",
22+
"sarif"
2223
],
2324
"author": "hoverkraft",
2425
"license": "MIT",

actions/parse-ci-reports/src/ReportPathResolver.test.js

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,12 @@ describe("ReportPathResolver", () => {
9999
const coveragePatterns = [
100100
"**/coverage/lcov.info",
101101
"**/lcov.info",
102-
"**/coverage/cobertura-coverage.xml",
103-
"**/coverage.xml",
104-
"**/cobertura.xml",
102+
"**/coverage/*-lcov.info",
103+
"**/*-lcov.info",
104+
"**/coverage/coverage.xml",
105+
"**/coverage/*-coverage.xml",
106+
"**/coverage/cobertura.xml",
107+
"**/coverage/*-cobertura.xml",
105108
];
106109
for (const pattern of coveragePatterns) {
107110
assert.ok(
@@ -119,8 +122,8 @@ describe("ReportPathResolver", () => {
119122
);
120123
}
121124

122-
// Verify total count matches expected (5 test + 5 coverage)
123-
assert.strictEqual(patterns.length, 10);
125+
// Verify total count matches expected (5 test + 8 coverage)
126+
assert.strictEqual(patterns.length, 13);
124127
});
125128

126129
it("deduplicates overlapping auto modes", () => {
@@ -133,16 +136,32 @@ describe("ReportPathResolver", () => {
133136
const lintPatterns = [
134137
"**/eslint-report.json",
135138
"**/eslint.json",
139+
"**/*-eslint-report.json",
140+
"**/*-eslint.json",
136141
"**/checkstyle-result.xml",
137142
"**/checkstyle.xml",
143+
"**/*-checkstyle-result.xml",
144+
"**/*-checkstyle.xml",
145+
"**/*.sarif",
146+
"**/*.sarif.json",
147+
"**/sarif-report.json",
148+
"**/*-sarif-report.json",
138149
"**/prettier-check.log",
139150
"**/prettier-check.txt",
140151
"**/prettier-report.log",
141152
"**/prettier-report.txt",
153+
"**/*-prettier-check.log",
154+
"**/*-prettier-check.txt",
155+
"**/*-prettier-report.log",
156+
"**/*-prettier-report.txt",
142157
"**/astro-check.log",
143158
"**/astro-check.txt",
144159
"**/astro-check-report.log",
145160
"**/astro-check-report.txt",
161+
"**/*-astro-check.log",
162+
"**/*-astro-check.txt",
163+
"**/*-astro-check-report.log",
164+
"**/*-astro-check-report.txt",
146165
];
147166
for (const pattern of lintPatterns) {
148167
assert.ok(patterns.includes(pattern), `Missing lint pattern: ${pattern}`);
@@ -164,9 +183,12 @@ describe("ReportPathResolver", () => {
164183
const coveragePatterns = [
165184
"**/coverage/lcov.info",
166185
"**/lcov.info",
167-
"**/coverage/cobertura-coverage.xml",
168-
"**/coverage.xml",
169-
"**/cobertura.xml",
186+
"**/coverage/*-lcov.info",
187+
"**/*-lcov.info",
188+
"**/coverage/coverage.xml",
189+
"**/coverage/*-coverage.xml",
190+
"**/coverage/cobertura.xml",
191+
"**/coverage/*-cobertura.xml",
170192
];
171193
for (const pattern of coveragePatterns) {
172194
assert.ok(
@@ -175,8 +197,8 @@ describe("ReportPathResolver", () => {
175197
);
176198
}
177199

178-
// Verify deduplication - total should be 12 lint + 5 test + 5 coverage = 22
179-
assert.strictEqual(patterns.length, 22);
200+
// Verify deduplication - total should be 28 lint + 5 test + 8 coverage = 41
201+
assert.strictEqual(patterns.length, 41);
180202
});
181203

182204
it("gets patterns from parsers via getAutoPatterns", () => {
@@ -196,13 +218,22 @@ describe("ReportPathResolver", () => {
196218

197219
// Verify coverage patterns come from LCOVParser and CoberturaParser
198220
assert.ok(autoPatterns.coverage.includes("**/coverage/lcov.info"));
199-
assert.ok(autoPatterns.coverage.includes("**/cobertura.xml"));
221+
assert.ok(autoPatterns.coverage.includes("**/coverage/cobertura.xml"));
222+
assert.ok(autoPatterns.coverage.includes("**/*-lcov.info"));
223+
assert.ok(autoPatterns.coverage.includes("**/coverage/*-coverage.xml"));
224+
assert.ok(autoPatterns.coverage.includes("**/coverage/*-cobertura.xml"));
200225

201-
// Verify lint patterns come from ESLintParser, CheckStyleParser, PrettierParser, AstroCheckParser
226+
// Verify lint patterns come from ESLintParser, CheckStyleParser, SarifParser, PrettierParser, AstroCheckParser
202227
assert.ok(autoPatterns.lint.includes("**/eslint-report.json"));
203228
assert.ok(autoPatterns.lint.includes("**/checkstyle.xml"));
229+
assert.ok(autoPatterns.lint.includes("**/*.sarif"));
204230
assert.ok(autoPatterns.lint.includes("**/prettier-check.log"));
205231
assert.ok(autoPatterns.lint.includes("**/astro-check.log"));
232+
assert.ok(autoPatterns.lint.includes("**/*-eslint.json"));
233+
assert.ok(autoPatterns.lint.includes("**/*-checkstyle.xml"));
234+
assert.ok(autoPatterns.lint.includes("**/*-sarif-report.json"));
235+
assert.ok(autoPatterns.lint.includes("**/*-prettier-check.log"));
236+
assert.ok(autoPatterns.lint.includes("**/*-astro-check.log"));
206237
});
207238

208239
it("excludes node_modules files from glob patterns", async () => {

actions/parse-ci-reports/src/parsers/AstroCheckParser.js

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,15 @@ export class AstroCheckParser extends BaseParser {
3434
}
3535

3636
getAutoPatterns() {
37-
return [
38-
"**/astro-check.log",
39-
"**/astro-check.txt",
40-
"**/astro-check-report.log",
41-
"**/astro-check-report.txt",
42-
];
37+
return this.buildBasenamePatterns(
38+
[
39+
"astro-check.log",
40+
"astro-check.txt",
41+
"astro-check-report.log",
42+
"astro-check-report.txt",
43+
],
44+
{ includePrefixed: true },
45+
);
4346
}
4447

4548
parse(content) {

0 commit comments

Comments
 (0)