Skip to content

Commit afc2770

Browse files
oschwaldclaude
andcommitted
Add API compatibility checking with japicmp
This adds a GitHub Actions workflow that runs on PRs to detect breaking changes in the public API. The check compares the current build against the baseline version on Maven Central. ENG-3367 Co-Authored-By: Claude Opus 4.5 <[email protected]>
1 parent 6aa0d3c commit afc2770

File tree

3 files changed

+84
-1
lines changed

3 files changed

+84
-1
lines changed

.github/workflows/api-compat.yml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
name: API Compatibility Check
2+
on:
3+
pull_request:
4+
permissions:
5+
contents: read
6+
jobs:
7+
api-compat:
8+
runs-on: ubuntu-latest
9+
steps:
10+
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
11+
with:
12+
persist-credentials: false
13+
14+
- uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0
15+
with:
16+
distribution: temurin
17+
java-version: '17'
18+
19+
- uses: gradle/actions/setup-gradle@4d9f0ba0025fe599b4ebab900eb7f3a1d93ef4c2 # v5.0.0
20+
21+
- name: Check API Compatibility
22+
run: ./gradlew :device-sdk:japicmp

dev-bin/release.sh

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,10 @@ perl -pi -e "s/version = \"[^\"]+\"/version = \"$version\"/" build.gradle.kts
126126
# Update version in README.md
127127
perl -pi -e "s/com\.maxmind\.device:device-sdk:[0-9]+\.[0-9]+\.[0-9]+[a-zA-Z0-9\-]*/com.maxmind.device:device-sdk:$version/g" README.md
128128

129+
# Update baselineVersion in device-sdk/build.gradle.kts for API compatibility checking
130+
# This ensures the next PR compares against this newly released version
131+
perl -pi -e "s/val baselineVersion = \"[^\"]+\"/val baselineVersion = \"$version\"/" device-sdk/build.gradle.kts
132+
129133
git diff
130134

131135
read -r -n 1 -p "Commit changes? (y/n) " should_commit
@@ -135,7 +139,7 @@ if [ "$should_commit" != "y" ]; then
135139
exit 1
136140
fi
137141

138-
git add build.gradle.kts README.md
142+
git add build.gradle.kts README.md device-sdk/build.gradle.kts
139143
git commit -m "Preparing for $version"
140144

141145
# Build and publish to Maven Central

device-sdk/build.gradle.kts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ plugins {
88
alias(libs.plugins.maven.publish)
99
signing
1010
id("tech.apter.junit5.jupiter.robolectric-extension-gradle-plugin") version "0.9.0"
11+
id("me.champeau.gradle.japicmp") version "0.4.5"
1112
}
1213

1314
android {
@@ -168,3 +169,59 @@ mavenPublishing {
168169
signing {
169170
useGpgCmd()
170171
}
172+
173+
// API compatibility checking with japicmp
174+
// Compares the current build against the latest released version on Maven Central
175+
// Update this version after each release (the release script should do this automatically)
176+
val baselineVersion = "0.1.0"
177+
178+
// Download baseline AAR directly from Maven Central to avoid local project resolution
179+
val downloadBaselineAar by tasks.registering {
180+
val outputFile = layout.buildDirectory.file("japicmp/baseline.aar")
181+
outputs.file(outputFile)
182+
doLast {
183+
val url = "https://repo1.maven.org/maven2/com/maxmind/device/device-sdk/$baselineVersion/device-sdk-$baselineVersion.aar"
184+
val destFile = outputFile.get().asFile
185+
destFile.parentFile.mkdirs()
186+
java.net.URI(url).toURL().openStream().use { input ->
187+
destFile.outputStream().use { output ->
188+
input.copyTo(output)
189+
}
190+
}
191+
logger.lifecycle("Downloaded baseline AAR from $url")
192+
}
193+
}
194+
195+
// Extract classes.jar from baseline AAR for comparison
196+
val extractBaselineClasses by tasks.registering(Copy::class) {
197+
dependsOn(downloadBaselineAar)
198+
from(zipTree(layout.buildDirectory.file("japicmp/baseline.aar"))) {
199+
include("classes.jar")
200+
rename("classes.jar", "baseline-classes.jar")
201+
}
202+
into(layout.buildDirectory.dir("japicmp"))
203+
}
204+
205+
// Extract classes.jar from current AAR for comparison
206+
val extractCurrentClasses by tasks.registering(Copy::class) {
207+
dependsOn("bundleReleaseAar")
208+
from(zipTree(layout.buildDirectory.file("outputs/aar/device-sdk-release.aar"))) {
209+
include("classes.jar")
210+
rename("classes.jar", "current-classes.jar")
211+
}
212+
into(layout.buildDirectory.dir("japicmp"))
213+
}
214+
215+
tasks.register<me.champeau.gradle.japicmp.JapicmpTask>("japicmp") {
216+
dependsOn(extractBaselineClasses, extractCurrentClasses)
217+
oldClasspath.from(layout.buildDirectory.file("japicmp/baseline-classes.jar"))
218+
newClasspath.from(layout.buildDirectory.file("japicmp/current-classes.jar"))
219+
oldArchives.from(layout.buildDirectory.file("japicmp/baseline-classes.jar"))
220+
newArchives.from(layout.buildDirectory.file("japicmp/current-classes.jar"))
221+
accessModifier.set("public")
222+
onlyModified.set(true)
223+
failOnModification.set(true)
224+
includeSynthetic.set(false)
225+
txtOutputFile.set(layout.buildDirectory.file("japicmp/report.txt"))
226+
htmlOutputFile.set(layout.buildDirectory.file("japicmp/report.html"))
227+
}

0 commit comments

Comments
 (0)