Skip to content

AGP 9: Final phase - Gradle/AGP upgrade#22727

Merged
nbradbury merged 15 commits intofeature/agp9-feature-branchfrom
feature/agp9-phase-final
Mar 24, 2026
Merged

AGP 9: Final phase - Gradle/AGP upgrade#22727
nbradbury merged 15 commits intofeature/agp9-feature-branchfrom
feature/agp9-phase-final

Conversation

@nbradbury
Copy link
Copy Markdown
Contributor

@nbradbury nbradbury commented Mar 23, 2026

This is the final PR - other than the feature branch - for the AGP 9 upgrade project. Unlike previous PRs in this project, lint and unit tests should not be failing on this one.

Summary

  • Replaces deprecated packagingOptions with AGP 9 packaging { resources { ... } } DSL in WordPress/build.gradle and libs/editor/build.gradle
  • Reverts compileOptions in all modules to use JavaVersion.toVersion() instead of JvmTarget.fromTarget().target, which returned a String instead of the expected JavaVersion type
  • Migrates deprecated kotlinOptions to compilerOptions in root build.gradle, and uses lazy .configureEach instead of eager .all for KotlinCompile tasks
  • Removes deprecated org.gradle.configureondemand=true from gradle.properties (incompatible with configuration cache)
  • Removes android.enableR8.fullMode=false from gradle.properties (property removed in AGP 9; -dontoptimize in proguard.cfg provides equivalent protection)
  • Adds -dontoptimize to proguard.cfg for release safety — AGP 9 requires proguard-android-optimize.txt which enables aggressive R8 optimizations that can strip reflection-accessed code (Gson, FluxC, React Native). This preserves the safe behavior of the old proguard-android.txt default.
  • Adds explicit compileOptions to libs/networking and libs/mocks modules for consistency with all other modules
  • Suppresses UseTomlInstead lint error on dynamic GutenbergKit dependency in libs/editor/build.gradle (path is intentionally dynamic for local/remote substitution)
  • Removes invalid activity-alias attributes and fixes string concatenation lint error
  • Migrates deprecated onBackPressed() in JetpackFullPluginInstallActivity to OnBackPressedCallback for Android 16+ predictive back gesture support (GestureBackNavigation lint error)
  • Fixes missing spaces in string concatenations (TextConcatSpace lint errors) across ApplicationPasswordsConfiguration, MediaUploadHandler, ReaderTagsFeedPostListItem, and GutenbergContainerFragment
  • Fixes stale comment referencing "Java API 1.8" when the project uses Java 11

Test plan

  • Verify assembleWordPressDebug and assembleJetpackDebug build successfully
  • Verify unit tests pass
  • Verify lintWordPressRelease and lintJetpackRelease pass with no new errors
  • Verify release builds pass compilation (signing expected to fail locally)
  • Smoke test both WordPress and Jetpack apps on device

nbradbury and others added 7 commits March 17, 2026 11:21
Simultaneously upgrade Gradle (8.12.1 → 9.1.0) and AGP (8.10.1 → 9.0.1)
since AGP 8.x does not support Gradle 9.x.

Uses opt-out flags (android.newDsl=false, android.builtInKotlin=false) to
preserve existing kotlin-android/kapt plugins while on AGP 9.

Version bumps:
- Gradle 8.12.1 → 9.1.0
- AGP 8.10.1 → 9.0.1
- Dagger/Hilt 2.58 → 2.59.2
- Fladle 0.19.0 → 0.21.0

AGP 9 migration fixes:
- Remove archivesBaseName from defaultConfig (use base.archivesName)
- Move compileSdk from defaultConfig to android block
- Replace proguard-android.txt with proguard-android-optimize.txt
- Rename lintOptions → lint in root build.gradle
- Replace deprecated buildDir with layout.buildDirectory
- Remove obsolete android.useAndroidX and android.enableJetifier properties
- Fix kotlin {} → java {} for sourceCompatibility in JVM-only modules
- Remove allowBackup from posttypes library manifest (app-level concern)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove android.newDsl=false and android.builtInKotlin=false to fully
adopt AGP 9's new DSL and built-in Kotlin compilation support.

