Upgrades to major dependencies (java 25, kotlin 2.3.21, gradle 9.5.0, AGP 9.3.1) - #308
Conversation
AndroidTestifyBot
left a comment
There was a problem hiding this comment.
Reviewed the AGP 9 / Gradle 9.5 / Kotlin 2.3 / Java 25 migration. The DSL migration itself is in good shape — lintOptions→lint, packagingOptions→packaging, kotlinOptions→kotlin { compilerOptions }, the devices→localDevices swap, dropping kotlin-android in favour of AGP's built-in Kotlin, and kapt→KSP all look correct, and the inline comments explaining the non-obvious AGP 9 replacements are appreciated. I verified locally on JDK 25 that :Library:bundleReleaseAar / :Ktx:bundleReleaseAar still produce testify-6.0.0.aar / testify-ktx-6.0.0.aar, and that ktlintCheck genuinely runs again on 14.2.0 (13.x was silently a no-op under AGP 9).
Three things need fixing before this merges:
1. Removing archivesBaseName renamed the androidTest APKs and broke the instrumented-test CI steps. The AAR name was preserved via the new BundleAar block, but the APK name wasn't. :Ktx:assembleDebugAndroidTest now emits Ktx-debug-androidTest.apk and :Library:assembleDebugAndroidTest emits Library-debug-androidTest.apk, while bitrise.yml:219-220 and bitrise.yml:254-255 still point at testify-debug-androidTest.apk / testify-ktx-debug-androidTest.apk. That's why the Ktx check is already red; Library will follow. Details and a verified fix in the inline comment.
2. The GitHub Actions workflows were never bumped off Java 21/17. bitrise.yml moved to 25, but .github/workflows/flix_sample.yml still pins java-version: 21 and is failing on this PR with error: invalid source release: 25, and .github/workflows/central_release.yml still pins java-version: 17, which would break Maven Central publishing on the next release.
3. :GmdSample no longer builds under AGP 9. androidx.databinding:viewbinding:9.3.1 declares minCompileSdk=34 (8.13.2 declared minCompileSdk=1), and the sample is still on compileSdk 33, so :GmdSample:assembleDebug fails outright. No bitrise workflow covers GMD, so CI won't catch it.
Also worth deciding before release (non-blocking on this diff): the published AARs are now Java 25 bytecode — I confirmed classes.jar in testify-6.0.0.aar is class file major version 69. That forces every consumer onto JDK 25 and AGP 9, which is a larger breaking change than the CHANGELOG currently conveys. See the CHANGELOG comment.
The rest are minor notes inline.
| } | ||
|
|
||
| android { | ||
| compileSdk 33 |
There was a problem hiding this comment.
compileSdk 33 no longer works under AGP 9. :GmdSample:assembleDebug fails on this branch:
> A failure occurred while executing CheckAarMetadataWorkAction
> 1. Dependency 'androidx.databinding:viewbinding:9.3.1' requires libraries and applications
that depend on it to compile against version 34 or later of the Android APIs.
:GmdSample is currently compiled against android-33.
This is a regression from the AGP bump, not pre-existing: viewbinding:8.13.2 declares minCompileSdk=1, viewbinding:9.3.1 declares minCompileSdk=34. The module uses viewBinding true, so it's unavoidable — compileSdk needs to go to 34+ (and targetSdk 33 is probably worth revisiting at the same time).
Note there's no test_gmd workflow in bitrise.yml, so CI stays green on this — it only shows up when someone builds the sample.
Separately: is jumping this sample from Java 1.8 straight to 25 intentional? It's the "old project" sample, so 1.8 may have been deliberate coverage for consumers who haven't migrated.
| @@ -1,6 +1,8 @@ | |||
| #Sun Aug 16 15:39:16 EDT 2026 | |||
There was a problem hiding this comment.
Machine-local timestamp from the wrapper task — worth dropping, it just creates churn on every wrapper update. (Adding distributionSha256Sum is a nice improvement, keep that.)
Restore archive base names:
- Gradle 9 removed the `archivesBaseName` property; replace it with
`base { archivesName = pom.artifact }` in the five published modules.
This drives the androidTest APK name as well as the AAR, so dropping it
had renamed the APKs that bitrise.yml pins and turned the Ktx and
Library instrumented-test steps red.
Move CI off the old JDKs:
- flix_sample.yml: java-version 21 -> 25 (was failing with
"invalid source release: 25").
- central_release.yml: java-version 17 -> 25, and refresh
actions/checkout@v2 -> v4 and actions/setup-java@v1 -> v4, since v1
cannot resolve JDK 25 and v4 requires an explicit distribution.
Fix samples that no longer build under AGP 9:
- GmdSample: compileSdk 33 -> 36. androidx.databinding:viewbinding:9.3.1
declares minCompileSdk=34, so checkDebugAarMetadata failed outright.
- LegacySample: compileSdk -> 36 to satisfy the AAR metadata of the
Testify modules it depends on, and drop the contradictory
`compileSdkVersion 35` from defaultConfig. targetSdk stays at 35.
Fix Plugin unit tests (run by bitrise as Plugin:test):
- Bump mockk to 1.14.11. JDK 25 reimplemented java.io over NIO
(IoOverNioFileSystem), so mockk 1.13.16 could not mock java.io.File and
every AdbTest failed with an NPE out of the real getAbsolutePath().
- Pull the ktlint plugin version from the included build's own catalog
instead of hardcoding it, so it cannot drift from the root catalog.
Remove the ActivityScenario context-parameter overload of
ScreenshotScenarioRule.assertSame():
- Kotlin 2.3 makes it ambiguous with the no-arg assertSame() at any call
site where an ActivityScenario is an implicit receiver, such as inside
ActivityScenario.test { }, which broke compilation of LegacySample's
androidTest sources. The overload was also what set the scenario, so
hiding rather than removing it compiles but throws
ScenarioRequiredException at runtime.
- Update ScenarioMainActivityScreenshotTest to set the scenario
explicitly via withScenario(this), matching the pattern used by every
other scenario test in the repo.
Also drop the machine-local timestamp from gradle-wrapper.properties and
document the consumer impact of Java 25 bytecode in the CHANGELOG.
mockk 1.14.5 bundles a Byte Buddy that officially supports up to Java 24 and rejects class file major version 69: java.lang.IllegalArgumentException: Java 25 (69) is not supported by the current version of Byte Buddy which officially supports Java 24 (68) Every mockk-based unit test in :Library and :Ktx failed with MockKException as a result, which is what turned the Ktx bitrise workflow red at its android-unit-test step. This is the same version already used by the :Plugin included build, where an older mockk additionally could not mock java.io.File on JDK 25.
AndroidTestifyBot
left a comment
There was a problem hiding this comment.
All seven points addressed. Re-verified on JDK 25 at c355dade:
| Was | Now |
|---|---|
Library-debug-androidTest.apk / Ktx-debug-androidTest.apk |
testify-debug-androidTest.apk / testify-ktx-debug-androidTest.apk — matches bitrise.yml:219-220 and :254-255; AAR still testify-6.0.0.aar |
flix_sample.yml on JDK 21, central_release.yml on JDK 17 |
both on 25 (plus checkout@v4 / setup-java@v4) |
:GmdSample:assembleDebug failing on checkDebugAarMetadata |
builds clean on compileSdk 36 |
Plugin pinning ktlint 13.1.0 inline |
alias(libs.plugins.ktlint) at 14.2.0 |
Plugin:test, Plugin:ktlintCheck, :Library:testDebugUnitTest and :Ktx:testDebugUnitTest all pass locally with mockk 1.14.11, and all 15 CI checks are green — including the two emulator shards and the Library/Ktx instrumented-test steps that were consuming the misnamed APKs.
Two notes on the parts that went beyond the review:
The assertSame() removal is better than what I suggested, and my premise was wrong. I claimed the context-parameter overload had no callers; it did — launchActivity<MainActivity>().test { rule.assertSame() } in the Legacy sample resolved to it via the implicit ActivityScenario receiver, which is invisible at the call site. That makes this a genuine behavioural trap rather than dead API: had the diagnostic just been suppressed, that call would have kept compiling while silently binding to the no-arg overload. Removing it and rewriting the call as rule.withScenario(this).assertSame() is the right resolution, and the CHANGELOG entry explains the migration well. I checked the remaining rule.assertSame() sites in Library/src/androidTest/**/scenario/ — they all use .use { scenario -> … } with a named parameter and call withScenario(scenario) first, so none of them were bound to the removed overload.
Good catch on Samples/Legacy. defaultConfig { compileSdkVersion 35 } was silently overriding the outer compileSdk 34 through Groovy's owner delegation — removing it and setting compileSdk 36 once is the correct fix, not just a version bump.
Non-blocking, for later:
:GmdSamplestill has no bitrise workflow, so nothing guards thecompileSdkfix. Given AGP 9 broke it once via a transitiveminCompileSdk, a build-only workflow would be cheap insurance.Plugins/Gradle/gradle/libs.versions.tomlkeepsgradle = "8.13.2"andkotlin = "2.2.0". I read that as deliberate —compileOnly libs.gradleplus a JVM 17 target keeps the plugin loadable in AGP 8 consumers' daemons even though the library artifacts now require AGP 9 — but flagging it in case the intent was to move it too.
LGTM.
What does this change accomplish?
Caution
Critical Update Needed: Check your configuration files.
This release uses Java 25 which requires you to update your app to use Java 25.
Java 25 also requires Android Gradle Plugin 9.x — the published artifacts are Java 25 bytecode and cannot be consumed by AGP 8.x
major version 69) and cannot be consumed by AGP 8.x
kotlinOptions,lintOptions,packagingOptionsand thelegacy
libraryVariantsAPI have been migrated to their AGP 9 replacementsScreenshotScenarioRule.assertSame()with anActivityScenariocontext parameter(JVM name
assertSameContext). Kotlin 2.3 makes it ambiguous with the no-argassertSame()at any call site where an
ActivityScenariois an implicit receiver, such as insideActivityScenario.test { }, so it could no longer be called. Set the scenario explicitlyinstead:
rule.withScenario(scenario).assertSame()