Skip to content

Sync fork: merge upstream tauri-apps/tauri dev - #17

Merged
OlympusLedgerOrg merged 20 commits into
devfrom
sync-upstream-dev-2026-08-11
Aug 12, 2026
Merged

Sync fork: merge upstream tauri-apps/tauri dev#17
OlympusLedgerOrg merged 20 commits into
devfrom
sync-upstream-dev-2026-08-11

Conversation

@OlympusLedgerOrg

@OlympusLedgerOrg OlympusLedgerOrg commented Aug 11, 2026

Copy link
Copy Markdown
Owner

Summary

  • Merges 2 upstream commits into our fork's dev branch, keeping all 82 org-specific commits
  • Upstream commits: e2e585ad1 (fix(android): update template to use gradle 9), 448d39ee2 (fix: menu related commands can panic)

Conflicts resolved

  • .github/workflows/test-android.yml — kept our custom Linux-deps install step + pinned corepack version
  • crates/tauri-cli/templates/mobile/android/app/build.gradle.kts — took upstream's file(...) wrapper syntax fix (no org customization on that line)

Test plan

  • CI status checks (12 required)

Summary by CodeRabbit

  • New Features

    • Android projects now use Gradle 9.6.1, Android Gradle Plugin 9.3.1, and Kotlin 2.2.
    • Improved ProGuard/R8 configuration preserves reflection-based plugin discovery in minified release builds.
    • Android plugin templates support customizable consumer ProGuard rules.
  • Bug Fixes

    • Invalid menu or tray item types now return a clear error instead of causing a panic.
  • Documentation

    • Added upgrade guidance and regression coverage for Android consumer ProGuard configuration and R8 plugin discovery.

Legend-Master and others added 3 commits August 10, 2026 16:28
* Android: Gradle upgrades

* typos

* bump agp to latest

* Remove all jvmTarget

* clean up unused

* migrate to getByName

* remove `id("org.jetbrains.kotlin.android")`  from plugin template

* Try disable `builtInKotlin`

* revert all gradle 9 migrations

* back to JVM_1_8

* remove `org.gradle.toolchains.foojay-resolver-convention` as I still don't understand what it does

* `android.useAndroidx` is true by default

* fix white spaces before first packaging

* Only `consumerProguardFiles` is used for Android libaries

* pickup the right proguard rules

* install cli instead of moving the built one

* bump spin and install locked

* try latest java and ndk

* try to work around missing `consumer-rules.pro`

* Add change files

* more specific about both gradle and plugin versions

* remove unused linux deps installation step

* Revert "try to work around missing `consumer-rules.pro`"

This reverts commit f1afdd9.

* Disable `android.proguard.failOnMissingFiles` instead

* update change file that we don't do the work around in tauri-build anymore

* update gradle wrapper and scripts

* use file helper
reference: https://issuetracker.google.com/issues/463283604#comment10

---------

Co-authored-by: Megh Parikh <meghprkh@gmail.com>
* fix: menu related commands can panic

* update change file
# Conflicts:
#	.github/workflows/test-android.yml
#	crates/tauri-cli/templates/mobile/android/app/build.gradle.kts
@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The pull request updates Android templates to Gradle 9.6.1, Android Gradle Plugin 9.3.1, and Kotlin 2.2. It adds consumer ProGuard rules and R8 plugin-discovery tests. Invalid menu kinds now return UnexpectedMenuKind instead of panicking.

Changes

Android template modernization