- Remove kotlin-android plugin from all modules (AGP 9 built-in)
- Migrate kapt → legacy-kapt in fluxc (AGP 9 requirement)
- Migrate applicationVariants API → androidComponents.onVariants
- Remove kotlin-android and kapt entries from version catalog
- Keep kotlin-compose plugin (still required for Compose compiler)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove android.nonTransitiveRClass and android.nonFinalResIds (now
always enabled in AGP 9) and android.enableR8.fullMode=false to adopt
the new default of full R8 mode.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Re-add android.enableR8.fullMode=false to preserve the previous R8
behavior. Enabling full mode can be done as a separate follow-up
after verifying keep rules with a release build.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…sistency

Migrate packagingOptions to packaging with resources block in WordPress
and editor modules, and align analytics module compileOptions to use
JvmTarget.fromTarget() matching all other modules.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@nbradbury nbradbury changed the base branch from trunk to feature/agp9-feature-branch March 23, 2026 15:26
@wpmobilebot
Copy link
Copy Markdown
Contributor

wpmobilebot commented Mar 23, 2026

App Icon📲 You can test the changes from this Pull Request in WordPress Android by scanning the QR code below to install the corresponding build.

App NameWordPress Android
Build TypeDebug
Versionpr22727-f07e071
Build Number1488
Application IDorg.wordpress.android.prealpha
Commitf07e071
Installation URL6hafred5qdom8
Automatticians: You can use our internal self-serve MC tool to give yourself access to those builds if needed.

@wpmobilebot
Copy link
Copy Markdown
Contributor

wpmobilebot commented Mar 23, 2026

App Icon📲 You can test the changes from this Pull Request in Jetpack Android by scanning the QR code below to install the corresponding build.

App NameJetpack Android
Build TypeDebug
Versionpr22727-f07e071
Build Number1488
Application IDcom.jetpack.android.prealpha
Commitf07e071
Installation URL0fo4lesi6vij0
Automatticians: You can use our internal self-serve MC tool to give yourself access to those builds if needed.

…oncat

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@wpmobilebot
Copy link
Copy Markdown
Contributor

wpmobilebot commented Mar 23, 2026

Project manifest changes for WordPress

The following changes in the WordPress's merged AndroidManifest.xml file were detected (build variant: jetpackRelease):

--- ./build/reports/diff_manifest/WordPress/jetpackRelease/base_manifest.txt	2026-03-24 10:54:30.520755489 +0000
+++ ./build/reports/diff_manifest/WordPress/jetpackRelease/head_manifest.txt	2026-03-24 10:54:41.690803726 +0000
@@ -180,11 +180,9 @@
         </activity> <!-- Web Links Deep Linking Activity Alias -->
         <activity-alias
             android:name="org.wordpress.android.WebLinksDeepLinkingIntentReceiverActivity"
-            android:excludeFromRecents="true"
             android:exported="true"
             android:label="@string/deep_linking_weblinks_alias_label"
-            android:targetActivity="org.wordpress.android.ui.deeplinks.DeepLinkingIntentReceiverActivity"
-            android:theme="@style/WordPress.NoActionBar" >
+            android:targetActivity="org.wordpress.android.ui.deeplinks.DeepLinkingIntentReceiverActivity" >
             <intent-filter android:autoVerify="true" >
                 <action android:name="android.intent.action.VIEW" />
 
@@ -303,11 +301,9 @@
         </activity-alias> <!-- Custom Wordpress URI Scheme Deep Linking Activity Alias -->
         <activity-alias
             android:name="org.wordpress.android.URISchemeDeepLinkingIntentReceiverActivity"
-            android:excludeFromRecents="true"
             android:exported="true"
             android:label="@string/deep_linking_urilinks_alias_label"
-            android:targetActivity="org.wordpress.android.ui.deeplinks.DeepLinkingIntentReceiverActivity"
-            android:theme="@style/WordPress.NoActionBar" >
+            android:targetActivity="org.wordpress.android.ui.deeplinks.DeepLinkingIntentReceiverActivity" >
             <intent-filter>
                 <action android:name="android.intent.action.VIEW" />
 

Go to https://buildkite.com/automattic/wordpress-android/builds/25662/canvas?sid=019d1f73-8371-4d82-9e12-295b0502575c, click on the Artifacts tab and audit the files.

