Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 58 additions & 22 deletions actions/parse-ci-reports/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,28 @@ It supports multiple common report standards out of the box.

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

### Expected Auto-Detection Paths

When `report-paths` uses `auto:test`, `auto:coverage`, `auto:lint`, or `auto:all`, the action searches for these glob patterns:

| **Report Type** | **Auto-Detected Paths** |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **JUnit XML** | `**/junit*.xml`, `**/test-results/**/*.xml`, `**/test-reports/**/*.xml`, `**/*test*.xml` |
| **TAP** | `**/*.tap` |
| **Cobertura XML** | `**/coverage/*-coverage.xml`, `**/coverage/*-cobertura.xml`, `**/coverage/coverage.xml`, `**/coverage/cobertura.xml` |
| **LCOV** | `**/coverage/lcov.info`, `**/lcov.info`, `**/coverage/*-lcov.info`, `**/*-lcov.info` |
| **ESLint JSON** | `**/eslint-report.json`, `**/eslint.json`, `**/*-eslint-report.json`, `**/*-eslint.json` |
| **CheckStyle XML** | `**/checkstyle-result.xml`, `**/checkstyle.xml`, `**/*-checkstyle-result.xml`, `**/*-checkstyle.xml` |
| **SARIF** | `**/*.sarif`, `**/*.sarif.json`, `**/sarif-report.json`, `**/*-sarif-report.json` |
| **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` |
| **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` |

If your reports are written elsewhere, pass explicit paths or glob patterns instead of relying on `auto:*` detection.

<!-- usage:start -->

## Usage
Expand All @@ -62,7 +81,7 @@ It supports multiple common report standards out of the box.
with:
# Paths to report files (glob patterns supported, one per line or comma-separated).
# Set to `auto:test`, `auto:coverage`, `auto:lint`, or `auto:all` for automatic detection.
# Examples: `**/junit.xml`, `coverage/lcov.info`, `eslint-report.json`, `auto:all`, `auto:test,coverage/lcov.info`, `auto:test,auto:coverage`
# Examples: `**/junit.xml`, `coverage/lcov.info`, `eslint-report.json`, `reports/results.sarif`, `auto:all`, `auto:test,coverage/lcov.info`, `auto:test,auto:coverage`
#
# Default: `auto:all`
report-paths: auto:all
Expand Down Expand Up @@ -110,26 +129,26 @@ It supports multiple common report standards out of the box.

## Inputs

| **Input** | **Description** | **Required** | **Default** |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | ---------------- |
| **`report-paths`** | Paths to report files (glob patterns supported, one per line or comma-separated). | **false** | `auto:all` |
| | Set to `auto:test`, `auto:coverage`, `auto:lint`, or `auto:all` for automatic detection. | | |
| | Examples: `**/junit.xml`, `coverage/lcov.info`, `eslint-report.json`, `auto:all`, `auto:test,coverage/lcov.info`, `auto:test,auto:coverage` | | |
| **`report-name`** | Name to display in the summary (e.g., `Test Results`, `Coverage Report`). | **false** | `Report Summary` |
| **`include-passed`** | Whether to include passed tests in the summary. | **false** | `false` |
| **`output-format`** | Output format: comma-separated list of `summary`, `markdown`, `annotations`, or `all` for everything. | **false** | `all` |
| **`fail-on-error`** | Whether to fail the action if any test failures are detected. | **false** | `false` |
| **`path-mapping`** | Path mapping(s) to rewrite file paths in reports (format: "from_path:to_path"). | **false** | - |
| | Useful when tests/lints run in a different directory or container. | | |
| | Multiple mappings can be provided separated by newlines or commas. | | |
| | Examples: | | |
| | - Single mapping: "/app/src:./src" | | |
| | - Multiple mappings: "/app/src:./src,/app/tests:./tests" | | |
| | - Multi-line: \| | | |
| | /app/src:./src | | |
| | /app/tests:./tests | | |
| **`working-directory`** | Working directory where the action should operate. | **false** | `.` |
| | Can be absolute or relative to the repository root. | | |
| **Input** | **Description** | **Required** | **Default** |
| ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | ---------------- |
| **`report-paths`** | Paths to report files (glob patterns supported, one per line or comma-separated). | **false** | `auto:all` |
| | Set to `auto:test`, `auto:coverage`, `auto:lint`, or `auto:all` for automatic detection. | | |
| | Examples: `**/junit.xml`, `coverage/lcov.info`, `eslint-report.json`, `reports/results.sarif`, `auto:all`, `auto:test,coverage/lcov.info`, `auto:test,auto:coverage` | | |
| **`report-name`** | Name to display in the summary (e.g., `Test Results`, `Coverage Report`). | **false** | `Report Summary` |
| **`include-passed`** | Whether to include passed tests in the summary. | **false** | `false` |
| **`output-format`** | Output format: comma-separated list of `summary`, `markdown`, `annotations`, or `all` for everything. | **false** | `all` |
| **`fail-on-error`** | Whether to fail the action if any test failures are detected. | **false** | `false` |
| **`path-mapping`** | Path mapping(s) to rewrite file paths in reports (format: "from_path:to_path"). | **false** | - |
| | Useful when tests/lints run in a different directory or container. | | |
| | Multiple mappings can be provided separated by newlines or commas. | | |
| | Examples: | | |
| | - Single mapping: "/app/src:./src" | | |
| | - Multiple mappings: "/app/src:./src,/app/tests:./tests" | | |
| | - Multi-line: \| | | |
| | /app/src:./src | | |
| | /app/tests:./tests | | |
| **`working-directory`** | Working directory where the action should operate. | **false** | `.` |
| | Can be absolute or relative to the repository root. | | |