Layer / File(s) Summary
Android Gradle 9 toolchain
.github/workflows/test-android.yml, crates/tauri-cli/templates/mobile/android/..., .changes/android-gradle-9.md
The Android templates and workflow use updated Gradle, Android Gradle Plugin, Kotlin, Java, and NDK versions. The wrapper scripts and Gradle properties support the updated toolchain.
Android build task execution
crates/tauri-build/src/mobile.rs, crates/tauri-cli/templates/mobile/android/buildSrc/src/main/kotlin/*
Generated tasks use injected ExecOperations, explicit project directories, lazy registration, and explicit implementation configuration lookup.
Android consumer ProGuard configuration
crates/tauri-cli/templates/mobile/android/..., crates/tauri-cli/templates/plugin/android/*, crates/tauri/mobile/android/*, examples/api/src-tauri/tauri-plugin-sample/android/*, .changes/android-consumer-proguard.md
Application and plugin templates use consumer ProGuard files. Tauri-specific annotation keep rules and project guidance are added. Release ProGuard build configuration is removed.
R8 plugin-discovery regression coverage
examples/android-r8-plugin-discovery/*, .github/workflows/test-android-r8-plugin-discovery.yml, .changes/android-consumer-proguard-keepattributes.md
A standalone Android project builds a minified release and tests reflective plugin, command, permission callback, and activity callback discovery. CI runs the instrumentation tests on an API 34 emulator.

Typed invalid menu-kind errors

Layer / File(s) Summary
Unexpected menu-kind error propagation
crates/tauri/src/error.rs, crates/tauri/src/menu/plugin.rs, crates/tauri/src/tray/plugin.rs, crates/tauri-macros/src/*, .changes/menu-command-panic-on-wrong-input.md
Menu and tray operations return crate::Error::UnexpectedMenuKind for unsupported kinds. Generated code and documentation no longer use unreachable!() for this case.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ReflectivePluginDiscoveryTest
  participant R8
  participant TauriAndroid
  ReflectivePluginDiscoveryTest->>R8: Build and run minified release instrumentation tests
  R8->>TauriAndroid: Apply transitive consumer-rules.pro
  ReflectivePluginDiscoveryTest->>TauriAndroid: Reflectively discover and invoke annotated plugin methods
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately identifies the pull request as a synchronization of upstream Tauri changes into the fork's dev branch.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch sync-upstream-dev-2026-08-11

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (1)
crates/tauri-cli/templates/mobile/android/app/build.gradle.kts (1)

57-60: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove the duplicate compileOptions block.

The same Java 8 settings already exist at Line 64 through Line 67. Keep one block to prevent future configuration drift.

Proposed cleanup
-    compileOptions {
-        sourceCompatibility = JavaVersion.VERSION_1_8
-        targetCompatibility = JavaVersion.VERSION_1_8
-    }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/tauri-cli/templates/mobile/android/app/build.gradle.kts` around lines
57 - 60, Remove the duplicate compileOptions block from the Android build
configuration, keeping the existing JavaVersion.VERSION_1_8 sourceCompatibility
and targetCompatibility block later in the file as the single authoritative
configuration.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.changes/menu-command-panic-on-wrong-input.md:
- Line 6: Update the changelog wording to use the hyphenated compound adjective
“menu-related” in the existing sentence, without changing its meaning.

In `@crates/tauri-cli/templates/mobile/android/gradlew`:
- Around line 202-203: Update DEFAULT_JVM_OPTS in
crates/tauri-cli/templates/mobile/android/gradlew at lines 202-203 and
crates/tauri-cli/templates/mobile/android/gradlew.bat at lines 38-39 to include
-Dfile.encoding=UTF-8 alongside the existing memory options, preserving the
generated wrapper behavior in both scripts.

In `@crates/tauri-cli/templates/plugin/android/consumer-rules.pro`:
- Around line 1-3: Update the comments in both consumer-rules.pro
files—crates/tauri-cli/templates/plugin/android/consumer-rules.pro lines 1-3 and
examples/api/src-tauri/tauri-plugin-sample/android/consumer-rules.pro lines
1-3—to refer to consumerProguardFiles instead of proguardFiles, matching the
registration used by the Android plugin configuration.

In `@crates/tauri/mobile/android/consumer-rules.pro`:
- Around line 19-41: Update the Android consumer ProGuard rules alongside the
TauriPlugin, InvokeArg, JsonDeserialize, and JsonSerialize keep rules to
preserve RuntimeVisibleAnnotations and AnnotationDefault metadata. Add a
minified-release test covering reflective discovery of an annotated command and
a permission or activity callback, including TauriPlugin.permissions.

---

Nitpick comments:
In `@crates/tauri-cli/templates/mobile/android/app/build.gradle.kts`:
- Around line 57-60: Remove the duplicate compileOptions block from the Android
build configuration, keeping the existing JavaVersion.VERSION_1_8
sourceCompatibility and targetCompatibility block later in the file as the
single authoritative configuration.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: bdfb87e2-960a-4d1d-a1a9-38b6cbce594b

📥 Commits

Reviewing files that changed from the base of the PR and between d1b9940 and de5e25b.

⛔ Files ignored due to path filters (2)
  • Cargo.lock is excluded by !**/*.lock
  • crates/tauri-cli/templates/mobile/android/gradle/wrapper/gradle-wrapper.jar is excluded by !**/*.jar
📒 Files selected for processing (25)
  • .changes/android-consumer-proguard.md
  • .changes/android-gradle-9.md
  • .changes/menu-command-panic-on-wrong-input.md
  • .github/workflows/test-android.yml
  • crates/tauri-build/src/mobile.rs
  • crates/tauri-cli/templates/mobile/android/app/build.gradle.kts
  • crates/tauri-cli/templates/mobile/android/build.gradle.kts
  • crates/tauri-cli/templates/mobile/android/buildSrc/build.gradle.kts
  • crates/tauri-cli/templates/mobile/android/buildSrc/src/main/kotlin/BuildTask.kt
  • crates/tauri-cli/templates/mobile/android/buildSrc/src/main/kotlin/RustPlugin.kt
  • crates/tauri-cli/templates/mobile/android/gradle.properties
  • crates/tauri-cli/templates/mobile/android/gradle/wrapper/gradle-wrapper.properties
  • crates/tauri-cli/templates/mobile/android/gradlew
  • crates/tauri-cli/templates/mobile/android/gradlew.bat
  • crates/tauri-cli/templates/plugin/android/build.gradle.kts
  • crates/tauri-cli/templates/plugin/android/consumer-rules.pro
  • crates/tauri-macros/src/lib.rs
  • crates/tauri-macros/src/menu.rs
  • crates/tauri/mobile/android/build.gradle.kts
  • crates/tauri/mobile/android/consumer-rules.pro
  • crates/tauri/src/error.rs
  • crates/tauri/src/menu/plugin.rs
  • crates/tauri/src/tray/plugin.rs
  • examples/api/src-tauri/tauri-plugin-sample/android/build.gradle.kts
  • examples/api/src-tauri/tauri-plugin-sample/android/consumer-rules.pro
💤 Files with no reviewable changes (2)
  • examples/api/src-tauri/tauri-plugin-sample/android/build.gradle.kts
  • crates/tauri-cli/templates/plugin/android/build.gradle.kts

Comment thread .changes/menu-command-panic-on-wrong-input.md Outdated
Comment thread crates/tauri-cli/templates/mobile/android/gradlew Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 4

🧹 Nitpick comments (1)
crates/tauri-cli/templates/mobile/android/app/build.gradle.kts (1)

57-60: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove the duplicate compileOptions block.

The same Java 8 settings already exist at Line 64 through Line 67. Keep one block to prevent future configuration drift.

Proposed cleanup
-    compileOptions {
-        sourceCompatibility = JavaVersion.VERSION_1_8
-        targetCompatibility = JavaVersion.VERSION_1_8
-    }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/tauri-cli/templates/mobile/android/app/build.gradle.kts` around lines
57 - 60, Remove the duplicate compileOptions block from the Android build
configuration, keeping the existing JavaVersion.VERSION_1_8 sourceCompatibility
and targetCompatibility block later in the file as the single authoritative
configuration.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.changes/menu-command-panic-on-wrong-input.md:
- Line 6: Update the changelog wording to use the hyphenated compound adjective
“menu-related” in the existing sentence, without changing its meaning.

In `@crates/tauri-cli/templates/mobile/android/gradlew`:
- Around line 202-203: Update DEFAULT_JVM_OPTS in
crates/tauri-cli/templates/mobile/android/gradlew at lines 202-203 and
crates/tauri-cli/templates/mobile/android/gradlew.bat at lines 38-39 to include
-Dfile.encoding=UTF-8 alongside the existing memory options, preserving the
generated wrapper behavior in both scripts.

In `@crates/tauri-cli/templates/plugin/android/consumer-rules.pro`:
- Around line 1-3: Update the comments in both consumer-rules.pro
files—crates/tauri-cli/templates/plugin/android/consumer-rules.pro lines 1-3 and
examples/api/src-tauri/tauri-plugin-sample/android/consumer-rules.pro lines
1-3—to refer to consumerProguardFiles instead of proguardFiles, matching the
registration used by the Android plugin configuration.

In `@crates/tauri/mobile/android/consumer-rules.pro`:
- Around line 19-41: Update the Android consumer ProGuard rules alongside the
TauriPlugin, InvokeArg, JsonDeserialize, and JsonSerialize keep rules to
preserve RuntimeVisibleAnnotations and AnnotationDefault metadata. Add a
minified-release test covering reflective discovery of an annotated command and
a permission or activity callback, including TauriPlugin.permissions.

---

Nitpick comments:
In `@crates/tauri-cli/templates/mobile/android/app/build.gradle.kts`:
- Around line 57-60: Remove the duplicate compileOptions block from the Android
build configuration, keeping the existing JavaVersion.VERSION_1_8
sourceCompatibility and targetCompatibility block later in the file as the
single authoritative configuration.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: bdfb87e2-960a-4d1d-a1a9-38b6cbce594b

📥 Commits

Reviewing files that changed from the base of the PR and between d1b9940 and de5e25b.

⛔ Files ignored due to path filters (2)
  • Cargo.lock is excluded by !**/*.lock
  • crates/tauri-cli/templates/mobile/android/gradle/wrapper/gradle-wrapper.jar is excluded by !**/*.jar
📒 Files selected for processing (25)
  • .changes/android-consumer-proguard.md
  • .changes/android-gradle-9.md
  • .changes/menu-command-panic-on-wrong-input.md
  • .github/workflows/test-android.yml
  • crates/tauri-build/src/mobile.rs
  • crates/tauri-cli/templates/mobile/android/app/build.gradle.kts
  • crates/tauri-cli/templates/mobile/android/build.gradle.kts
  • crates/tauri-cli/templates/mobile/android/buildSrc/build.gradle.kts
  • crates/tauri-cli/templates/mobile/android/buildSrc/src/main/kotlin/BuildTask.kt
  • crates/tauri-cli/templates/mobile/android/buildSrc/src/main/kotlin/RustPlugin.kt
  • crates/tauri-cli/templates/mobile/android/gradle.properties
  • crates/tauri-cli/templates/mobile/android/gradle/wrapper/gradle-wrapper.properties
  • crates/tauri-cli/templates/mobile/android/gradlew
  • crates/tauri-cli/templates/mobile/android/gradlew.bat
  • crates/tauri-cli/templates/plugin/android/build.gradle.kts
  • crates/tauri-cli/templates/plugin/android/consumer-rules.pro
  • crates/tauri-macros/src/lib.rs
  • crates/tauri-macros/src/menu.rs
  • crates/tauri/mobile/android/build.gradle.kts
  • crates/tauri/mobile/android/consumer-rules.pro
  • crates/tauri/src/error.rs
  • crates/tauri/src/menu/plugin.rs
  • crates/tauri/src/tray/plugin.rs
  • examples/api/src-tauri/tauri-plugin-sample/android/build.gradle.kts
  • examples/api/src-tauri/tauri-plugin-sample/android/consumer-rules.pro
💤 Files with no reviewable changes (2)
  • examples/api/src-tauri/tauri-plugin-sample/android/build.gradle.kts
  • crates/tauri-cli/templates/plugin/android/build.gradle.kts
🛑 Comments failed to post (2)
crates/tauri-cli/templates/plugin/android/consumer-rules.pro (1)

1-3: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- candidate files ---'
git ls-files | grep -E '(^|/)(consumer-rules\.pro|build\.gradle|build\.gradle\.kts)$' | head -200

printf '%s\n' '--- template and example rule files ---'
for f in \
  crates/tauri-cli/templates/plugin/android/consumer-rules.pro \
  examples/api/src-tauri/tauri-plugin-sample/android/consumer-rules.pro
do
  if [ -f "$f" ]; then
    echo "### $f"
    cat -n "$f"
  else
    echo "MISSING: $f"
  fi
done

printf '%s\n' '--- Gradle property usage near these Android projects ---'
rg -n -C 3 'consumerProguardFiles|proguardFiles|consumer-rules\.pro|proguard-rules\.pro' \
  crates/tauri-cli/templates/plugin/android \
  examples/api/src-tauri/tauri-plugin-sample \
  2>/dev/null || true

Repository: OlympusLedgerOrg/tauri

Length of output: 5359


🌐 Web query:

Android Gradle Plugin consumerProguardFiles versus proguardFiles library module official documentation

💡 Result:

In Android library modules, proguardFiles and consumerProguardFiles serve distinct purposes during the build process [1][2]. proguardFiles These rules are used at build time to configure the shrinking, obfuscation, and optimization of the library itself [1][2]. They define how the library's code should be processed when the library module is built (e.g., keeping public APIs so they are not removed during shrinking) [1][2]. consumerProguardFiles These rules are packaged into the library's published artifact (AAR) [3][4]. They are intended to affect the build process of the application that consumes the library [1][2]. When an app developer uses your library, the rules specified in consumerProguardFiles are automatically consumed and applied during the app's own R8/ProGuard shrinking process [1][2]. This is essential for cases where the library uses reflection, JNI, or other dynamic features that the consumer's R8 configuration would not otherwise know how to handle [1][2]. Summary of Key Differences - Scope: proguardFiles affects the library's own build; consumerProguardFiles affects the consuming app's build [1][2]. - Purpose: proguardFiles is for library-internal optimization/protection; consumerProguardFiles is for providing necessary keep rules to the consumer to ensure the library functions correctly within the final app [1][2]. - Distribution: Only consumerProguardFiles are bundled into the published AAR for external use [3][4]. If your library uses reflection to access internal classes, you might need to include those rules in both proguardFiles (to protect them while building the library) and consumerProguardFiles (so the consumer's app build knows to keep them) [1][2].

Citations:


🏁 Script executed:

#!/bin/bash
set -e
python3 - <<'PY'
from pathlib import Path
import re

pairs = [
    (
        Path("crates/tauri-cli/templates/plugin/android/build.gradle.kts"),
        Path("crates/tauri-cli/templates/plugin/android/consumer-rules.pro"),
    ),
    (
        Path("examples/api/src-tauri/tauri-plugin-sample/android/build.gradle.kts"),
        Path("examples/api/src-tauri/tauri-plugin-sample/android/consumer-rules.pro"),
    ),
]

for gradle, rules in pairs:
    gradle_text = gradle.read_text()
    rules_text = rules.read_text()
    bound = re.findall(r'consumerProguardFiles\("([^"]+)"\)', gradle_text)
    print(f"{rules}:")
    print(f"  consumerProguardFiles bindings: {bound}")
    print(f"  basename bound: {rules.name in bound}")
    print(f"  comment uses consumerProguardFiles: {'consumerProguardFiles' in rules_text}")
    print(f"  comment uses proguardFiles: {bool(re.search(r'\\bproguardFiles\\b', rules_text))}")
PY

Repository: OlympusLedgerOrg/tauri

Length of output: 447


🏁 Script executed:

#!/bin/bash
set -e
python3 - <<'PY'
from pathlib import Path
import re

pairs = [
    (
        Path("crates/tauri-cli/templates/plugin/android/build.gradle.kts"),
        Path("crates/tauri-cli/templates/plugin/android/consumer-rules.pro"),
    ),
    (
        Path("examples/api/src-tauri/tauri-plugin-sample/android/build.gradle.kts"),
        Path("examples/api/src-tauri/tauri-plugin-sample/android/consumer-rules.pro"),
    ),
]

for gradle, rules in pairs:
    gradle_text = gradle.read_text()
    rules_text = rules.read_text()
    bound = re.findall(r'consumerProguardFiles\("([^"]+)"\)', gradle_text)
    uses_consumer = "consumerProguardFiles" in rules_text
    uses_library = bool(re.search(r"\bproguardFiles\b", rules_text))
    print(f"{rules}:")
    print(f"  consumerProguardFiles bindings: {bound}")
    print(f"  basename bound: {rules.name in bound}")
    print(f"  comment uses consumerProguardFiles: {uses_consumer}")
    print(f"  comment uses proguardFiles: {uses_library}")
PY

Repository: OlympusLedgerOrg/tauri

Length of output: 611


Replace proguardFiles with consumerProguardFiles in both comments.

Both files are registered with consumerProguardFiles("consumer-rules.pro"). The current guidance names the library-only proguardFiles property and can cause incorrect configuration.

📍 Affects 2 files
  • crates/tauri-cli/templates/plugin/android/consumer-rules.pro#L1-L3 (this comment)
  • examples/api/src-tauri/tauri-plugin-sample/android/consumer-rules.pro#L1-L3
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/tauri-cli/templates/plugin/android/consumer-rules.pro` around lines 1
- 3, Update the comments in both consumer-rules.pro
files—crates/tauri-cli/templates/plugin/android/consumer-rules.pro lines 1-3 and
examples/api/src-tauri/tauri-plugin-sample/android/consumer-rules.pro lines
1-3—to refer to consumerProguardFiles instead of proguardFiles, matching the
registration used by the Android plugin configuration.

Source: MCP tools

crates/tauri/mobile/android/consumer-rules.pro (1)

19-41: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '== candidate files =='
fd -i 'consumer-rules\.pro|PluginHandle|TauriPlugin|Command|ActivityCallback|PermissionCallback|InvokeArg' .

printf '%s\n' '== annotation and reflection references =='
rg -n -S 'class (TauriPlugin|Command|ActivityCallback|PermissionCallback|Permission|InvokeArg)|`@Retention`|RuntimeVisibleAnnotations|PluginHandle|getAnnotation|isAnnotationPresent|TauriPlugin|ActivityCallback|PermissionCallback' \
  crates android . 2>/dev/null | head -n 500

printf '%s\n' '== consumer rules =='
RULE=$(fd -i -t f 'consumer-rules\.pro' | head -n 1 || true)
if [ -n "$RULE" ]; then
  printf 'FILE: %s\n' "$RULE"
  cat -n "$RULE"
fi

Repository: OlympusLedgerOrg/tauri

Length of output: 33545


🏁 Script executed:

#!/bin/bash
set -eu

for f in \
  crates/tauri/mobile/android/src/main/java/app/tauri/annotation/TauriPlugin.kt \
  crates/tauri/mobile/android/src/main/java/app/tauri/annotation/PluginMethod.kt \
  crates/tauri/mobile/android/src/main/java/app/tauri/annotation/ActivityCallback.kt \
  crates/tauri/mobile/android/src/main/java/app/tauri/annotation/PermissionCallback.kt \
  crates/tauri/mobile/android/src/main/java/app/tauri/annotation/Permission.kt \
  crates/tauri/mobile/android/src/main/java/app/tauri/annotation/InvokeArg.kt \
  crates/tauri/mobile/android/src/main/java/app/tauri/plugin/PluginHandle.kt \
  crates/tauri/mobile/android/src/main/java/app/tauri/plugin/PluginManager.kt \
  crates/tauri/mobile/android/src/main/java/app/tauri/plugin/Plugin.kt
do
  printf '\n== %s ==\n' "$f"
  wc -l "$f"
  cat -n "$f"
done

printf '\n== all R8/ProGuard attribute directives and Android build configuration ==\n'
rg -n -S --glob '*.pro' --glob '*.gradle' --glob '*.gradle.kts' --glob '*.xml' \
  'keepattributes|consumerProguardFiles|minifyEnabled|isMinifyEnabled|proguardFiles|shrinkResources' .

Repository: OlympusLedgerOrg/tauri

Length of output: 43476


🌐 Web query:

site:developer.android.com/topic/performance/app-optimization/library-optimization keepattributes RuntimeVisibleAnnotations AnnotationDefault consumer ProGuard rules runtime annotations

💡 Result:

To preserve runtime annotations in ProGuard or R8, you must use the -keepattributes directive [1]. This ensures that the R8 compiler does not strip critical metadata required for reflection or other runtime operations [1]. If your library or application relies on runtime annotations, you should include the following line in your ProGuard rules file: -keepattributes RuntimeVisibleAnnotations,AnnotationDefault The RuntimeVisibleAnnotations attribute is necessary to keep annotations that are accessible via reflection at runtime [1]. The AnnotationDefault attribute is required to preserve the default values of annotation members [1]. For library authors, it is recommended to keep RuntimeVisibleAnnotations in your consumer keep rules if your library uses annotations that must be accessed by the application consuming your library [1]. Other attributes such as Signature, InnerClasses, and various RuntimeInvisible or Parameter annotations should be declared in your library's build keep rules rather than the consumer rules if they are required for the library's internal functionality [1].

Citations:


🌐 Web query:

site:guardsquare.com/manual/configuration/attributes RuntimeVisibleAnnotations AnnotationDefault ProGuard reflection annotations keepattributes

💡 Result:

ProGuard removes attributes like RuntimeVisibleAnnotations and AnnotationDefault by default to reduce the size of the processed code [1]. Because these attributes are necessary for code that uses reflection to access annotations at runtime, you must explicitly instruct ProGuard to retain them using the -keepattributes configuration option [1]. To ensure that annotations and their default values are preserved, you should include the following rule in your ProGuard configuration file: -keepattributes RuntimeVisibleAnnotations,AnnotationDefault When these attributes are kept, reflection-based code can successfully access class, field, and method annotations, as well as their defined default values, after the ProGuard optimization process [1]. Without this configuration, the necessary metadata is discarded, which may cause runtime errors or unexpected behavior in applications that rely on annotation processing via reflection [1].

Citations:


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '== plugin consumer-rule templates =='
for f in $(fd -i -t f 'consumer-rules\.pro' crates/tauri-cli/templates examples/api/src-tauri); do
  printf '\n-- %s --\n' "$f"
  cat -n "$f"
done

printf '%s\n' '== Android library configuration =='
cat -n crates/tauri/mobile/android/build.gradle.kts
cat -n crates/tauri-cli/templates/plugin/android/build.gradle.kts
cat -n crates/tauri-cli/templates/mobile/android/app/build.gradle.kts

printf '%s\n' '== annotations and reflection-related bytecode-sensitive constructs =='
rg -n -S --glob '*.kt' --glob '*.java' \
  'Retention|Target|annotation class|declaredMethods|getAnnotation|isAnnotationPresent|permissions|strings|alias' \
  crates/tauri/mobile/android/src/main/java crates/tauri-cli/templates/plugin/android/src/main/java examples/api/src-tauri/tauri-plugin-sample/android/src/main/java

Repository: OlympusLedgerOrg/tauri

Length of output: 26794


🏁 Script executed:

#!/bin/bash
set -eu

python3 - <<'PY'
from pathlib import Path
import re

root = Path(".")
rules = (root / "crates/tauri/mobile/android/consumer-rules.pro").read_text()
handle = (root / "crates/tauri/mobile/android/src/main/java/app/tauri/plugin/PluginHandle.kt").read_text()
plugin = (root / "crates/tauri/mobile/android/src/main/java/app/tauri/plugin/Plugin.kt").read_text()

annotations = {
    "TauriPlugin": root / "crates/tauri/mobile/android/src/main/java/app/tauri/annotation/TauriPlugin.kt",
    "Command": root / "crates/tauri/mobile/android/src/main/java/app/tauri/annotation/PluginMethod.kt",
    "ActivityCallback": root / "crates/tauri/mobile/android/src/main/java/app/tauri/annotation/ActivityCallback.kt",
    "PermissionCallback": root / "crates/tauri/mobile/android/src/main/java/app/tauri/annotation/PermissionCallback.kt",
    "Permission": root / "crates/tauri/mobile/android/src/main/java/app/tauri/annotation/Permission.kt",
    "InvokeArg": root / "crates/tauri/mobile/android/src/main/java/app/tauri/annotation/InvokeArg.kt",
}

runtime = []
for name, path in annotations.items():
    text = path.read_text()
    if "AnnotationRetention.RUNTIME" in text:
        runtime.append(name)

reflection_uses = {
    "TauriPlugin": "getAnnotation(TauriPlugin::class.java)",
    "Command": "isAnnotationPresent(Command::class.java)",
    "ActivityCallback": "isAnnotationPresent(ActivityCallback::class.java)",
    "PermissionCallback": "isAnnotationPresent(PermissionCallback::class.java)",
}
found_reflection = {
    name: expr in handle for name, expr in reflection_uses.items()
}
permission_values_used = all(
    token in plugin for token in ("annotation.permissions", "perm.strings", "perm.alias")
)

print("runtime_retained:", ",".join(runtime))
print("reflection_uses:", found_reflection)
print("nested_permission_values_used:", permission_values_used)
print("consumer_has_runtime_annotation_attributes:",
      bool(re.search(r"(?m)^\s*-keepattributes\s+.*RuntimeVisibleAnnotations", rules)))
print("consumer_has_annotation_defaults:",
      bool(re.search(r"(?m)^\s*-keepattributes\s+.*AnnotationDefault", rules)))

assert set(runtime) == set(annotations)
assert all(found_reflection.values())
assert permission_values_used
assert not re.search(r"(?m)^\s*-keepattributes\s+.*RuntimeVisibleAnnotations", rules)
assert not re.search(r"(?m)^\s*-keepattributes\s+.*AnnotationDefault", rules)
PY

Repository: OlympusLedgerOrg/tauri

Length of output: 491


Preserve runtime annotation metadata.

Add -keepattributes RuntimeVisibleAnnotations,AnnotationDefault. R8 otherwise can remove the metadata required for reflective plugin discovery, command dispatch, callbacks, and TauriPlugin.permissions. Add a minified-release test for an annotated command and permission or activity callback.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/tauri/mobile/android/consumer-rules.pro` around lines 19 - 41, Update
the Android consumer ProGuard rules alongside the TauriPlugin, InvokeArg,
JsonDeserialize, and JsonSerialize keep rules to preserve
RuntimeVisibleAnnotations and AnnotationDefault metadata. Add a minified-release
test covering reflective discovery of an annotated command and a permission or
activity callback, including TauriPlugin.permissions.

Source: MCP tools

…sumer rules

Add `-keepattributes RuntimeVisibleAnnotations,AnnotationDefault` to
crates/tauri/mobile/android/consumer-rules.pro (CodeRabbit finding on this
PR, functional-correctness/major). Reflective plugin discovery in
PluginHandle (TauriPlugin.permissions, @Command/@ActivityCallback/
@PermissionCallback method lookup via getAnnotation()/isAnnotationPresent())
depends on these attributes surviving R8; the existing -keep rules protect
the classes and methods themselves but not the annotation metadata attached
to them, so a minified release build could silently break plugin dispatch
without this.

Add examples/android-r8-plugin-discovery: a standalone Gradle project that
builds a real isMinifyEnabled=true "release" app depending only on the
shipped consumer-rules.pro (via project(":tauri-android") pointing straight
at crates/tauri/mobile/android, the same way generate_gradle_files wires a
real app) and asserts, in an instrumented test running against that minified
build, that a sample @TauriPlugin's annotation, permissions, and
@Command/@ActivityCallback/@PermissionCallback methods are still reflectively
discoverable and invokable -- mirroring PluginHandle's exact reflection
calls. Wired into CI via a new emulator-backed workflow.

Note: this was authored and cross-checked against PluginHandle's actual
reflection code, but not locally build-verified -- no Android SDK/Gradle/JDK
17+ was available in the environment it was written in. See
examples/android-r8-plugin-discovery/README.md.
@OlympusLedgerOrg

Copy link
Copy Markdown
Owner Author

@coderabbitai Addressed the "Preserve runtime annotation metadata" finding on crates/tauri/mobile/android/consumer-rules.pro (functional-correctness/major) in 90042ee:

  • Added -keepattributes RuntimeVisibleAnnotations,AnnotationDefault to consumer-rules.pro, as suggested.
  • Added examples/android-r8-plugin-discovery, a standalone Gradle project that builds a real isMinifyEnabled = true release app depending only on the shipped consumer rules, and an instrumented test (ReflectivePluginDiscoveryTest) that mirrors PluginHandle's actual reflection calls to assert @TauriPlugin/permissions/@Command/@ActivityCallback/@PermissionCallback discovery and invocation survive minification — including a case that exercises the AnnotationDefault default-value path specifically, not just RuntimeVisibleAnnotations presence.
  • Wired into CI via .github/workflows/test-android-r8-plugin-discovery.yml (emulator-backed, since a JVM unit test would never see R8 output).

Note: this was authored without local Android/Gradle tooling available, so the CI run on this PR is the first real execution of the new module — see examples/android-r8-plugin-discovery/README.md for details. @coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== Commit summary =='
git show --stat --oneline --summary 90042ee

echo
echo '== Relevant files =='
fd -HI -t f . \
  .github/workflows \
  crates/tauri/mobile/android \
  examples/android-r8-plugin-discovery 2>/dev/null \
  | sort

echo
echo '== Consumer rules =='
cat -n crates/tauri/mobile/android/consumer-rules.pro

echo
echo '== Workflow =='
cat -n .github/workflows/test-android-r8-plugin-discovery.yml

echo
echo '== Module files and reflection-related references =='
rg -n -i -C 3 \
  'consumer-rules|isMinifyEnabled|ReflectivePluginDiscoveryTest|TauriPlugin|ActivityCallback|PermissionCallback|AnnotationDefault|RuntimeVisibleAnnotations|PluginHandle|proguard' \
  examples/android-r8-plugin-discovery \
  crates/tauri/mobile/android \
  .github/workflows/test-android-r8-plugin-discovery.yml

echo
echo '== PluginHandle reflection implementation =='
rg -n -i -C 5 \
  'getAnnotation|TauriPlugin|ActivityCallback|PermissionCallback|Command|permissions' \
  crates/tauri/src

Length of output: 163687


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== Files changed by 90042ee =='
git diff-tree --no-commit-id --name-status -r 90042ee

echo
echo '== Exact diff for consumer rules and workflow =='
git show --format= -- \
  crates/tauri/mobile/android/consumer-rules.pro \
  .github/workflows/test-android-r8-plugin-discovery.yml \
  examples/android-r8-plugin-discovery

echo
echo '== New module text files =='
fd -HI -t f -e kts -e kotlin -e java -e xml -e pro -e yml -e yaml -e md \
  . examples/android-r8-plugin-discovery \
  -x sh -c 'echo "--- $1"; cat -n "$1"' sh {}

echo
echo '== Android PluginHandle locations =='
rg -n -l -i 'class\s+PluginHandle|PluginHandle' crates \
  | while IFS= read -r file; do
      echo "--- $file"
      rg -n -i -C 12 'class\s+PluginHandle|getAnnotation|TauriPlugin|ActivityCallback|PermissionCallback|Command|permissions' "$file"
    done

Length of output: 227880


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== Android PluginHandle source =='
plugin_handle=$(fd -HI -t f 'PluginHandle\.(java|kt)$' crates/tauri/mobile/android/src/main || true)
if [ -z "$plugin_handle" ]; then
  echo 'PluginHandle source was not found.'
  exit 1
fi
printf '%s\n' "$plugin_handle"
while IFS= read -r file; do
  echo "--- $file"
  rg -n -C 14 \
    'getAnnotation|isAnnotationPresent|declaredMethods|TauriPlugin|ActivityCallback|PermissionCallback|Command|indexMethods|invoke\(' \
    "$file"
done <<< "$plugin_handle"

echo
echo '== Complete shipped consumer rules =='
cat -n crates/tauri/mobile/android/consumer-rules.pro

echo
echo '== Annotation retention declarations =='
annotation_files=$(fd -HI -t f -e java -e kt . crates/tauri/mobile/android/src/main/java/app/tauri/annotation)
if [ -z "$annotation_files" ]; then
  echo 'Annotation sources were not found.'
  exit 1
fi
while IFS= read -r file; do
  echo "--- $file"
  rg -n -C 4 '`@Retention`|RUNTIME|interface\s+(TauriPlugin|Command|ActivityCallback|PermissionCallback|Permission)' "$file"
done <<< "$annotation_files"

Length of output: 9149


@OlympusLedgerOrg The finding is addressed.

consumer-rules.pro now retains both attributes required by the production reflection path. PluginHandle uses getAnnotation and isAnnotationPresent for the tested annotations.

The new instrumented test uses the real :tauri-android module. It runs against the R8-minified release app. It verifies explicit annotation values, AnnotationDefault values, and reflective callback discovery and invocation.

The new workflow runs :app:connectedReleaseAndroidTest on an emulator for relevant changes. I could not execute Android tooling in this environment. The CI result remains the runtime verification.


If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (3)
examples/android-r8-plugin-discovery/app/src/main/java/com/example/r8plugindiscovery/plugin/DiscoveryTestPlugin.kt (1)

18-24: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Correct the claim about constant survival.

const val values are inlined into every call site at compile time. The androidTest module compiles the literal strings directly. R8 renaming or removing DiscoveryTestPluginKt cannot make these assertions fail. The constants only guarantee that the plugin methods produce the expected payloads. Update the comment so it does not suggest coverage that does not exist.

📝 Proposed comment change
-// Markers the androidTest asserts on. If these constants disappeared from the
-// resolved output (renamed, inlined wrong, etc.) the test would fail loudly
-// rather than silently pass on a no-op.
+// Marker payloads the androidTest asserts on, so a reflective invoke that
+// silently does nothing cannot pass. These are `const val`, so both sides
+// inline the literal; they do not themselves prove anything about R8 renaming.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@examples/android-r8-plugin-discovery/app/src/main/java/com/example/r8plugindiscovery/plugin/DiscoveryTestPlugin.kt`
around lines 18 - 24, Update the comment above PING_RESULT,
ACTIVITY_CALLBACK_RESULT, PERMISSION_CALLBACK_RESULT, and LOCATION_PERMISSION to
state that these constants define expected payloads produced by the plugin
methods, not markers that verify constant survival or DiscoveryTestPluginKt
retention after R8 processing.
examples/android-r8-plugin-discovery/gradle/wrapper/gradle-wrapper.properties (1)

3-3: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Consider pinning the distribution checksum.

Add distributionSha256Sum for the Gradle 9.6.1 distribution. CI downloads this archive on every run. A pinned checksum makes the download verifiable. Add validateDistributionUrl=true and networkTimeout as well if the other wrapper files in this repository use them.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@examples/android-r8-plugin-discovery/gradle/wrapper/gradle-wrapper.properties`
at line 3, Update the Gradle wrapper properties around distributionUrl to add
the official SHA-256 checksum for the Gradle 9.6.1 binary distribution, ensuring
downloads are verified. Also add validateDistributionUrl=true and networkTimeout
using the same values and conventions as other repository Gradle wrapper files
if they are present there.
examples/android-r8-plugin-discovery/app/src/androidTest/java/com/example/r8plugindiscovery/ReflectivePluginDiscoveryTest.kt (1)

52-77: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Close ActivityScenario on the failure path.

scenario.close() runs only when the block completes. An assertion inside onActivity propagates out of the test method and skips close(). The activity then stays alive and can affect the later tests in this class. ActivityScenario implements AutoCloseable, so use use.

♻️ Proposed change (apply the same pattern to all five tests)
-        val scenario = ActivityScenario.launch(MainActivity::class.java)
-        scenario.onActivity { activity ->
-            ...
-        }
-        scenario.close()
+        ActivityScenario.launch(MainActivity::class.java).use { scenario ->
+            scenario.onActivity { activity ->
+                ...
+            }
+        }

Also applies to: 79-99, 101-113, 115-127, 129-148

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@examples/android-r8-plugin-discovery/app/src/androidTest/java/com/example/r8plugindiscovery/ReflectivePluginDiscoveryTest.kt`
around lines 52 - 77, Update all five test methods in
ReflectivePluginDiscoveryTest, including
tauriPluginAnnotationAndPermissionsSurviveMinification, to wrap each
ActivityScenario.launch(MainActivity::class.java) result in Kotlin’s use scope.
Move the existing onActivity assertions inside that scope and remove the
separate scenario.close() calls so the scenario closes when assertions fail or
the block completes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.changes/android-consumer-proguard-keepattributes.md:
- Around line 1-3: Fix the change-tag front matter in the change file so it uses
the repository’s accepted syntax and is recognized for package tauri;
alternatively, place the tag in the expected change file format. Verify the
correction by rerunning .scripts/ci/check-change-tags.js.

In `@examples/android-r8-plugin-discovery/app/src/main/AndroidManifest.xml`:
- Around line 6-16: Set an AppCompat-derived android:theme on the application or
the MainActivity declaration in the AndroidManifest.xml. Use an existing
Theme.AppCompat descendant from the project’s resources and preserve the current
activity configuration.

In `@examples/android-r8-plugin-discovery/build.gradle.kts`:
- Around line 25-27: Update the clean task registration to explicitly use
Gradle’s Delete task type, so delete("build") is executed only when the task
runs rather than during configuration. Preserve the existing task name and
build-directory deletion behavior.

In `@examples/android-r8-plugin-discovery/gradlew`:
- Line 1: Update the tracked file mode for the gradlew script from 100644 to
100755 so the workflow can execute ./gradlew successfully. Do not modify the
script contents.

In `@examples/android-r8-plugin-discovery/settings.gradle.kts`:
- Around line 20-26: Remove the project-level repository block from
examples/android-r8-plugin-discovery/build.gradle.kts lines 18-23, while
preserving its buildscript block. Keep the settings-level repositories
configuration in examples/android-r8-plugin-discovery/settings.gradle.kts lines
20-26 unchanged.

---

Nitpick comments:
In
`@examples/android-r8-plugin-discovery/app/src/androidTest/java/com/example/r8plugindiscovery/ReflectivePluginDiscoveryTest.kt`:
- Around line 52-77: Update all five test methods in
ReflectivePluginDiscoveryTest, including
tauriPluginAnnotationAndPermissionsSurviveMinification, to wrap each
ActivityScenario.launch(MainActivity::class.java) result in Kotlin’s use scope.
Move the existing onActivity assertions inside that scope and remove the
separate scenario.close() calls so the scenario closes when assertions fail or
the block completes.

In
`@examples/android-r8-plugin-discovery/app/src/main/java/com/example/r8plugindiscovery/plugin/DiscoveryTestPlugin.kt`:
- Around line 18-24: Update the comment above PING_RESULT,
ACTIVITY_CALLBACK_RESULT, PERMISSION_CALLBACK_RESULT, and LOCATION_PERMISSION to
state that these constants define expected payloads produced by the plugin
methods, not markers that verify constant survival or DiscoveryTestPluginKt
retention after R8 processing.

In
`@examples/android-r8-plugin-discovery/gradle/wrapper/gradle-wrapper.properties`:
- Line 3: Update the Gradle wrapper properties around distributionUrl to add the
official SHA-256 checksum for the Gradle 9.6.1 binary distribution, ensuring
downloads are verified. Also add validateDistributionUrl=true and networkTimeout
using the same values and conventions as other repository Gradle wrapper files
if they are present there.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 073e5c9a-90ff-4584-85f9-d7b647b24b80

📥 Commits

Reviewing files that changed from the base of the PR and between de5e25b and 90042ee.

⛔ Files ignored due to path filters (1)
  • examples/android-r8-plugin-discovery/gradle/wrapper/gradle-wrapper.jar is excluded by !**/*.jar