@wpmobilebot
Copy link
Copy Markdown
Contributor

wpmobilebot commented Mar 23, 2026

Project manifest changes for WordPress

The following changes in the WordPress's merged AndroidManifest.xml file were detected (build variant: wordpressRelease):

--- ./build/reports/diff_manifest/WordPress/wordpressRelease/base_manifest.txt	2026-03-24 10:54:52.398733600 +0000
+++ ./build/reports/diff_manifest/WordPress/wordpressRelease/head_manifest.txt	2026-03-24 10:55:08.568769840 +0000
@@ -573,11 +573,9 @@
         </activity> <!-- Web Links Deep Linking Activity Alias -->
         <activity-alias
             android:name="org.wordpress.android.WebLinksDeepLinkingIntentReceiverActivity"
-            android:excludeFromRecents="true"
             android:exported="true"
             android:label="@string/deep_linking_weblinks_alias_label"
-            android:targetActivity="org.wordpress.android.ui.deeplinks.DeepLinkingIntentReceiverActivity"
-            android:theme="@style/WordPress.NoActionBar" >
+            android:targetActivity="org.wordpress.android.ui.deeplinks.DeepLinkingIntentReceiverActivity" >
             <intent-filter android:autoVerify="true" >
                 <action android:name="android.intent.action.VIEW" />
 
@@ -696,11 +694,9 @@
         </activity-alias> <!-- Custom Wordpress URI Scheme Deep Linking Activity Alias -->
         <activity-alias
             android:name="org.wordpress.android.URISchemeDeepLinkingIntentReceiverActivity"
-            android:excludeFromRecents="true"
             android:exported="true"
             android:label="@string/deep_linking_urilinks_alias_label"
-            android:targetActivity="org.wordpress.android.ui.deeplinks.DeepLinkingIntentReceiverActivity"
-            android:theme="@style/WordPress.NoActionBar" >
+            android:targetActivity="org.wordpress.android.ui.deeplinks.DeepLinkingIntentReceiverActivity" >
             <intent-filter>
                 <action android:name="android.intent.action.VIEW" />
 

Go to https://buildkite.com/automattic/wordpress-android/builds/25662/canvas?sid=019d1f73-8370-4104-843d-b292ffdc60db, click on the Artifacts tab and audit the files.

Replace deprecated onBackPressed() in JetpackFullPluginInstallActivity
with OnBackPressedCallback for Android 16+ predictive back gesture
support. Fix missing spaces in string concatenations across 4 files
(ApplicationPasswordsConfiguration, MediaUploadHandler,
ReaderTagsFeedPostListItem, GutenbergContainerFragment).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@nbradbury nbradbury added bot: dependencies update Dependabot dependency update dependencies Pull requests that update a dependency file Tech Debt and removed unit-tests-exemption bot: dependencies update Dependabot dependency update labels Mar 23, 2026
@nbradbury nbradbury changed the title AGP 9: Final phase - Gradle/AGP upgrade with deprecated API cleanup AGP 9: Final phase - Gradle/AGP upgrade Mar 23, 2026
@codecov
Copy link
Copy Markdown

codecov bot commented Mar 23, 2026

Codecov Report

❌ Patch coverage is 0% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 37.34%. Comparing base (367ce37) to head (f07e071).
⚠️ Report is 1 commits behind head on feature/agp9-feature-branch.

Files with missing lines Patch % Lines
...rdpress/android/ui/uploads/MediaUploadHandler.java 0.00% 1 Missing ⚠️
...tionpasswords/ApplicationPasswordsConfiguration.kt 0.00% 1 Missing ⚠️
Additional details and impacted files
@@                     Coverage Diff                      @@
##           feature/agp9-feature-branch   #22727   +/-   ##
============================================================
  Coverage                        37.34%   37.34%           
============================================================
  Files                             2316     2316           
  Lines                           123272   123272           
  Branches                         16712    16712           
============================================================
  Hits                             46042    46042           
  Misses                           73531    73531           
  Partials                          3699     3699           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@nbradbury nbradbury requested a review from adalpari March 23, 2026 18:41