<!-- inputs:end -->
<!-- secrets:start -->
Expand Down Expand Up @@ -178,7 +197,7 @@ Auto-detection modes:

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

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

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

Expand Down Expand Up @@ -253,6 +272,22 @@ linting tools:
output-format: "summary,annotations"
```

### SARIF Static Analysis

Parse SARIF output from tools such as CodeQL or other static analyzers:

```yaml
- name: Run static analysis
run: codeql database analyze db javascript-security-extended --format=sarif-latest --output=reports/results.sarif

- name: Parse SARIF report
uses: hoverkraft-tech/ci-github-common/actions/parse-ci-reports@66578f5b9aec4ac5558b5dad750c4c74dfcb65c5 # 0.35.5
with:
report-paths: "reports/results.sarif"
report-name: "Static Analysis"
output-format: "summary,annotations"
```

### Fail on Test Failures

```yaml
Expand Down Expand Up @@ -464,6 +499,7 @@ src/
│ ├── LCOVParser.js
│ ├── ESLintParser.js
│ ├── CheckStyleParser.js
│ ├── SarifParser.js
│ └── ParserFactory.js # Factory pattern for parser selection
├── formatters/ # Output formatters
│ ├── SummaryFormatter.js
Expand Down
2 changes: 1 addition & 1 deletion actions/parse-ci-reports/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ inputs:
description: |
Paths to report files (glob patterns supported, one per line or comma-separated).
Set to `auto:test`, `auto:coverage`, `auto:lint`, or `auto:all` for automatic detection.
Examples: `**/junit.xml`, `coverage/lcov.info`, `eslint-report.json`, `auto:all`, `auto:test,coverage/lcov.info`, `auto:test,auto:coverage`
Examples: `**/junit.xml`, `coverage/lcov.info`, `eslint-report.json`, `reports/results.sarif`, `auto:all`, `auto:test,coverage/lcov.info`, `auto:test,auto:coverage`
required: false
default: "auto:all"
report-name:
Expand Down
3 changes: 2 additions & 1 deletion actions/parse-ci-reports/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
"lcov",
"cobertura",
"eslint",
"checkstyle"
"checkstyle",
"sarif"
],
"author": "hoverkraft",
"license": "MIT",
Expand Down
55 changes: 43 additions & 12 deletions actions/parse-ci-reports/src/ReportPathResolver.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,12 @@ describe("ReportPathResolver", () => {
const coveragePatterns = [
"**/coverage/lcov.info",
"**/lcov.info",
"**/coverage/cobertura-coverage.xml",
"**/coverage.xml",
"**/cobertura.xml",
"**/coverage/*-lcov.info",
"**/*-lcov.info",
"**/coverage/coverage.xml",
"**/coverage/*-coverage.xml",
"**/coverage/cobertura.xml",
"**/coverage/*-cobertura.xml",
];
for (const pattern of coveragePatterns) {
assert.ok(
Expand All @@ -119,8 +122,8 @@ describe("ReportPathResolver", () => {
);
}

// Verify total count matches expected (5 test + 5 coverage)
assert.strictEqual(patterns.length, 10);
// Verify total count matches expected (5 test + 8 coverage)
assert.strictEqual(patterns.length, 13);
});

it("deduplicates overlapping auto modes", () => {
Expand All @@ -133,16 +136,32 @@ describe("ReportPathResolver", () => {
const lintPatterns = [
"**/eslint-report.json",
"**/eslint.json",
"**/*-eslint-report.json",
"**/*-eslint.json",
"**/checkstyle-result.xml",
"**/checkstyle.xml",
"**/*-checkstyle-result.xml",
"**/*-checkstyle.xml",
"**/*.sarif",
"**/*.sarif.json",
"**/sarif-report.json",
"**/*-sarif-report.json",
"**/prettier-check.log",
"**/prettier-check.txt",
"**/prettier-report.log",
"**/prettier-report.txt",
"**/*-prettier-check.log",
"**/*-prettier-check.txt",
"**/*-prettier-report.log",
"**/*-prettier-report.txt",
"**/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",
];
for (const pattern of lintPatterns) {
assert.ok(patterns.includes(pattern), `Missing lint pattern: ${pattern}`);
Expand All @@ -164,9 +183,12 @@ describe("ReportPathResolver", () => {
const coveragePatterns = [
"**/coverage/lcov.info",
"**/lcov.info",
"**/coverage/cobertura-coverage.xml",
"**/coverage.xml",
"**/cobertura.xml",
"**/coverage/*-lcov.info",
"**/*-lcov.info",
"**/coverage/coverage.xml",
"**/coverage/*-coverage.xml",
"**/coverage/cobertura.xml",
"**/coverage/*-cobertura.xml",
];
for (const pattern of coveragePatterns) {
assert.ok(
Expand All @@ -175,8 +197,8 @@ describe("ReportPathResolver", () => {
);
}

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

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

// Verify coverage patterns come from LCOVParser and CoberturaParser
assert.ok(autoPatterns.coverage.includes("**/coverage/lcov.info"));
assert.ok(autoPatterns.coverage.includes("**/cobertura.xml"));
assert.ok(autoPatterns.coverage.includes("**/coverage/cobertura.xml"));
assert.ok(autoPatterns.coverage.includes("**/*-lcov.info"));
assert.ok(autoPatterns.coverage.includes("**/coverage/*-coverage.xml"));
assert.ok(autoPatterns.coverage.includes("**/coverage/*-cobertura.xml"));

// Verify lint patterns come from ESLintParser, CheckStyleParser, PrettierParser, AstroCheckParser
// Verify lint patterns come from ESLintParser, CheckStyleParser, SarifParser, PrettierParser, AstroCheckParser
assert.ok(autoPatterns.lint.includes("**/eslint-report.json"));
assert.ok(autoPatterns.lint.includes("**/checkstyle.xml"));
assert.ok(autoPatterns.lint.includes("**/*.sarif"));
assert.ok(autoPatterns.lint.includes("**/prettier-check.log"));
assert.ok(autoPatterns.lint.includes("**/astro-check.log"));
assert.ok(autoPatterns.lint.includes("**/*-eslint.json"));
assert.ok(autoPatterns.lint.includes("**/*-checkstyle.xml"));
assert.ok(autoPatterns.lint.includes("**/*-sarif-report.json"));
assert.ok(autoPatterns.lint.includes("**/*-prettier-check.log"));
assert.ok(autoPatterns.lint.includes("**/*-astro-check.log"));
});

it("excludes node_modules files from glob patterns", async () => {
Expand Down
15 changes: 9 additions & 6 deletions actions/parse-ci-reports/src/parsers/AstroCheckParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,15 @@ export class AstroCheckParser extends BaseParser {
}

getAutoPatterns() {
return [
"**/astro-check.log",
"**/astro-check.txt",
"**/astro-check-report.log",
"**/astro-check-report.txt",
];
return this.buildBasenamePatterns(
[
"astro-check.log",
"astro-check.txt",
"astro-check-report.log",
"astro-check-report.txt",
],
{ includePrefixed: true },
);
}

parse(content) {
Expand Down
Loading
Loading