📒 Files selected for processing (17)
  • .changes/android-consumer-proguard-keepattributes.md
  • .github/workflows/test-android-r8-plugin-discovery.yml
  • crates/tauri/mobile/android/consumer-rules.pro
  • examples/android-r8-plugin-discovery/.gitignore
  • examples/android-r8-plugin-discovery/README.md
  • examples/android-r8-plugin-discovery/app/build.gradle.kts
  • examples/android-r8-plugin-discovery/app/proguard-rules.pro
  • examples/android-r8-plugin-discovery/app/src/androidTest/java/com/example/r8plugindiscovery/ReflectivePluginDiscoveryTest.kt
  • examples/android-r8-plugin-discovery/app/src/main/AndroidManifest.xml
  • examples/android-r8-plugin-discovery/app/src/main/java/com/example/r8plugindiscovery/MainActivity.kt
  • examples/android-r8-plugin-discovery/app/src/main/java/com/example/r8plugindiscovery/plugin/DiscoveryTestPlugin.kt
  • examples/android-r8-plugin-discovery/build.gradle.kts
  • examples/android-r8-plugin-discovery/gradle.properties
  • examples/android-r8-plugin-discovery/gradle/wrapper/gradle-wrapper.properties
  • examples/android-r8-plugin-discovery/gradlew
  • examples/android-r8-plugin-discovery/gradlew.bat
  • examples/android-r8-plugin-discovery/settings.gradle.kts
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/tauri/mobile/android/consumer-rules.pro