@nbradbury nbradbury marked this pull request as ready for review March 23, 2026 18:41
nbradbury and others added 2 commits March 23, 2026 14:57
AGP 9 requires proguard-android-optimize.txt, which enables aggressive
R8 optimizations that can strip code accessed via reflection (Gson,
FluxC, React Native). Adding -dontoptimize to proguard.cfg preserves
the safe behavior of the old proguard-android.txt default. Optimization
can be enabled in a separate, well-tested PR.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@adalpari
Copy link
Copy Markdown
Contributor

General checks look good. You might want to double check this one:

  1. compileOptions in analytics module — potential type mismatch (Medium)

File: libs/analytics/build.gradle:25-26

sourceCompatibility JvmTarget.fromTarget(libs.versions.java.get()).target
targetCompatibility JvmTarget.fromTarget(libs.versions.java.get()).target

JvmTarget.fromTarget() returns a JvmTarget enum, and .target returns a String. But sourceCompatibility/targetCompatibility expect a JavaVersion. This works in other modules because they use kotlinOptions { jvmTarget = ... }, not compileOptions. I'd verify this doesn't silently coerce incorrectly — the safer approach is:

sourceCompatibility JavaVersion.toVersion(libs.versions.java.get())
targetCompatibility JavaVersion.toVersion(libs.versions.java.get())

The original code used JavaVersion.toVersion() which is the correct API for compileOptions. The "consistency" argument in the commit message doesn't hold since compileOptions and kotlinOptions take different types. I'd recommend reverting this change unless there's a specific AGP 9 deprecation of JavaVersion.toVersion().

Copy link
Copy Markdown
Contributor

@adalpari adalpari left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great progress with the migration!!
LGTM!

@nbradbury
Copy link
Copy Markdown
Contributor Author

  1. compileOptions in analytics module — potential type mismatch (Medium)

This is a good catch, and I'm surprised my review didn't spot it. I pointed Claude at your comment and it had this to say:

The reviewer is correct. The original code used JavaVersion.toVersion() which is the proper API for compileOptions. The change to JvmTarget.fromTarget().target returns a String, and while Groovy may coerce it, JavaVersion.toVersion() is the semantically correct API here.

I'll look into this further then update this PR once I'm confident of the changes.

…rget

JvmTarget.fromTarget().target returns a String, but compileOptions
expects JavaVersion. Revert all modules to use JavaVersion.toVersion()
which is the correct API for sourceCompatibility/targetCompatibility.
Remove unused JvmTarget imports from modules that no longer need them.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
nbradbury and others added 3 commits March 24, 2026 06:10
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Migrate kotlinOptions to compilerOptions in root build.gradle
- Use .configureEach instead of eager .all for KotlinCompile tasks
- Remove deprecated org.gradle.configureondemand (incompatible with
  configuration cache)
- Remove android.enableR8.fullMode=false (removed in AGP 9;
  -dontoptimize in proguard.cfg provides equivalent protection)
- Add explicit compileOptions to networking and mocks modules for
  consistency with all other modules

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The dependency path is intentionally dynamic for local/remote
substitution and cannot be moved to the version catalog.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@sonarqubecloud
Copy link
Copy Markdown

@nbradbury
Copy link
Copy Markdown
Contributor Author

@adalpari I resolved that issue then asked Claude for a thorough review:

Verdict
Approve. Clean, well-scoped migration work across 21 files. All changes are correct, consistent, and well-commented where needed. No correctness, security, or performance concerns. The PR is in good shape to merge.

I'll merge this into the feature branch, then prepare that for review.

@nbradbury nbradbury merged commit b38f37c into feature/agp9-feature-branch Mar 24, 2026
25 checks passed
@nbradbury nbradbury deleted the feature/agp9-phase-final branch March 24, 2026 11:22
@nbradbury nbradbury mentioned this pull request Mar 24, 2026
6 tasks
nbradbury added a commit that referenced this pull request Mar 24, 2026
* Upgrade Gradle 9.1.0 + AGP 9.0.1

Simultaneously upgrade Gradle (8.12.1 → 9.1.0) and AGP (8.10.1 → 9.0.1)
since AGP 8.x does not support Gradle 9.x.

Uses opt-out flags (android.newDsl=false, android.builtInKotlin=false) to
preserve existing kotlin-android/kapt plugins while on AGP 9.

Version bumps:
- Gradle 8.12.1 → 9.1.0
- AGP 8.10.1 → 9.0.1
- Dagger/Hilt 2.58 → 2.59.2
- Fladle 0.19.0 → 0.21.0

AGP 9 migration fixes:
- Remove archivesBaseName from defaultConfig (use base.archivesName)
- Move compileSdk from defaultConfig to android block
- Replace proguard-android.txt with proguard-android-optimize.txt
- Rename lintOptions → lint in root build.gradle
- Replace deprecated buildDir with layout.buildDirectory
- Remove obsolete android.useAndroidX and android.enableJetifier properties
- Fix kotlin {} → java {} for sourceCompatibility in JVM-only modules
- Remove allowBackup from posttypes library manifest (app-level concern)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Remove AGP 9 opt-out flags and adopt built-in Kotlin

Remove android.newDsl=false and android.builtInKotlin=false to fully
adopt AGP 9's new DSL and built-in Kotlin compilation support.

- Remove kotlin-android plugin from all modules (AGP 9 built-in)
- Migrate kapt → legacy-kapt in fluxc (AGP 9 requirement)
- Migrate applicationVariants API → androidComponents.onVariants
- Remove kotlin-android and kapt entries from version catalog
- Keep kotlin-compose plugin (still required for Compose compiler)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Remove obsolete AGP 9 gradle.properties flags

Remove android.nonTransitiveRClass and android.nonFinalResIds (now
always enabled in AGP 9) and android.enableR8.fullMode=false to adopt
the new default of full R8 mode.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Keep R8 full mode disabled to reduce migration risk

Re-add android.enableR8.fullMode=false to preserve the previous R8
behavior. Enabling full mode can be done as a separate follow-up
after verifying keep rules with a release build.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* AGP 9: Replace deprecated packagingOptions and fix compileOptions consistency

Migrate packagingOptions to packaging with resources block in WordPress
and editor modules, and align analytics module compileOptions to use
JvmTarget.fromTarget() matching all other modules.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix lint errors: remove invalid activity-alias attrs and fix string concat

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix lint errors: migrate onBackPressed and fix TextConcatSpace warnings

Replace deprecated onBackPressed() in JetpackFullPluginInstallActivity
with OnBackPressedCallback for Android 16+ predictive back gesture
support. Fix missing spaces in string concatenations across 4 files
(ApplicationPasswordsConfiguration, MediaUploadHandler,
ReaderTagsFeedPostListItem, GutenbergContainerFragment).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add -dontoptimize to ProGuard rules for release safety

AGP 9 requires proguard-android-optimize.txt, which enables aggressive
R8 optimizations that can strip code accessed via reflection (Gson,
FluxC, React Native). Adding -dontoptimize to proguard.cfg preserves
the safe behavior of the old proguard-android.txt default. Optimization
can be enabled in a separate, well-tested PR.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add comment explaining -dontoptimize in ProGuard rules

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Revert compileOptions to use JavaVersion.toVersion() instead of JvmTarget

JvmTarget.fromTarget().target returns a String, but compileOptions
expects JavaVersion. Revert all modules to use JavaVersion.toVersion()
which is the correct API for sourceCompatibility/targetCompatibility.
Remove unused JvmTarget imports from modules that no longer need them.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix stale comment referencing Java 1.8 instead of version catalog

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix deprecated Gradle/AGP 9 config and add missing compileOptions

- Migrate kotlinOptions to compilerOptions in root build.gradle
- Use .configureEach instead of eager .all for KotlinCompile tasks
- Remove deprecated org.gradle.configureondemand (incompatible with
  configuration cache)
- Remove android.enableR8.fullMode=false (removed in AGP 9;
  -dontoptimize in proguard.cfg provides equivalent protection)
- Add explicit compileOptions to networking and mocks modules for
  consistency with all other modules

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Suppress UseTomlInstead lint error for dynamic GutenbergKit dependency

The dependency path is intentionally dynamic for local/remote
substitution and cannot be moved to the version catalog.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file Tech Debt

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants