Skip to content

Commit dc5b77b

Browse files
Copilotneilime
andcommitted
Add auto-detection, GitHub annotations, and use hoverkraft setup-node
Co-authored-by: neilime <314088+neilime@users.noreply.github.com>
1 parent 3f5e399 commit dc5b77b

3 files changed

Lines changed: 150 additions & 19 deletions

File tree

actions/parse-ci-reports/README.md

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,35 @@ It supports multiple common report standards out of the box.
4343
report-name: "CI Results"
4444
```
4545
46+
### Auto-Detection Mode
47+
48+
Let the action automatically find common report files:
49+
50+
```yaml
51+
- name: Parse all CI reports
52+
uses: hoverkraft-tech/ci-github-common/actions/parse-ci-reports@v1
53+
with:
54+
report-paths: "auto:all"
55+
report-name: "CI Results"
56+
```
57+
58+
Or target specific report types:
59+
60+
```yaml
61+
- name: Parse test reports only
62+
uses: hoverkraft-tech/ci-github-common/actions/parse-ci-reports@v1
63+
with:
64+
report-paths: "auto:test"
65+
report-name: "Test Results"
66+
```
67+
68+
Auto-detection modes:
69+
70+
- `auto:test` - Finds JUnit XML and TAP files
71+
- `auto:coverage` - Finds LCOV and Cobertura coverage files
72+
- `auto:lint` - Finds ESLint JSON and CheckStyle XML files
73+
- `auto:all` - Finds all supported report types
74+
4675
### Generate PR Comment
4776

4877
```yaml
@@ -83,15 +112,28 @@ It supports multiple common report standards out of the box.
83112
fail-on-error: "true"
84113
```
85114

115+
### GitHub Annotations
116+
117+
Generate GitHub annotations for failed tests and linting issues:
118+
119+
```yaml
120+
- name: Parse reports with annotations
121+
uses: hoverkraft-tech/ci-github-common/actions/parse-ci-reports@v1
122+
with:
123+
report-paths: "auto:all"
124+
report-name: "CI Results"
125+
output-format: "annotations"
126+
```
127+
86128
## Inputs
87129

88-
| Input | Description | Required | Default |
89-
| ---------------- | -------------------------------------------------------------------------------- | -------- | ------------------ |
90-
| `report-paths` | Paths to report files (glob patterns supported, one per line or comma-separated) | Yes | - |
91-
| `report-name` | Name to display in the summary | No | `"Report Summary"` |
92-
| `include-passed` | Whether to include passed tests in the summary | No | `false` |
93-
| `output-format` | Output format: 'summary', 'Markdown', or 'both' | No | `"both"` |
94-
| `fail-on-error` | Whether to fail the action if any test failures are detected | No | `false` |
130+
| Input | Description | Required | Default |
131+
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------ | -------- | ------------------ |
132+
| `report-paths` | Paths to report files (glob patterns supported) or auto-detection mode ('auto:test', 'auto:coverage', 'auto:lint', 'auto:all') | Yes | - |
133+
| `report-name` | Name to display in the summary | No | `"Report Summary"` |
134+
| `include-passed` | Whether to include passed tests in the summary | No | `false` |
135+
| `output-format` | Output format: 'summary', 'Markdown', 'annotations', or 'both' | No | `"both"` |
136+
| `fail-on-error` | Whether to fail the action if any test failures are detected | No | `false` |
95137

96138
## Outputs
97139

actions/parse-ci-reports/action.yml

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ inputs:
99
report-paths:
1010
description: |
1111
Paths to report files (glob patterns supported, one per line or comma-separated).
12-
Examples: '**/junit.xml', 'coverage/lcov.info', 'eslint-report.json'
12+
Set to 'auto:test', 'auto:coverage', 'auto:lint', or 'auto:all' for automatic detection.
13+
Examples: '**/junit.xml', 'coverage/lcov.info', 'eslint-report.json', 'auto:all'
1314
required: true
1415
report-name:
1516
description: |
@@ -21,7 +22,7 @@ inputs:
2122
default: "false"
2223
output-format:
2324
description: |
24-
Output format: 'summary' (GitHub Step Summary), 'markdown' (for PR comments), or 'both'.
25+
Output format: 'summary' (GitHub Step Summary), 'markdown' (for PR comments), 'annotations' (GitHub annotations), or 'both'.
2526
default: "both"
2627
fail-on-error:
2728
description: |
@@ -58,20 +59,15 @@ runs:
5859
using: "composite"
5960
steps:
6061
- name: Setup Node.js
61-
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
62+
uses: hoverkraft-tech/ci-github-nodejs/actions/setup-node@32a69b7b8fd5f7ab7bf656e7e88aa90ad235cf8d # 0.18.0
6263
with:
63-
node-version: "18"
64-
65-
- name: Install dependencies
66-
shell: bash
67-
run: |
68-
cd ${{ github.action_path }}
69-
npm ci --omit=dev
64+
working-directory: ${{ github.action_path }}
7065

7166
- name: Parse reports
7267
id: parse-reports
7368
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
7469
env:
70+
NODE_PATH: ${{ github.action_path }}/node_modules
7571
REPORT_PATHS: ${{ inputs.report-paths }}
7672
REPORT_NAME: ${{ inputs.report-name }}
7773
INCLUDE_PASSED: ${{ inputs.include-passed }}

actions/parse-ci-reports/src/index-action.js

Lines changed: 95 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,27 @@
1+
// Auto-detection patterns for common report files
2+
const AUTO_PATTERNS = {
3+
test: [
4+
"**/junit*.xml",
5+
"**/test-results/**/*.xml",
6+
"**/test-reports/**/*.xml",
7+
"**/*test*.xml",
8+
"**/*.tap",
9+
],
10+
coverage: [
11+
"**/coverage/lcov.info",
12+
"**/coverage/cobertura-coverage.xml",
13+
"**/coverage.xml",
14+
"**/lcov.info",
15+
"**/cobertura.xml",
16+
],
17+
lint: [
18+
"**/eslint-report.json",
19+
"**/eslint.json",
20+
"**/checkstyle-result.xml",
21+
"**/checkstyle.xml",
22+
],
23+
};
24+
125
// Dynamically import ES modules
226
async function runAction({ core, glob: globModule, inputs }) {
327
try {
@@ -9,12 +33,33 @@ async function runAction({ core, glob: globModule, inputs }) {
933
core.info(`Report Name: ${inputs.reportName}`);
1034
core.info(`Output Format: ${inputs.outputFormat}`);
1135

12-
// Find report files using glob
13-
const patternList = inputs.reportPaths
36+
// Handle auto-detection
37+
let patternList = inputs.reportPaths
1438
.split(/[,\n]/)
1539
.map((p) => p.trim())
1640
.filter((p) => p.length > 0);
1741

42+
// Check for auto mode
43+
const autoMode = patternList.find((p) => p.startsWith("auto:"));
44+
if (autoMode) {
45+
const mode = autoMode.split(":")[1];
46+
core.info(`Auto-detection mode: ${mode}`);
47+
48+
patternList = [];
49+
if (mode === "all" || mode === "test") {
50+
patternList.push(...AUTO_PATTERNS.test);
51+
}
52+
if (mode === "all" || mode === "coverage") {
53+
patternList.push(...AUTO_PATTERNS.coverage);
54+
}
55+
if (mode === "all" || mode === "lint") {
56+
patternList.push(...AUTO_PATTERNS.lint);
57+
}
58+
59+
core.info(`Using patterns: ${patternList.join(", ")}`);
60+
}
61+
62+
// Find report files using glob
1863
const files = [];
1964
for (const pattern of patternList) {
2065
const globber = await globModule.create(pattern, {
@@ -40,6 +85,14 @@ async function runAction({ core, glob: globModule, inputs }) {
4085
(msg) => core.error(msg),
4186
);
4287

88+
// Generate GitHub annotations if requested
89+
if (
90+
inputs.outputFormat === "annotations" ||
91+
inputs.outputFormat === "both"
92+
) {
93+
generateAnnotations(core, aggregatedData);
94+
}
95+
4396
// Generate outputs using core
4497
const { markdown, summary } = parserCore.generateOutput(
4598
aggregatedData,
@@ -86,4 +139,44 @@ async function runAction({ core, glob: globModule, inputs }) {
86139
}
87140
}
88141

142+
/**
143+
* Generate GitHub annotations for failed tests and lint issues
144+
*/
145+
function generateAnnotations(core, reportData) {
146+
// Annotate failed tests
147+
const failedTests = reportData.getFailedTests();
148+
for (const test of failedTests.slice(0, 10)) {
149+
// Limit to 10 annotations
150+
const properties = {
151+
title: `Test Failed: ${test.name}`,
152+
};
153+
154+
if (test.file) {
155+
properties.file = test.file;
156+
}
157+
158+
core.error(test.message || "Test failed", properties);
159+
}
160+
161+
// Annotate lint errors and warnings
162+
annotateLintIssues(core, reportData.getErrors(), core.error, 10);
163+
annotateLintIssues(core, reportData.getWarnings(), core.warning, 10);
164+
}
165+
166+
/**
167+
* Helper to annotate lint issues
168+
*/
169+
function annotateLintIssues(core, issues, annotationFn, limit) {
170+
for (const issue of issues.slice(0, limit)) {
171+
const properties = {
172+
title: `${issue.rule}: ${issue.message}`,
173+
file: issue.file,
174+
startLine: issue.line,
175+
startColumn: issue.column,
176+
};
177+
178+
annotationFn.call(core, issue.message, properties);
179+
}
180+
}
181+
89182
module.exports = { runAction };

0 commit comments

Comments
 (0)