Comment thread .changes/android-consumer-proguard-keepattributes.md
Comment thread examples/android-r8-plugin-discovery/build.gradle.kts Outdated
Comment thread examples/android-r8-plugin-discovery/gradlew
Comment thread examples/android-r8-plugin-discovery/settings.gradle.kts
OlympusLedgerOrg and others added 2 commits August 11, 2026 15:12
… repositories block

- AndroidManifest.xml had no android:theme though MainActivity extends
  AppCompatActivity -- activity startup would crash before the test ran
- tasks.register("clean").configure { delete("build") } ran delete() at
  Gradle configuration time, not task execution time
- allprojects { repositories {...} } conflicts with settings.gradle.kts's
  RepositoriesMode.FAIL_ON_PROJECT_REPOS and fails configuration under
  Gradle 9; settings-level repositories already cover this
@OlympusLedgerOrg

Copy link
Copy Markdown
Owner Author

CodeRabbit Fixes Applied

Fixed 9 file(s) across 3 commits based on CodeRabbit feedback on this PR.

Round 1 (sync-related findings, df64d7d, f53eed2):

  • .changes/menu-command-panic-on-wrong-input.md — "menu related" → "menu-related"
  • crates/tauri-cli/templates/mobile/android/gradlew, gradlew.bat — restored -Dfile.encoding=UTF-8 in DEFAULT_JVM_OPTS
  • crates/tauri-cli/templates/plugin/android/consumer-rules.pro, examples/api/src-tauri/tauri-plugin-sample/android/consumer-rules.pro — comment now says consumerProguardFiles (matches actual registration)
  • crates/tauri-cli/templates/mobile/android/app/build.gradle.kts — removed duplicate compileOptions block
  • .changes/android-consumer-proguard-keepattributes.md — fixed missing change-tag (tauri: patch:bug)
  • examples/android-r8-plugin-discovery/gradlew — restored executable bit (100755), fixing the CI Permission denied failure

Round 2 (new example project findings, 89b0323):

  • app/src/main/AndroidManifest.xml — added android:theme (MainActivity extends AppCompatActivity but had no AppCompat theme set — would crash on launch)
  • build.gradle.ktsclean task now correctly typed as Delete (was running delete("build") at Gradle configuration time, not task execution time) and removed the allprojects { repositories {...} } block, which conflicted with settings.gradle.kts's RepositoriesMode.FAIL_ON_PROJECT_REPOS and would fail Gradle configuration under Gradle 9

All findings independently verified against current code before fixing; nothing applied blind. Resolving all threads now.

- README.md failed pnpm format:check (prettier)
- gradle.properties was missing android.builtInKotlin=false /
  android.newDsl=false, causing 'Cannot add extension with name kotlin,
  as there is an extension already registered with that name' when
  building :app -- AGP 9.3.1's built-in Kotlin support collides with the
  explicit org.jetbrains.kotlin.android plugin unless disabled, same as
  crates/tauri-cli/templates/mobile/android/gradle.properties already
  does for the CLI-generated template
R8 failed with 'Missing class com.google.errorprone.annotations.MustBeClosed
(referenced from androidx.test.platform.tracing.Tracer$Span...)'. This is a
compile-only annotation referenced by androidx.test's own tracing code, on
the androidTest classpath, unrelated to Tauri plugin discovery -- safe to
silence and does not touch anything the test is actually exercising.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.changes/menu-command-panic-on-wrong-input.md:
- Line 6: Revise the changelog sentence to clearly state that menu-related
commands no longer panic when invoked directly with invalid menu types, using
grammatically complete wording.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: f79a86b1-fb99-4dab-b019-31046d4c36f3

📥 Commits

Reviewing files that changed from the base of the PR and between 90042ee and 69d03df.

📒 Files selected for processing (13)
  • .changes/android-consumer-proguard-keepattributes.md
  • .changes/menu-command-panic-on-wrong-input.md
  • crates/tauri-cli/templates/mobile/android/app/build.gradle.kts
  • crates/tauri-cli/templates/mobile/android/gradlew
  • crates/tauri-cli/templates/mobile/android/gradlew.bat
  • crates/tauri-cli/templates/plugin/android/consumer-rules.pro
  • examples/android-r8-plugin-discovery/README.md
  • examples/android-r8-plugin-discovery/app/proguard-rules.pro
  • examples/android-r8-plugin-discovery/app/src/main/AndroidManifest.xml
  • examples/android-r8-plugin-discovery/build.gradle.kts
  • examples/android-r8-plugin-discovery/gradle.properties
  • examples/android-r8-plugin-discovery/gradlew
  • examples/api/src-tauri/tauri-plugin-sample/android/consumer-rules.pro
💤 Files with no reviewable changes (2)
  • crates/tauri-cli/templates/mobile/android/app/build.gradle.kts
  • examples/android-r8-plugin-discovery/gradlew
🚧 Files skipped from review as they are similar to previous changes (9)
  • examples/api/src-tauri/tauri-plugin-sample/android/consumer-rules.pro
  • crates/tauri-cli/templates/plugin/android/consumer-rules.pro
  • examples/android-r8-plugin-discovery/app/src/main/AndroidManifest.xml
  • .changes/android-consumer-proguard-keepattributes.md
  • examples/android-r8-plugin-discovery/gradle.properties
  • examples/android-r8-plugin-discovery/README.md
  • examples/android-r8-plugin-discovery/build.gradle.kts
  • crates/tauri-cli/templates/mobile/android/gradlew
  • crates/tauri-cli/templates/mobile/android/gradlew.bat

Comment thread .changes/menu-command-panic-on-wrong-input.md
OlympusLedgerOrg and others added 7 commits August 11, 2026 16:16
The previous dontwarn fix only applied to the app APK's own proguardFiles.
The androidTest APK (also minified, since testBuildType = "release") runs
its own separate R8 pass and was still hitting the same missing-class
error because it wasn't told about the same -dontwarn rule.
The connectedReleaseAndroidTest step has no time bound, so a hung
emulator/instrumentation run (observed: 2.5h with zero log output
before manual cancellation) would otherwise run until GitHub's
360-minute default job timeout instead of failing fast.
connectedAndroidTest prints nothing to the Gradle console between task
start and completion by default, so the earlier hang (23:27:49 to the
15-minute timeout, zero output) is indistinguishable from a slow run
in the CI logs. Add --info to surface adb install/instrumentation
progress, capture logcat in the background, and upload both plus the
androidTest report/results as an artifact via if: always() so the
next hang leaves evidence instead of silence.
Artifact downloads route through Azure Blob Storage, which this CI
log tooling can't always reach. Print the filtered logcat and any
androidTest XML results directly into the job's own log output (via
if: always()) so they're readable without a separate artifact fetch.
… APK

Root cause of the CI hang: R8 strips androidx.tracing.Trace from the
minified androidTest APK since nothing in this app references it
directly. AndroidJUnitRunner.onCreate() calls into it unconditionally,
so the instrumented process crashed with NoClassDefFoundError before
the runner could attach -- confirmed via logcat captured by the
previous diagnostics commit:

  FATAL EXCEPTION: main
  Process: com.example.r8plugindiscovery, PID: 6628
  java.lang.NoClassDefFoundError: Failed resolution of: Landroidx/tracing/Trace;
  	at androidx.test.runner.AndroidJUnitRunner.onCreate(...)
  Caused by: java.lang.ClassNotFoundException: androidx.tracing.Trace

`adb shell am instrument -w` doesn't reliably report
INSTRUMENTATION_FAILED when the runner crashes this early, which is
why the job hung for 2.5h instead of failing fast with a test report.
This keep rule is unrelated to Tauri plugin discovery (the thing this
module actually tests), same category as the existing errorprone
-dontwarn line.
… APK

Same category of bug as the androidx.tracing.Trace fix, one step
further into AndroidJUnitRunner startup. With tracing.Trace kept, the
instrumentation process now crashes cleanly and fast (reported
promptly as "Process crashed" instead of hanging) at the next missing
class:

  INSTRUMENTATION_RESULT: shortMsg=Process crashed.
  java.lang.NoClassDefFoundError: Failed resolution of: Lkotlin/jvm/internal/Lambda;
  	at androidx.test.platform.io.FileTestStorage.<init>(...)
  	at androidx.test.runner.AndroidJUnitRunner.registerTestStorage(...)
  	at androidx.test.runner.AndroidJUnitRunner.onStart(...)
  Caused by: java.lang.ClassNotFoundException: kotlin.jvm.internal.Lambda

Keeping the whole kotlin.jvm.internal package rather than just Lambda
to also cover the FunctionN interfaces compiled lambdas implement,
which R8 would otherwise strip next for the same reason.
…t APK

Third missing-class crash in the same pattern: after keeping
kotlin.jvm.internal.Lambda, OutputDirCalculator.<init> (also called
from AndroidJUnitRunner.onStart -> FileTestStorage.<init>) crashed on
kotlin.LazyKt (the `by lazy {}` support class) instead:

  java.lang.NoClassDefFoundError: Failed resolution of: Lkotlin/LazyKt;
  	at androidx.test.platform.io.OutputDirCalculator.<init>(...)
  	at androidx.test.platform.io.FileTestStorage.<init>(...)
  Caused by: java.lang.ClassNotFoundException: kotlin.LazyKt

androidx.test's Kotlin-compiled IO helpers reference enough of the
stdlib surface that chasing individual classes costs a full CI
round-trip each time. Widen the keep rule from kotlin.jvm.internal.**
to all of kotlin.** to stop the whack-a-mole in one shot.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
.github/workflows/test-android-r8-plugin-discovery.yml (1)

16-31: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Restrict the workflow token to read-only contents access.

This workflow runs pull-request-controlled Gradle code and third-party actions. Add permissions: contents: read at workflow or job scope to prevent broader repository defaults from granting unnecessary access.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/test-android-r8-plugin-discovery.yml around lines 16 - 31,
Add a workflow-level permissions declaration to the “test android r8 plugin
discovery” workflow granting only contents read access. Keep the existing
triggers and concurrency configuration unchanged, ensuring
pull-request-controlled Gradle code and actions do not receive broader
repository permissions.

Source: Learnings

🧹 Nitpick comments (1)
.github/workflows/test-android-r8-plugin-discovery.yml (1)

77-80: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Close the background adb logcat process before the step exits.

The script starts adb logcat asynchronously and never waits for it. The diagnostics and artifact steps can read a partially written file, or the process can survive emulator teardown.

Track the process ID and clean it up with a trap:

Proposed logcat cleanup
           "$ANDROID_HOME/platform-tools/adb" logcat -c
+          logcat_pid=""
+          cleanup() {
+            if [ -n "$logcat_pid" ]; then
+              kill "$logcat_pid" 2>/dev/null || true
+              wait "$logcat_pid" 2>/dev/null || true
+            fi
+          }
+          trap cleanup EXIT
           "$ANDROID_HOME/platform-tools/adb" logcat -v threadtime > "$GITHUB_WORKSPACE/r8-plugin-discovery-logcat.txt" &
+          logcat_pid=$!
           ./gradlew :app:connectedReleaseAndroidTest --stacktrace --info

Based on learnings: track defects imported from upstream as upstream follow-up work unless this PR documents a downstream divergence.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/test-android-r8-plugin-discovery.yml around lines 77 - 80,
Update the workflow script around the background adb logcat command to capture
its process ID and install an EXIT trap that terminates and waits for that
process before the step exits. Preserve the existing log capture and Gradle test
commands, ensuring cleanup completes before diagnostics or artifact handling
proceeds.

Source: Learnings

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/test-android-r8-plugin-discovery.yml:
- Around line 93-96: Update the androidTest XML diagnostics traversal to avoid
interpolating find paths into shell code: pass each matched file as a positional
argument to sh -c and reference that argument when echoing and reading it.
Configure find to skip missing result/report roots so the diagnostic step does
not fail when either directory is absent.

---

Outside diff comments:
In @.github/workflows/test-android-r8-plugin-discovery.yml:
- Around line 16-31: Add a workflow-level permissions declaration to the “test
android r8 plugin discovery” workflow granting only contents read access. Keep
the existing triggers and concurrency configuration unchanged, ensuring
pull-request-controlled Gradle code and actions do not receive broader
repository permissions.

---

Nitpick comments:
In @.github/workflows/test-android-r8-plugin-discovery.yml:
- Around line 77-80: Update the workflow script around the background adb logcat
command to capture its process ID and install an EXIT trap that terminates and
waits for that process before the step exits. Preserve the existing log capture
and Gradle test commands, ensuring cleanup completes before diagnostics or
artifact handling proceeds.
🪄 Autofix

✅ Autofix completed


ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 7ebdc969-c985-46c4-9505-92881986acea

📥 Commits

Reviewing files that changed from the base of the PR and between d41113f and db67836.

📒 Files selected for processing (2)
  • .github/workflows/test-android-r8-plugin-discovery.yml
  • examples/android-r8-plugin-discovery/app/proguard-rules.pro

Comment thread .github/workflows/test-android-r8-plugin-discovery.yml Outdated
@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown

Fixes Applied Successfully

Fixed 1 file(s) based on 1 unresolved review comment.

Files modified:

  • package.json

Commit: 2dd4b056f19b5b53051b46bddece03653633bea7

The changes have been pushed to the sync-upstream-dev-2026-08-11 branch.

Time taken: 3m 25s


⚠️ 1 file(s) could not be committed — the agent does not have permission to push to .github/workflows/. Please apply these changes manually:

.github/workflows/test-android-r8-plugin-discovery.yml — 1 change:

Lines 91–99
             r8-plugin-discovery-logcat.txt 2>/dev/null | tail -n 400 || echo "(no logcat file captured)"
           echo "::endgroup::"
           echo "::group::androidTest XML results"
-          find examples/android-r8-plugin-discovery/app/build/outputs/androidTest-results \
-            examples/android-r8-plugin-discovery/app/build/reports/androidTests \
-            -type f 2>/dev/null -exec sh -c 'echo "--- {} ---"; cat "{}"' \;
+          for d in examples/android-r8-plugin-discovery/app/build/outputs/androidTest-results \
+                   examples/android-r8-plugin-discovery/app/build/reports/androidTests; do
+            [ -d "$d" ] && find "$d" -type f -exec sh -c 'echo "--- $1 ---"; cat "$1"' sh {} \;
+          done
           echo "::endgroup::"
 
       - name: upload diagnostics

coderabbitai Bot and others added 2 commits August 12, 2026 00:37
Fixed 1 file(s) based on 1 unresolved review comment.

Co-authored-by: CodeRabbit <noreply@coderabbit.ai>
… passes

Fourth failure in the same debugging session, but a different bug
class: with the Kotlin/tracing crashes fixed, the instrumentation
finally ran real tests -- 3 of 5 failed with:

  java.lang.NoSuchMethodError: No direct method <init>(JLjava/lang/String;JJLkotlin/jvm/functions/Function2;Ljava/lang/String;Loy;)V
  in class Lapp/tauri/plugin/Invoke;

The test's newInvoke() call matches Invoke's Kotlin constructor
exactly in source. The obfuscated last parameter type ("Loy;") is the
tell: the app APK and its androidTest APK are minified in two
separate R8 passes, and the test calls Invoke's constructor directly
(not via reflection), so its compiled descriptor must match byte-for-
byte between both APKs. Without a keep rule, R8 is free to rename
Jackson's ObjectMapper differently (or not at all) in each pass,
producing a constructor that exists in both APKs under two different
descriptors. Applies to both APKs since this file is wired into both
proguardFiles and testProguardFiles.

Also addresses CodeRabbit's review of the earlier diagnostics commit,
applied here since its autofix bot can't push to .github/workflows/:
- add `permissions: contents: read` (this job runs PR-controlled
  Gradle code + third-party actions)
- avoid shell interpolation of found paths in the diagnostics printer
  (`find -exec sh -c '...{}...'` -> pass paths as positional args) and
  skip missing result/report roots instead of failing
- terminate the background `adb logcat` process via an EXIT trap
  instead of leaving it running past the step
Real bug, not test scaffolding: Invoke's constructor is called
directly (not via reflection) by any code invoking a plugin's
Command/ActivityCallback/PermissionCallback methods, which can live
outside app.tauri.plugin's own compilation unit -- e.g. this
regression test's androidTest APK, which is minified separately from
the app APK. R8 saw no in-APK caller for the constructor from the
androidTest side and was free to shrink/rename it inconsistently with
the app APK, producing:

  java.lang.NoSuchMethodError: No direct method <init>(...)
  in class Lapp/tauri/plugin/Invoke;

even with matching source and a stable ObjectMapper name (previous
commit). This is exactly the class of bug examples/android-r8-plugin-
discovery/app/proguard-rules.pro's header describes: since fixing it
required a rule in *this* file rather than the example app's, that
confirms consumer-rules.pro itself was insufficient. Keep the
constructor here, matching the existing JSArray/JSObject pattern for
classes constructed directly from outside the library. Moves the
ObjectMapper keep here too, since it's a real parameter type of this
shipped constructor, not just test-APK scaffolding.
@OlympusLedgerOrg
OlympusLedgerOrg merged commit b768a02 into dev Aug 12, 2026
35 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants