Skip to content

Commit ae0f500

Browse files
committed
feat: Add report aggregator plugin
1 parent 012a99b commit ae0f500

20 files changed

+906
-2
lines changed

.github/pull_request_template.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<!--
2+
# Pull Request Instructions
3+
4+
* PR titles should follow https://www.conventionalcommits.org.
5+
6+
-->
7+
8+
## Pull Request Submission Checklist
9+
10+
Please confirm that you have done the following before requesting reviews:
11+
12+
- [ ] I have typed an adequate description that explains **why** I am making this change.
13+
- [ ] I have installed and run standard pre-commit hooks that lints and validates my code.
14+
15+
### Description
16+
17+
* <!-- WRITE A SHORT DESCRIPTION OF CHANGES -->
18+
19+
---

.github/workflows/build.yaml

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
name: Gradle Build
2+
3+
on:
4+
push:
5+
branches: [ main, master, develop ]
6+
pull_request:
7+
branches: [ main, master, develop ]
8+
9+
jobs:
10+
build:
11+
runs-on: ubuntu-latest
12+
13+
steps:
14+
- name: Checkout repository
15+
uses: actions/checkout@v3
16+
17+
- name: Set up JDK 21
18+
uses: actions/setup-java@v3
19+
with:
20+
java-version: '21'
21+
distribution: 'temurin'
22+
cache: gradle
23+
24+
- name: Grant execute permission for gradlew
25+
run: chmod +x gradlew
26+
27+
- name: Run ktlintFormat
28+
run: ./gradlew ktlintFormat
29+
30+
- name: Commit format changes if any
31+
uses: stefanzweifel/git-auto-commit-action@v4
32+
with:
33+
commit_message: "style: Format code with ktlint"
34+
file_pattern: "*.kt *.kts"
35+
36+
- name: Run assemble
37+
run: ./gradlew assemble
38+
39+
- name: Run tests
40+
run: ./gradlew test

.github/workflows/publish.yaml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
name: Publish Gradle Plugin
2+
3+
on:
4+
release:
5+
types: [published]
6+
7+
jobs:
8+
publish:
9+
runs-on: ubuntu-latest
10+
11+
steps:
12+
- name: Checkout repository
13+
uses: actions/checkout@v3
14+
15+
- name: Set up JDK 21
16+
uses: actions/setup-java@v3
17+
with:
18+
java-version: '21'
19+
distribution: 'temurin'
20+
cache: gradle
21+
22+
- name: Grant execute permission for gradlew
23+
run: chmod +x gradlew
24+
25+
- name: Publish plugin
26+
run: ./gradlew publishPlugins
27+
env:
28+
GRADLE_PUBLISH_KEY: ${{ secrets.GRADLE_PUBLISH_KEY }}
29+
GRADLE_PUBLISH_SECRET: ${{ secrets.GRADLE_PUBLISH_SECRET }}

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
.gradle/
33
build/
44

5+
*.DS_Store
6+
57
# Local configuration file (sdk path, etc)
68
local.properties
79

.pre-commit-config.yaml

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
default_install_hook_types: [ pre-commit, commit-msg ]
2+
# See https://pre-commit.com for more information
3+
# See https://pre-commit.com/hooks.html for more hooks
4+
repos:
5+
- repo: https://github.com/pre-commit/pre-commit-hooks
6+
rev: v4.3.0 # Get the latest version from: https://github.com/pre-commit/pre-commit-hooks/releases
7+
hooks:
8+
- id: trailing-whitespace
9+
exclude: \.key$
10+
stages:
11+
- pre-commit
12+
- id: end-of-file-fixer
13+
exclude: \.key$
14+
stages:
15+
- pre-commit
16+
- id: check-yaml
17+
args: [ --allow-multiple-documents ]
18+
stages:
19+
- pre-commit
20+
- id: check-added-large-files
21+
stages:
22+
- pre-commit
23+
- repo: https://github.com/commitizen-tools/commitizen
24+
rev: v2.32.5 # Grab latest version from https://github.com/commitizen-tools/commitizen/tags
25+
hooks:
26+
- id: commitizen
27+
name: Commit Zen
28+
stages: [ commit-msg ]
29+
- repo: local
30+
hooks:
31+
- id: ktlint-format
32+
name: Kotlin Linter Format
33+
entry: ./gradlew ktlintFormat
34+
language: system
35+
files: \.(kt|kts)$
36+
pass_filenames: false
37+
stages:
38+
- pre-commit
39+
- repo: https://github.com/gitguardian/ggshield
40+
rev: v1.25.0
41+
hooks:
42+
- id: ggshield
43+
language: python
44+
stages: [ pre-commit ]
45+
args: [ 'secret', 'scan', 'pre-commit' ]

README.md

Lines changed: 141 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,141 @@
1-
# better-android-junit-report-aggregator
2-
A better JUnit XML Test Report Aggregator for Complex Android Build Systems
1+
# better-android-junit-report-aggregator: Android JUnit XML Test Report Aggregator
2+
3+
[![Gradle Plugin Portal](https://img.shields.io/gradle-plugin-portal/v/io.github.bhargavms.junit.better-aggregator?label=Gradle%20Plugin%20Portal)](https://plugins.gradle.org/plugin/io.github.bhargavms.junit.better-aggregator)
4+
[![License](https://img.shields.io/github/license/bhargavms/better-android-junit-report-aggregator)](LICENSE)
5+
6+
better-android-junit-report-aggregator elegantly consolidates JUnit XML test results across multi-module, multi-project Gradle architectures in Android projects. It solves the challenge of collecting and accessing distributed test reports in complex build systems without modifying your existing test infrastructure.
7+
8+
## Features
9+
10+
- **Seamless Multi-Module Collection**: Automatically discovers and aggregates test results from all modules
11+
- **Centralized Access**: Creates a single consolidated location for all test results
12+
- **CI/CD Integration**: Simplifies test outcomes for popular CI systems (GitHub Actions, CircleCI, Jenkins)
13+
14+
## Installation
15+
16+
Add to your root build.gradle:
17+
18+
```kotlin
19+
plugins {
20+
id("io.github.bhargavms.junit.better-aggregator") version "1.0.0"
21+
}
22+
```
23+
24+
Or using the legacy syntax:
25+
26+
```groovy
27+
buildscript {
28+
repositories {
29+
gradlePluginPortal()
30+
}
31+
dependencies {
32+
classpath "io.github.bhargavms.junit.better-aggregator:io.github.bhargavms.junit.better-aggregator.gradle.plugin:1.0.0"
33+
}
34+
}
35+
36+
apply plugin: "io.github.bhargavms.junit.better-aggregator"
37+
```
38+
39+
## Basic Usage
40+
41+
After applying the plugin, run your tests as normal:
42+
43+
```bash
44+
./gradlew test
45+
```
46+
47+
Then, run the aggregation task:
48+
49+
```bash
50+
./gradlew aggregateJUnitReports
51+
```
52+
53+
This will collect all JUnit XML reports into the `outputDir` directory that you configured.
54+
55+
## Configuration
56+
57+
Configure the plugin in your root build.gradle:
58+
59+
```kotlin
60+
betterAggregator {
61+
// Output directory (relative to root project)
62+
outputDir.set("${project.getLayout().buildDirectory.dir("aggregated-reports").get().asFile.absolutePath}")
63+
}
64+
```
65+
66+
### CI Integration
67+
68+
For GitHub Actions:
69+
70+
```yaml
71+
- name: Run tests
72+
run: ./gradlew test
73+
74+
- name: Aggregate test reports
75+
run: ./gradlew aggregateJUnitReports
76+
77+
- name: Upload test reports
78+
uses: actions/upload-artifact@v2
79+
with:
80+
name: test-reports
81+
# If using the outputDir configuration as defined in this README
82+
path: build/reports/aggregated-junit-reports
83+
```
84+
85+
### Custom Tasks
86+
87+
Create a task that depends on test execution and report aggregation:
88+
89+
```kotlin
90+
tasks.register("testAndAggregate") {
91+
dependsOn("test", "aggregateJUnitReports")
92+
group = "Verification"
93+
description = "Runs tests and aggregates all reports"
94+
}
95+
```
96+
97+
## Compatibility
98+
99+
- Gradle: 7.0+
100+
- Kotlin: 1.5.0+
101+
- Android Gradle Plugin: 4.1.0+
102+
- Java: 8+
103+
104+
## How It Works
105+
106+
The plugin:
107+
108+
1. Scans your project structure to identify modules
109+
2. Searches for JUnit XML reports in configured directories
110+
3. Copies and organizes reports in the output directory
111+
4. Generates a consolidated overview of test results
112+
113+
## Best Practices
114+
115+
- Run the aggregation task after all tests complete
116+
- Include the task in your CI pipeline
117+
- Consider adding the output directory to your .gitignore file
118+
119+
## Contributing
120+
121+
Contributions are welcome! Please feel free to submit a Pull Request.
122+
123+
1. Fork the repository
124+
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
125+
3. Ensure you follow [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/)
126+
Commit your changes (`git commit -m 'feat: Add some amazing feature'`)
127+
4. Push to the branch (`git push origin feature/amazing-feature`)
128+
5. Open a Pull Request
129+
130+
## License
131+
132+
This project is licensed under the Apache 2.0 - see the [LICENSE](LICENSE) file for details.
133+
134+
## Credits
135+
136+
Developed by [Bhargav Srinivasan](https://github.com/bhargavms).
137+
138+
## Similar Projects
139+
140+
- [Kover](https://github.com/Kotlin/kotlinx-kover) - Kotlin coverage tool
141+
- [JaCoCo](https://github.com/jacoco/jacoco) - Java code coverage library

build.gradle.kts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
buildscript {
2+
dependencies {
3+
classpath(libs.plugin.android.gradle)
4+
classpath(libs.plugin.kotlin.gradle)
5+
}
6+
}
7+
8+
plugins {
9+
alias(libs.plugins.kotlin.lint.jlleitschuh)
10+
}
11+
12+
tasks.ktlintFormat {
13+
dependsOn(
14+
subprojects
15+
.filterNot(::isPlainDir)
16+
.map { "${it.path}:ktlintFormat" }
17+
)
18+
}
19+
20+
tasks.register("check") {
21+
dependsOn(subprojects.map { "${it.name}:check" })
22+
}
23+
24+
fun isPlainDir(project: Project) =
25+
!project.file("build.gradle").exists() && !project.file("build.gradle.kts").exists()

gradle.properties

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
kotlin.code.style=official
2+
3+
org.gradle.parallel=true
4+
org.gradle.jvmargs=-Xmx32g
5+
org.gradle.configureondemand=true
6+
org.gradle.daemon=true
7+
8+
android.useAndroidX=true
9+
android.enableJetifier=true
10+
android.useDeprecatedNdk=true
11+
android.jetifier.ignorelist=android-base-common,common
12+
13+
systemProp.sonar.host.url=https\://sonarcloud.io
14+
15+
file.encoding=utf-8
16+
android.defaults.buildfeatures.buildconfig=true
17+
android.nonTransitiveRClass=false
18+
android.nonFinalResIds=false
19+
android.enableR8.fullMode=false

gradle/libs.versions.toml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[libraries]
2+
plugin-android-gradle = { module = "com.android.tools.build:gradle", version.ref = "version-plugin-agp" }
3+
plugin-kotlin-gradle = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "version-kotlin-stdlib" }
4+
5+
[plugins]
6+
kotlin-lint-jlleitschuh = { id = "org.jlleitschuh.gradle.ktlint", version = "11.5.1"}
7+
android-junit5-support = { id = "de.mannodermaus.android-junit5", version = "1.8.2.1" }
8+
documentation-dokka = { id = "org.jetbrains.dokka", version = "1.8.10" }
9+
10+
11+
[versions]
12+
version-kotlin-stdlib = "2.0.21"
13+
version-plugin-agp = "8.9.0"

gradle/wrapper/gradle-wrapper.jar

42.6 KB
Binary file not shown.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
distributionBase=GRADLE_USER_HOME
2+
distributionPath=wrapper/dists
3+
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip
4+
networkTimeout=10000
5+
validateDistributionUrl=true
6+
zipStoreBase=GRADLE_USER_HOME
7+
zipStorePath=wrapper/dists

0 commit comments

Comments
 (0)