Skip to content

Conversation

@AnthonyRonning
Copy link
Contributor

@AnthonyRonning AnthonyRonning commented Jan 9, 2026

Summary

Upgrades all GitHub Actions workflows to use larger runners for faster builds.

Changes

Platform Before After Specs
macOS macos-latest (3-core M1, 7GB) macos-latest-xlarge M2, 5 cores + 8 GPU, 14GB RAM
Linux ubuntu-latest (2 CPU, 7GB) ubuntu-latest-8-cores 8 CPU, 32GB RAM, 300GB SSD

Workflows Updated

  • desktop-build.yml - macOS and Linux builds
  • mobile-build.yml - iOS builds
  • release.yml - Release builds (macOS, Linux, Android)
  • android-build.yml - Android CI builds
  • testflight-on-comment.yml - TestFlight deployments
  • claude.yml - Claude Code automation

Setup Required

Before merging, create the Ubuntu larger runner in your org settings:

  1. Go to https://github.com/organizations/OpenSecretCloud/settings/actions/runners
  2. Click New runnerNew GitHub-hosted runner
  3. Name: ubuntu-latest-8-cores
  4. Image: Ubuntu 24.04 (or 22.04)
  5. Size: 8-core (8 CPU, 32GB RAM, 300GB SSD)
  6. Click Create

The macOS macos-latest-xlarge runner works automatically with no setup needed.

Expected Impact

  • Significantly faster Rust compilation (4x more cores on Linux)
  • Android builds should be much faster (compiles 4 targets in parallel)
  • macOS universal builds benefit from M2 chip

Summary by CodeRabbit

  • Chores
    • Split Android CI into modular jobs: frontend build, gradle/Android metadata generation, parallel native builds per target, and a packaging stage producing signed APK/AAB.
    • Switched frontend tooling to a Bun-based flow with caching and explicit artifact uploads between stages.
    • Added top-level environment variables for API endpoints and client ID.
    • Improved per-target caching and native-build caching; upgraded runner sizes across workflows for faster builds.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Jan 9, 2026

Warning

Rate limit exceeded

@AnthonyRonning has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 5 minutes and 54 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between e1b2c4c and 79e2d43.

⛔ Files ignored due to path filters (1)
  • frontend/src-tauri/gen/android/buildSrc/src/main/java/cloud/opensecret/maple/kotlin/RustPlugin.kt is excluded by !**/gen/**
📒 Files selected for processing (1)
  • .github/workflows/android-build.yml
📝 Walkthrough

Walkthrough

Android CI was split from a monolithic build into a staged, multi-job pipeline (frontend build → tauri gradle generation → matrixed per-ABI Rust builds → packaging); several workflows had runner labels upgraded to higher-capacity variants and top-level env vars were added for frontend.

Changes

Cohort / File(s) Summary
Android multi-stage CI
/.github/workflows/android-build.yml
Replaces single build-android with build-frontend, generate-gradle-files, matrix build-rust (per-ABI cross-compilation with sccache/Cargo caching) and package-android; adds Bun-based frontend steps, artifact uploads/downloads (frontend-dist, tauri-android-generated, android-lib-<ABI>.so), tauri.properties generation, and parallel APK/AAB packaging.
Runner label updates
/.github/workflows/claude.yml, /.github/workflows/testflight-on-comment.yml, /.github/workflows/mobile-build.yml
Swapped runner labels: ubuntu-latestubuntu-latest-8-cores, macos-latestmacos-latest-xlarge (no step logic changes).
Desktop workflow runner update
/.github/workflows/desktop-build.yml
Updated runners: macOS → macos-latest-xlarge, Linux → ubuntu-latest-8-cores (steps unchanged).
Release workflow adjustments
/.github/workflows/release.yml
Aligned runner identifiers and platform-specific caching/install conditionals to new runner labels; adjusted related cache/path conditionals.

Sequence Diagram(s)

sequenceDiagram
  participant FE as build-frontend
  participant Art as GitHub Artifacts
  participant Gen as generate-gradle-files
  participant Rust as build-rust (matrix)
  participant Pack as package-android
  participant Gradle as Gradle

  FE->>Art: upload `frontend-dist`
  Gen->>Art: upload `tauri-android-generated`
  Rust->>Art: for each ABI upload `android-lib-<ABI>.so`
  Pack->>Art: download `frontend-dist`, `tauri-android-generated`, all `.so`
  Pack->>Pack: place `.so` into `app/src/main/jniLibs/<ABI>/`
  Pack->>Pack: generate `tauri.properties` and signing config
  Pack->>Gradle: run APK/AAB build tasks (parallel)
  Gradle->>Pack: return APK / AAB
  Pack->>Art: upload final artifacts
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I hopped from build to bundle, neat and spry,
Collected frontend-dist and each lib per-ABI,
I tucked them in jniLibs with a twitch and a spin,
Gave Gradle my props, let the packaging begin,
Out popped APK and AAB — a carrot-shaped win 🥕

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the primary change: refactoring the Android build pipeline to use larger GitHub Actions runners and restructure the workflow into modular, parallel jobs.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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

@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Jan 9, 2026

Deploying maple with  Cloudflare Pages  Cloudflare Pages

Latest commit: 79e2d43
Status: ✅  Deploy successful!
Preview URL: https://2637ddfd.maple-ca8.pages.dev
Branch Preview URL: https://upgrade-github-actions-runne.maple-ca8.pages.dev

View logs

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

Greptile Overview

Greptile Summary

This PR upgrades all GitHub Actions workflows to use larger runners for improved build performance:

Linux workflows transition from ubuntu-latest (2 CPU, 7GB) to ubuntu-latest-8-cores (8 CPU, 32GB RAM, 300GB SSD):

  • android-build.yml - Benefits Android builds that compile 4 targets in parallel
  • claude.yml - Speeds up Claude Code automation tasks
  • desktop-build.yml (build-linux job) - Accelerates Linux desktop builds
  • release.yml (3 jobs: create-release, build-android, update-latest-json) - Improves release pipeline
  • testflight-on-comment.yml (check-comment job) - Faster permission checks

macOS workflows upgrade from macos-latest (3-core M1, 7GB) to macos-latest-xlarge (M2, 5 cores + 8 GPU, 14GB RAM):

  • desktop-build.yml (build-macos job) - Leverages M2 for universal binary compilation
  • mobile-build.yml (build-ios job) - Accelerates iOS builds and TestFlight submissions
  • release.yml (build-tauri matrix) - Speeds up release builds for macOS
  • testflight-on-comment.yml (deploy-testflight job) - Faster TestFlight deployments

The changes are consistent across all files. In release.yml, all conditional checks comparing matrix.platform were correctly updated to use the new runner names. The PR description clearly documents that ubuntu-latest-8-cores must be manually created in organization settings before merging, while macos-latest-xlarge is a standard GitHub-hosted runner requiring no setup.

Expected Impact: Significantly faster Rust compilation (4x more cores for Linux), improved Android multi-target builds, and better macOS universal binary performance with M2 chips.

Confidence Score: 5/5

  • This PR is safe to merge once the ubuntu-latest-8-cores runner is created in organization settings
  • All workflow changes are straightforward runner label replacements with no logic modifications. The changes are consistent across all 6 files, and all conditional checks in release.yml correctly reference the new runner names. The PR description clearly documents the setup requirement for the Ubuntu runner, and the syntax is valid throughout.
  • No files require special attention - all changes follow the same simple pattern of updating runner labels

Important Files Changed

File Analysis

Filename Score Overview
.github/workflows/android-build.yml 5/5 Changed runner from ubuntu-latest to ubuntu-latest-8-cores for Android builds, requires manual setup before merging
.github/workflows/claude.yml 5/5 Changed runner from ubuntu-latest to ubuntu-latest-8-cores for Claude Code automation, requires manual setup before merging
.github/workflows/desktop-build.yml 5/5 Updated both macOS (to macos-latest-xlarge) and Linux (to ubuntu-latest-8-cores) runners for desktop builds
.github/workflows/mobile-build.yml 5/5 Changed iOS build runner from macos-latest to macos-latest-xlarge for faster builds
.github/workflows/release.yml 5/5 Updated all jobs to use larger runners, correctly updated matrix platform conditionals to match new runner names
.github/workflows/testflight-on-comment.yml 5/5 Updated both check-comment (ubuntu) and deploy-testflight (macOS) jobs to use larger runners

Sequence Diagram

sequenceDiagram
    participant Dev as Developer
    participant GHA as GitHub Actions
    participant OrgAdmin as Org Admin
    participant Ubuntu as ubuntu-latest-8-cores
    participant MacOS as macos-latest-xlarge

    Note over Dev,MacOS: PR #377: Runner Upgrade Flow

    Dev->>GHA: Push to master/PR
    
    alt Ubuntu Runner Setup Required
        GHA->>OrgAdmin: Workflow fails (runner not found)
        OrgAdmin->>GHA: Create ubuntu-latest-8-cores runner
        Note over OrgAdmin,GHA: Org Settings → New Runner<br/>8-core, 32GB RAM, 300GB SSD
    end

    par Desktop Build (macOS)
        GHA->>MacOS: build-macos job
        MacOS->>MacOS: Universal binary (M2, 5 cores, 14GB)
        MacOS->>GHA: Upload DMG artifacts
    and Desktop Build (Linux)
        GHA->>Ubuntu: build-linux job
        Ubuntu->>Ubuntu: Linux build (8 CPU, 32GB)
        Ubuntu->>GHA: Upload AppImage/DEB
    and Mobile Build (iOS)
        GHA->>MacOS: build-ios job
        MacOS->>MacOS: iOS build + TestFlight
        MacOS->>GHA: Upload IPA
    and Android Build
        GHA->>Ubuntu: build-android job
        Ubuntu->>Ubuntu: 4 target builds (parallel)
        Ubuntu->>GHA: Upload APK/AAB
    and Claude Automation
        GHA->>Ubuntu: claude job
        Ubuntu->>Ubuntu: Run Claude Code
        Ubuntu->>GHA: Results
    end

    Note over Dev,MacOS: All workflows now use larger runners<br/>for faster compilation
Loading

Copy link

@coderabbitai coderabbitai bot left a comment

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/release.yml (1)

52-88: Include matrix.platform in the sccache cache key.

The sccache cache is platform-specific: macOS uses Homebrew while Linux uses a downloaded binary, producing incompatible caches. Using runner.os alone risks restoring macOS cache on Linux (or vice versa). Update the key to:

-          key: ${{ runner.os }}-sccache-release-${{ hashFiles('**/Cargo.lock') }}
+          key: ${{ runner.os }}-${{ matrix.platform }}-sccache-release-${{ hashFiles('**/Cargo.lock') }}

(The ~ expansion in actions/cache is reliable, including in conditional expressions—no concern there.)

🧹 Nitpick comments (2)
.github/workflows/release.yml (2)

323-326: Consider keeping update-latest-json on standard ubuntu-latest.
This job is mostly gh + file generation; using the large runner increases cost and failure modes without much speedup.

Proposed diff
   update-latest-json:
     needs: build-tauri
-    runs-on: ubuntu-latest-8-cores
+    runs-on: ubuntu-latest

32-38: Consider pinning the Ubuntu runner version explicitly.

The workflow uses ubuntu-latest-8-cores, which currently resolves to Ubuntu 24.04. For clarity and to avoid unexpected changes when GitHub updates runner images, use ubuntu-24.04-8-cores instead. This makes the Ubuntu version dependency explicit in the code rather than relying on GitHub's label mappings.

(Note: libwebkit2gtk-4.1-dev itself is available on both Ubuntu 22.04 and 24.04, so it doesn't force a specific version—the Ubuntu 24.04 assumption comes from the -latest label.)

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 925c1b5 and e2b73d5.

📒 Files selected for processing (6)
  • .github/workflows/android-build.yml
  • .github/workflows/claude.yml
  • .github/workflows/desktop-build.yml
  • .github/workflows/mobile-build.yml
  • .github/workflows/release.yml
  • .github/workflows/testflight-on-comment.yml
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: AnthonyRonning
Repo: OpenSecretCloud/Maple PR: 372
File: frontend/src-tauri/Cargo.toml:9-9
Timestamp: 2026-01-08T17:09:40.432Z
Learning: The OpenSecretCloud/Maple repository has comprehensive GitHub Actions workflows (e.g., desktop-build.yml) that automatically validate compilation and testing, so manual compilation verification reminders are unnecessary when reviewing version bumps or dependency updates.
📚 Learning: 2026-01-08T17:09:40.432Z
Learnt from: AnthonyRonning
Repo: OpenSecretCloud/Maple PR: 372
File: frontend/src-tauri/Cargo.toml:9-9
Timestamp: 2026-01-08T17:09:40.432Z
Learning: The OpenSecretCloud/Maple repository has comprehensive GitHub Actions workflows (e.g., desktop-build.yml) that automatically validate compilation and testing, so manual compilation verification reminders are unnecessary when reviewing version bumps or dependency updates.

Applied to files:

  • .github/workflows/release.yml
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: build-ios
  • GitHub Check: build-android
  • GitHub Check: build-macos (universal-apple-darwin)
  • GitHub Check: build-linux
  • GitHub Check: Cloudflare Pages
🔇 Additional comments (11)
.github/workflows/mobile-build.yml (1)

11-11: LGTM! Valid upgrade to M2-powered runner.

The macos-latest-xlarge runner uses a 5 vCPU M2 Pro machine, providing improved performance for iOS builds.

.github/workflows/desktop-build.yml (2)

11-11: LGTM! Valid upgrade to M2-powered runner.

The macOS build will benefit from the enhanced M2 Pro hardware with 5 cores for faster universal builds.


117-117: LGTM! Upgrade to 8-core runner.

The Linux build will benefit from 4× more cores (8 vs 2) for faster Rust compilation.

.github/workflows/claude.yml (1)

33-33: LGTM! Upgrade to 8-core runner.

The Claude automation workflow will benefit from the additional compute resources.

.github/workflows/android-build.yml (1)

11-11: LGTM! Upgrade to 8-core runner.

The 8-core runner will significantly improve parallel compilation of the 4 Android targets (aarch64, armv7, x86_64, i686).

.github/workflows/testflight-on-comment.yml (3)

15-15: LGTM! Upgrade to 8-core runner.

The permission check job will benefit from the enhanced runner resources.


67-67: LGTM! Valid upgrade to M2-powered runner.

The TestFlight deployment will benefit from the M2 Pro hardware for faster iOS builds.


15-15: Remove the "Also applies to: 67-67" reference—only line 15 requires verification.

Line 15 correctly uses ubuntu-latest-8-cores, a custom runner that must be manually created in your organization's Actions runner settings before merging. However, line 67 uses macos-latest-xlarge, which is a GitHub-provided larger runner label and does not require custom setup.

To proceed, ensure the following is completed:

  1. Navigate to organization Actions runner settings
  2. Create a new GitHub-hosted runner named ubuntu-latest-8-cores
  3. Configure it with Ubuntu 24.04 (or 22.04) and 8-core size (8 CPU, 32GB RAM, 300GB SSD)

Likely an incorrect or invalid review comment.

.github/workflows/release.yml (3)

133-136: Runner upgrade for Android build makes sense; just note it’s now dependent on the custom runner existing.
No functional concerns in this diff itself.


10-12: No action needed. The ubuntu-latest-8-cores label is an official GitHub-hosted larger runner (not a custom organization-created runner), so there is no "blast radius" risk from tying the job to it. The choice to use an 8-core runner for the release job is likely intentional for build performance and is a standard GitHub Actions feature.

Likely an incorrect or invalid review comment.


71-80: No action needed. The ubuntu-latest-8-cores runner already uses Ubuntu 24.04, and libwebkit2gtk-4.1-dev is available and installable on Ubuntu 22.04 (in the universe repository) with versions ranging from 2.36.x to 2.50.x. The package will not fail on either version.

Likely an incorrect or invalid review comment.

Comment on lines 47 to 51
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.platform == 'macos-latest' && 'aarch64-apple-darwin,x86_64-apple-darwin' || '' }}
targets: ${{ matrix.platform == 'macos-latest-xlarge' && 'aarch64-apple-darwin,x86_64-apple-darwin' || '' }}

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

dtolnay/rust-toolchain action targets empty string parameter

💡 Result:

Short answer: don’t pass an empty string to the targets input — instead make the rust-toolchain step conditional so it only runs when a target is present.

Why: dtolnay/rust-toolchain’s inputs expect a comma-separated list of target triples; giving an empty string can be interpreted as “install nothing” or can cause rustup to be invoked with an empty argument. The action’s docs show the targets input is a comma-separated string. [1]

How to fix: condition the step on the matrix value (or on the variable) being non-empty. Example:

  • name: Install toolchain
    if: ${{ matrix.target != '' }}
    uses: dtolnay/rust-toolchain@stable
    with:
    targets: ${{ matrix.target }}

You can use GitHub Actions expressions to compare to the empty string as shown above. [2]

References:

  • dtolnay/rust-toolchain README (inputs: targets). [1]
  • GitHub Actions expressions / conditionals (string / empty-string behavior). [2]

🏁 Script executed:

cat -n .github/workflows/release.yml | sed -n '40,60p'

Repository: OpenSecretCloud/Maple

Length of output: 908


Split Rust installation into conditional steps to avoid empty targets parameter.

The dtolnay/rust-toolchain action does not handle an empty string for the targets input; passing targets: "" causes workflow failures. Use conditional steps instead to install the toolchain only when targets are needed.

Proposed diff
       - name: Install Rust
         uses: dtolnay/rust-toolchain@stable
-        with:
-          targets: ${{ matrix.platform == 'macos-latest-xlarge' && 'aarch64-apple-darwin,x86_64-apple-darwin' || '' }}
+        if: matrix.platform != 'macos-latest-xlarge'
+
+      - name: Install Rust (macOS universal targets)
+        if: matrix.platform == 'macos-latest-xlarge'
+        uses: dtolnay/rust-toolchain@stable
+        with:
+          targets: aarch64-apple-darwin,x86_64-apple-darwin

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

Greptile Overview

Greptile Summary

Upgrades all GitHub Actions workflows to use larger runners for improved build performance. Linux workflows now use ubuntu-latest-8-cores (8 CPU, 32GB RAM) and macOS workflows use macos-latest-xlarge (M2, 5 cores). The Android build workflow was also refactored to split builds into three parallel jobs, enabling simultaneous compilation of all four Rust targets.

Confidence Score: 4/5

  • Safe to merge after creating the ubuntu-latest-8-cores runner in organization settings
  • All runner configuration changes are syntactically correct and consistent across workflows. The macOS xlarge runner works automatically, but the PR description correctly notes that the Ubuntu 8-core runner must be manually created in organization settings before merging, which is a prerequisite configuration step rather than a code issue.
  • .github/workflows/android-build.yml requires verification that the refactored multi-job workflow produces identical artifacts

Important Files Changed

File Analysis

Filename Score Overview
.github/workflows/android-build.yml 4/5 Refactored to split Android build into 3 jobs (frontend, parallel Rust targets, Gradle packaging) using ubuntu-latest-8-cores runners; significantly improves build parallelism

Sequence Diagram

sequenceDiagram
    participant Frontend as build-frontend
    participant Matrix as build-rust (matrix)
    participant Package as package-android
    participant Artifacts as Outputs
    
    Frontend->>Frontend: "Build frontend with Bun"
    Frontend->>Matrix: "Upload frontend-dist artifact"
    
    par Parallel Rust builds
        Matrix->>Matrix: "Build aarch64 (arm64-v8a)"
        Matrix->>Matrix: "Build armv7 (armeabi-v7a)"
        Matrix->>Matrix: "Build i686 (x86)"
        Matrix->>Matrix: "Build x86_64"
    end
    
    Matrix->>Package: "Upload 4 .so artifacts"
    Package->>Package: "Download frontend-dist"
    Package->>Package: "Download all .so files"
    Package->>Package: "Place in jniLibs directories"
    Package->>Package: "Run Gradle (APK & AAB)"
    Package->>Artifacts: "Upload APK and AAB"
Loading

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @.github/workflows/android-build.yml:
- Around line 86-93: The workflow step that installs sccache downloads and
executes an archive without validating its integrity; change the "Install
sccache" step to first download the tarball and its corresponding .sha256
checksum (using SCCACHE_VERSION / SCCACHE_URL), verify the tarball with
sha256sum -c (failing the job if verification fails), only then extract the
archive and run sudo mv / chmod on the verified binary; ensure you stop piping
curl into tar and instead write files to disk so the checksum can be validated
before extraction or installation.
🧹 Nitpick comments (4)
.github/workflows/android-build.yml (4)

16-17: Current job dependencies block parallelism (Rust doesn’t need to wait for frontend).

build-rust doesn’t consume frontend-dist, so needs: build-frontend forces unnecessary serialization; let them run concurrently and make package-android depend on both.

Proposed diff
   build-rust:
     runs-on: ubuntu-latest-8-cores
-    needs: build-frontend
     strategy:
       fail-fast: false
@@
   package-android:
     runs-on: ubuntu-latest-8-cores
-    needs: build-rust
+    needs: [build-frontend, build-rust]

Also applies to: 52-55, 174-176


36-43: Use frozen installs in CI for Bun.

Prefer failing on lockfile drift to keep CI deterministic.

Proposed diff
       - name: Install frontend dependencies
         working-directory: ./frontend
-        run: bun install
+        run: bun install --frozen-lockfile
@@
       - name: Install frontend dependencies
         working-directory: ./frontend
-        run: bun install
+        run: bun install --frozen-lockfile

Also applies to: 221-224


231-251: Make native-lib placement step fail fast and clearer when artifacts are missing.

This step will currently fail on missing artifacts, but adding strict shell flags makes failures more diagnosable (and avoids partial copies).

Proposed diff
       - name: Place native libraries in jniLibs
         run: |
+          set -euo pipefail
           # Create jniLibs directories
           mkdir -p frontend/src-tauri/gen/android/app/src/main/jniLibs/{arm64-v8a,armeabi-v7a,x86,x86_64}

118-156: Hard-coded API level 24 matches your current minSdk, but consider parameterizing it for maintainability.

The workflow uses *android24-clang which currently aligns with your minSdk = 24 in build.gradle.kts. However, hard-coding this value in the workflow separate from the Gradle config creates a maintenance risk: if minSdk changes in the future, you'd need to remember to update both places. Consider extracting the Android API level into a shared configuration or environment variable to keep it in sync.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e2b73d5 and 93dcabe.

📒 Files selected for processing (1)
  • .github/workflows/android-build.yml
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: AnthonyRonning
Repo: OpenSecretCloud/Maple PR: 372
File: frontend/src-tauri/Cargo.toml:9-9
Timestamp: 2026-01-08T17:09:40.432Z
Learning: The OpenSecretCloud/Maple repository has comprehensive GitHub Actions workflows (e.g., desktop-build.yml) that automatically validate compilation and testing, so manual compilation verification reminders are unnecessary when reviewing version bumps or dependency updates.
📚 Learning: 2026-01-08T17:09:40.432Z
Learnt from: AnthonyRonning
Repo: OpenSecretCloud/Maple PR: 372
File: frontend/src-tauri/Cargo.toml:9-9
Timestamp: 2026-01-08T17:09:40.432Z
Learning: The OpenSecretCloud/Maple repository has comprehensive GitHub Actions workflows (e.g., desktop-build.yml) that automatically validate compilation and testing, so manual compilation verification reminders are unnecessary when reviewing version bumps or dependency updates.

Applied to files:

  • .github/workflows/android-build.yml
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: build-frontend
  • GitHub Check: build-linux
  • GitHub Check: build-macos (universal-apple-darwin)
  • GitHub Check: build-ios
  • GitHub Check: Cloudflare Pages
🔇 Additional comments (1)
.github/workflows/android-build.yml (1)

9-13: Double-check PR vs push env routing for Vite endpoints.

The ternary expression will embed different backend URLs depending on github.event_name; make sure PR builds should indeed point at https://enclave.secretgpt.ai / billing-dev... and pushes to master at https://enclave.trymaple.ai / billing....

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

Greptile Overview

Greptile Summary

This PR upgrades all GitHub Actions workflows to use larger, faster runners for improved build performance.

Runner Upgrades:

  • macOS: macos-latestmacos-latest-xlarge (M2, 5 cores + 8 GPU, 14GB RAM)
  • Linux: ubuntu-latestubuntu-latest-8-cores (8 CPU, 32GB RAM, 300GB SSD)

Key Changes:

  1. Simple Runner Upgrades (5 workflows): claude.yml, desktop-build.yml, mobile-build.yml, release.yml, and testflight-on-comment.yml only change runs-on directives and update conditional checks from old to new runner names.

  2. Android Build Restructure (android-build.yml): Beyond the runner upgrade, this workflow was significantly refactored into a 3-stage pipeline:

    • Stage 1 (build-frontend): Builds frontend once, uploads frontend-dist artifact
    • Stage 2 (build-rust): Matrix job compiling 4 Android targets in parallel (aarch64, armv7, i686, x86_64), each uploading native .so libraries
    • Stage 3 (package-android): Downloads all artifacts, places native libs in jniLibs/ directories, and runs Gradle to produce APK/AAB

    The restructure eliminates Tauri CLI usage in favor of manual Rust builds + Gradle packaging, enabling true parallelization of the expensive cross-compilation step.

  3. Environment Variables: Android workflow moves VITE_* variables from step-level to workflow-level for cleaner sharing across jobs.

Setup Note: The ubuntu-latest-8-cores runner must be manually created in GitHub org settings before workflows will function.

Confidence Score: 4/5

  • Safe to merge after creating the ubuntu-latest-8-cores runner in org settings. No breaking changes to build logic.
  • The PR is mostly safe - it's primarily runner upgrades with one significant architectural change (Android build parallelization). The macOS runner works automatically, but the custom Linux runner must be created first or all Linux/Android builds will fail. The Android restructure appears sound: gen/android is committed, artifacts flow correctly, and the manual Gradle approach mirrors release.yml's existing pattern. One minor inefficiency: package-android unnecessarily runs bun install before downloading pre-built artifacts.
  • .github/workflows/android-build.yml requires the ubuntu-latest-8-cores runner to be created in org settings before it will work

Important Files Changed

File Analysis

Filename Score Overview
.github/workflows/android-build.yml 4/5 Refactored to use ubuntu-latest-8-cores runner; restructured into 3-stage pipeline (build-frontend, build-rust with matrix, package-android) for parallel builds. Removed Tauri CLI usage in favor of manual Gradle builds. Environment variables moved to workflow level.
.github/workflows/claude.yml 5/5 Simple runner upgrade from ubuntu-latest to ubuntu-latest-8-cores. No other changes.
.github/workflows/desktop-build.yml 5/5 Runner upgrades: macos-latest to macos-latest-xlarge and ubuntu-latest to ubuntu-latest-8-cores. No other logic changes.
.github/workflows/mobile-build.yml 5/5 Simple runner upgrade from macos-latest to macos-latest-xlarge. No other changes.
.github/workflows/release.yml 5/5 Runner upgrades across all jobs: ubuntu-latest to ubuntu-latest-8-cores, macos-latest to macos-latest-xlarge. Updated conditional checks throughout to reference new runner names.
.github/workflows/testflight-on-comment.yml 5/5 Runner upgrades: ubuntu-latest to ubuntu-latest-8-cores and macos-latest to macos-latest-xlarge. No other logic changes.

Sequence Diagram

sequenceDiagram
    participant GH as GitHub Actions
    participant BF as build-frontend
    participant BR as build-rust (matrix x4)
    participant PA as package-android
    participant Artifacts as Artifact Storage
    
    Note over GH: Trigger: push/PR to master
    
    GH->>BF: Start job (ubuntu-latest-8-cores)
    BF->>BF: Setup Bun & install deps
    BF->>BF: Build frontend (bun run build)
    BF->>Artifacts: Upload frontend-dist
    
    BF->>BR: Trigger build-rust (needs: build-frontend)
    
    par Build 4 targets in parallel
        BR->>BR: aarch64-linux-android (ubuntu-latest-8-cores)
        BR->>BR: armv7-linux-androideabi (ubuntu-latest-8-cores)
        BR->>BR: i686-linux-android (ubuntu-latest-8-cores)
        BR->>BR: x86_64-linux-android (ubuntu-latest-8-cores)
    end
    
    BR->>BR: Setup NDK & Rust toolchain (each)
    BR->>BR: Configure cross-compilation env
    BR->>BR: cargo build --release --target {target} --lib
    BR->>Artifacts: Upload android-lib-{short_name} (x4)
    
    BR->>PA: Trigger package-android (needs: build-rust)
    
    PA->>PA: Setup Java, Android SDK, NDK
    PA->>Artifacts: Download frontend-dist
    PA->>Artifacts: Download all android-lib-* artifacts
    PA->>PA: Place .so files in jniLibs/{arch} dirs
    PA->>PA: Setup Android signing & tauri.properties
    PA->>PA: ./gradlew assembleUniversalRelease bundleUniversalRelease
    PA->>Artifacts: Upload APK & AAB artifacts
Loading

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
.github/workflows/android-build.yml (2)

94-102: Consider larger sccache size with 32GB RAM.

The per-target cache isolation using matrix.short_name is correct for parallel builds. However, with 32GB RAM available on the 8-core runner, the 2G sccache limit (set at line 122) might be conservative—consider testing with 4-5G if build times remain slow.


225-227: Remove redundant frontend dependency installation.

The package-android job downloads the pre-built frontend-dist artifact (lines 229-233) and reads tauri.conf.json from the checked-out repo (line 270). Installing frontend dependencies here appears unnecessary and wastes build time on the 8-core runner.

♻️ Proposed fix: Remove the redundant install step
-      - name: Install frontend dependencies
-        working-directory: ./frontend
-        run: bun install
-
       - name: Download frontend dist

Note: If frontend dependencies are needed for some reason (e.g., a post-build script), please document why.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 93dcabe and e347d22.

📒 Files selected for processing (1)
  • .github/workflows/android-build.yml
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: AnthonyRonning
Repo: OpenSecretCloud/Maple PR: 372
File: frontend/src-tauri/Cargo.toml:9-9
Timestamp: 2026-01-08T17:09:40.432Z
Learning: The OpenSecretCloud/Maple repository has comprehensive GitHub Actions workflows (e.g., desktop-build.yml) that automatically validate compilation and testing, so manual compilation verification reminders are unnecessary when reviewing version bumps or dependency updates.
📚 Learning: 2026-01-08T17:09:40.432Z
Learnt from: AnthonyRonning
Repo: OpenSecretCloud/Maple PR: 372
File: frontend/src-tauri/Cargo.toml:9-9
Timestamp: 2026-01-08T17:09:40.432Z
Learning: The OpenSecretCloud/Maple repository has comprehensive GitHub Actions workflows (e.g., desktop-build.yml) that automatically validate compilation and testing, so manual compilation verification reminders are unnecessary when reviewing version bumps or dependency updates.

Applied to files:

  • .github/workflows/android-build.yml
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: build-rust (x86_64-linux-android, x86_64, x86_64)
  • GitHub Check: build-rust (armv7-linux-androideabi, armeabi-v7a, armv7)
  • GitHub Check: build-rust (i686-linux-android, x86, i686)
  • GitHub Check: build-rust (aarch64-linux-android, arm64-v8a, aarch64)
  • GitHub Check: build-linux
  • GitHub Check: build-macos (universal-apple-darwin)
  • GitHub Check: build-ios
  • GitHub Check: Cloudflare Pages
🔇 Additional comments (8)
.github/workflows/android-build.yml (8)

9-12: LGTM! Environment-aware API configuration.

The conditional API URLs properly separate PR builds (dev endpoints) from master builds (production endpoints), which is a solid practice for testing changes in isolation.


52-71: LGTM! Well-structured matrix for parallel Android target builds.

The fail-fast: false configuration ensures all targets complete even if one fails, providing comprehensive CI feedback. The inclusion of short_name and jni_dir metadata cleanly handles per-target differences.


162-168: LGTM! Correct Rust library build command.

Building only the library (--lib) for each target is appropriate for Android JNI integration. The sccache stats output is helpful for monitoring cache effectiveness.


241-254: LGTM! Correct JNI library placement with helpful verification.

The native library organization matches Android's expected JNI structure. The verification output (lines 252-254) will be valuable for debugging any library placement issues.


282-289: LGTM! Efficient parallel APK and AAB generation.

Building both release artifacts with --parallel takes advantage of the 8-core runner to reduce overall package time.


170-175: Use the explicit library name instead of wildcard for clarity.

The library is configured with name = "app_lib" and crate-type = ["cdylib", ...], which produces exactly one .so file: libapp_lib.so. While *.so works correctly, explicitly naming the artifact file as libapp_lib.so would be clearer and more maintainable.


118-160: API level 24 is correctly aligned with minSdkVersion; NDK paths are valid.

The verification confirms:

  1. API level alignment: ✓ The minSdk in build.gradle.kts is set to 24, matching the android24 API level used consistently across all target toolchain paths.

  2. NDK r27c and linux-x86_64 paths: ✓ Confirmed correct for GitHub Actions runners. All targets properly configured with the NDK toolchain paths.

  3. Symlinks with sudo ln -sf: The RANLIB_* environment variables are set correctly and should be sufficient for Cargo to locate ranlib. The symlinks create names with hyphens (e.g., aarch64-linux-android-ranlib) while Cargo uses the RANLIB_* env vars with underscores. The symlinks may be redundant, but they provide no harm and could serve as a fallback for other build tools.


17-17: No action needed. The ubuntu-latest-8-cores runner is a standard GitHub-hosted runner and does not require custom organization configuration. It is part of GitHub's larger hosted runners offering and is already in use across multiple workflows in this repository.

Likely an incorrect or invalid review comment.

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

Greptile Overview

Greptile Summary

Upgrades all GitHub Actions workflows to use larger runners for faster builds. macOS jobs move to macos-latest-xlarge (M2, 5 cores, 14GB RAM) and Linux jobs to ubuntu-latest-8-cores (8 CPU, 32GB RAM, 300GB SSD).

Major Changes:

  • android-build.yml - Completely restructured into a 3-job pipeline: separate frontend build, parallel Rust compilation for 4 Android ABIs (aarch64, armv7, i686, x86_64), and final Gradle packaging with artifact passing
  • desktop-build.yml, mobile-build.yml, testflight-on-comment.yml - Straightforward runner upgrades for build jobs
  • release.yml - All jobs upgraded to larger runners
  • claude.yml - Upgraded to 8-core runner for AI automation tasks

Minor Issues:

  • Some lightweight jobs (permission checks, JSON generation) use 8-core runners unnecessarily, adding cost without benefit
  • PR requires manual setup of ubuntu-latest-8-cores runner in org settings before merging (instructions provided)

Confidence Score: 4/5

  • Safe to merge after creating the ubuntu-latest-8-cores runner in org settings
  • The changes are well-structured and the Android pipeline refactor is architecturally sound. Minor inefficiencies exist with oversized runners for lightweight jobs, but these won't cause failures. Main blocker is the required manual runner setup before merge.
  • Verify the ubuntu-latest-8-cores runner is created in org settings before merging, otherwise all Linux and Android builds will fail

Important Files Changed

File Analysis

Filename Score Overview
.github/workflows/android-build.yml 4/5 Restructured into 3-job pipeline (frontend build, parallel Rust builds, Gradle packaging) and upgraded to 8-core runners. Architecture looks sound with proper artifact passing.
.github/workflows/claude.yml 5/5 Simple runner upgrade from ubuntu-latest to ubuntu-latest-8-cores for Claude Code automation. Appropriate for complex AI-driven tasks.
.github/workflows/desktop-build.yml 5/5 Runner upgrades to macos-latest-xlarge and ubuntu-latest-8-cores. Appropriate for Rust compilation and Tauri builds.
.github/workflows/mobile-build.yml 5/5 Runner upgrade to macos-latest-xlarge for iOS builds. Appropriate for Xcode compilation.
.github/workflows/release.yml 4/5 All jobs upgraded to larger runners. Some lightweight jobs (create-release, update-latest-json) use 8-core runners unnecessarily.
.github/workflows/testflight-on-comment.yml 4/5 Both jobs upgraded to larger runners. The check-comment job uses 8-core runner unnecessarily for permission checking.

Sequence Diagram

sequenceDiagram
    participant GH as GitHub Actions
    participant BF as build-frontend Job
    participant BR as build-rust Job (Matrix)
    participant PA as package-android Job
    participant Artifacts as GitHub Artifacts

    Note over GH: Android Build Workflow (New Architecture)
    
    GH->>BF: Start on ubuntu-latest-8-cores
    BF->>BF: Setup Bun & Install Dependencies
    BF->>BF: Build Frontend (bun run build)
    BF->>Artifacts: Upload frontend dist
    
    BF->>BR: Trigger 4 parallel matrix jobs
    
    par Parallel Rust Builds (4 targets)
        BR->>BR: aarch64 - Setup NDK, Build Rust lib
        BR->>BR: Generate tauri.settings.gradle (aarch64 only)
        BR->>Artifacts: Upload aarch64 .so + gradle file
    and
        BR->>BR: armv7 - Setup NDK, Build Rust lib
        BR->>Artifacts: Upload armv7 .so
    and
        BR->>BR: i686 - Setup NDK, Build Rust lib
        BR->>Artifacts: Upload i686 .so
    and
        BR->>BR: x86_64 - Setup NDK, Build Rust lib
        BR->>Artifacts: Upload x86_64 .so
    end
    
    BR->>PA: All builds complete
    
    PA->>Artifacts: Download frontend dist
    PA->>Artifacts: Download all 4 native libraries
    PA->>Artifacts: Download tauri.settings.gradle
    PA->>PA: Place libraries in jniLibs directories
    PA->>PA: Setup Android signing
    PA->>PA: Generate tauri.properties
    PA->>PA: Run Gradle (assemble + bundle)
    PA->>Artifacts: Upload APK & AAB (5 day retention)
Loading

check-comment:
if: github.event.issue.pull_request && contains(github.event.comment.body, 'testflight build')
runs-on: ubuntu-latest
runs-on: ubuntu-latest-8-cores
Copy link
Contributor

Choose a reason for hiding this comment

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

unnecessary 8-core runner for lightweight permission check

Suggested change
runs-on: ubuntu-latest-8-cores
runs-on: ubuntu-latest

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: .github/workflows/testflight-on-comment.yml
Line: 15:15

Comment:
unnecessary 8-core runner for lightweight permission check

```suggestion
    runs-on: ubuntu-latest
```

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

How can I resolve this? If you propose a fix, please make it concise.

jobs:
create-release:
runs-on: ubuntu-latest
runs-on: ubuntu-latest-8-cores
Copy link
Contributor

Choose a reason for hiding this comment

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

unnecessary 8-core runner for lightweight version extraction

Suggested change
runs-on: ubuntu-latest-8-cores
runs-on: ubuntu-latest

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: .github/workflows/release.yml
Line: 11:11

Comment:
unnecessary 8-core runner for lightweight version extraction

```suggestion
    runs-on: ubuntu-latest
```

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

How can I resolve this? If you propose a fix, please make it concise.

update-latest-json:
needs: build-tauri
runs-on: ubuntu-latest
runs-on: ubuntu-latest-8-cores
Copy link
Contributor

Choose a reason for hiding this comment

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

unnecessary 8-core runner for lightweight artifact download and JSON generation

Suggested change
runs-on: ubuntu-latest-8-cores
runs-on: ubuntu-latest

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: .github/workflows/release.yml
Line: 325:325

Comment:
unnecessary 8-core runner for lightweight artifact download and JSON generation

```suggestion
    runs-on: ubuntu-latest
```

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

How can I resolve this? If you propose a fix, please make it concise.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
.github/workflows/android-build.yml (3)

180-203: Consider checking if Tauri CLI has a dedicated command for generating gradle files.

The current approach runs a build that intentionally fails to generate tauri.settings.gradle. While functional, this is fragile and wastes build time attempting Rust compilation before failing.

Does Tauri CLI 2.9.2 have a command to generate Android gradle files without building?

243-251: Potential cache key issue: gradle files may not exist at cache-restore time.

The Gradle cache key includes frontend/src-tauri/gen/android/**/*.gradle*, but these files are downloaded from artifacts after the cache is restored (line 265-269). This means hashFiles() will return an empty hash at cache-restore time, potentially causing cache misses.

Consider using a cache key that doesn't depend on downloaded artifacts, such as:

key: ${{ runner.os }}-gradle-${{ hashFiles('**/Cargo.lock') }}

271-284: Add error handling for library placement.

The cp commands with *.so globs may fail silently if artifacts are missing. Consider adding verification after each copy or using set -e to ensure the script exits on any error.

♻️ Proposed improvement
       - name: Place native libraries in jniLibs
         run: |
+          set -e  # Exit on any error
+          
           # Create jniLibs directories
           mkdir -p frontend/src-tauri/gen/android/app/src/main/jniLibs/{arm64-v8a,armeabi-v7a,x86,x86_64}
           
           # Copy libraries to correct locations
           cp native-libs/android-lib-aarch64/*.so frontend/src-tauri/gen/android/app/src/main/jniLibs/arm64-v8a/
           cp native-libs/android-lib-armv7/*.so frontend/src-tauri/gen/android/app/src/main/jniLibs/armeabi-v7a/
           cp native-libs/android-lib-i686/*.so frontend/src-tauri/gen/android/app/src/main/jniLibs/x86/
           cp native-libs/android-lib-x86_64/*.so frontend/src-tauri/gen/android/app/src/main/jniLibs/x86_64/
           
+          # Verify all ABIs have libraries
+          for abi in arm64-v8a armeabi-v7a x86 x86_64; do
+            if [ ! -f frontend/src-tauri/gen/android/app/src/main/jniLibs/$abi/*.so ]; then
+              echo "ERROR: No .so files found for ABI: $abi"
+              exit 1
+            fi
+          done
+          
           # Verify
           echo "=== Native libraries placed ==="
           find frontend/src-tauri/gen/android/app/src/main/jniLibs -name "*.so" -exec ls -la {} \;
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e347d22 and e7670fb.

📒 Files selected for processing (1)
  • .github/workflows/android-build.yml
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: AnthonyRonning
Repo: OpenSecretCloud/Maple PR: 372
File: frontend/src-tauri/Cargo.toml:9-9
Timestamp: 2026-01-08T17:09:40.432Z
Learning: The OpenSecretCloud/Maple repository has comprehensive GitHub Actions workflows (e.g., desktop-build.yml) that automatically validate compilation and testing, so manual compilation verification reminders are unnecessary when reviewing version bumps or dependency updates.
📚 Learning: 2026-01-08T17:09:40.432Z
Learnt from: AnthonyRonning
Repo: OpenSecretCloud/Maple PR: 372
File: frontend/src-tauri/Cargo.toml:9-9
Timestamp: 2026-01-08T17:09:40.432Z
Learning: The OpenSecretCloud/Maple repository has comprehensive GitHub Actions workflows (e.g., desktop-build.yml) that automatically validate compilation and testing, so manual compilation verification reminders are unnecessary when reviewing version bumps or dependency updates.

Applied to files:

  • .github/workflows/android-build.yml
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: build-rust (aarch64-linux-android, arm64-v8a, aarch64)
  • GitHub Check: build-rust (armv7-linux-androideabi, armeabi-v7a, armv7)
  • GitHub Check: build-rust (x86_64-linux-android, x86_64, x86_64)
  • GitHub Check: build-ios
  • GitHub Check: build-linux
  • GitHub Check: build-macos (universal-apple-darwin)
  • GitHub Check: Cloudflare Pages
🔇 Additional comments (9)
.github/workflows/android-build.yml (9)

9-13: LGTM: Environment configuration is correct.

The conditional API URL configuration appropriately routes PRs to staging/dev environments and pushes to production endpoints.


55-77: LGTM: Matrix strategy is well-configured.

The parallel build strategy with fail-fast: false allows all Android targets to build independently, maximizing the benefit of the 8-core runner and providing comprehensive build results even if one target fails.


86-102: LGTM: Per-target sccache configuration optimizes parallel builds.

The per-target cache keys with fallbacks enable effective cache sharing while maintaining isolation, which is ideal for the parallelized matrix strategy.


162-178: LGTM: Efficient Tauri CLI installation strategy.

Installing Tauri CLI only for the aarch64 job (to generate shared gradle files) avoids redundant installations across all four matrix jobs, saving build time.


205-218: LGTM: Rust library build and artifact upload are correctly configured.

The per-target artifact naming strategy (android-lib-{short_name}) facilitates clean artifact retrieval in the packaging stage.


253-269: LGTM: Artifact downloads are properly configured.

The pattern-based download for native libraries (android-lib-*) efficiently retrieves all four target libraries in a single step.


286-319: LGTM: Signing and packaging steps are correctly configured.

The workflow properly handles Android app signing and builds both APK and AAB artifacts. The use of RUNNER_TEMP for the keystore ensures automatic cleanup.


321-333: LGTM: Final artifact uploads are properly configured.

The 5-day retention for final APK and AAB artifacts (vs. 1-day for intermediate artifacts) appropriately balances storage costs with the need to access release builds.


17-17: The ubuntu-latest-8-cores runner is a GitHub-provided label for larger hosted runners, not a custom runner requiring manual setup. It's already being used across multiple workflows in the repository (android-build.yml, release.yml, testflight-on-comment.yml, desktop-build.yml, and claude.yml), indicating the organization already has access to the larger runners feature. No additional setup or creation is needed.

Likely an incorrect or invalid review comment.

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

Greptile Overview

Greptile Summary

Upgrades all GitHub Actions workflows to use larger runners—macos-latest-xlarge (M2, 5 cores + 8 GPU, 14GB RAM) for macOS jobs and ubuntu-latest-8-cores (8 CPU, 32GB RAM, 300GB SSD) for Linux jobs—to accelerate build times across desktop, mobile, Android, release, and automation workflows. The android-build.yml was additionally restructured into a parallel job architecture with separate stages for frontend build, Gradle file generation, parallel Rust compilation across four Android targets, and final packaging.

Confidence Score: 4/5

  • Safe to merge with minor optimization opportunities identified
  • The runner upgrades are straightforward infrastructure changes with no logic modifications. One potential issue was identified: the build-rust job in android-build.yml depends on build-frontend but doesn't use the artifact, which could indicate either missing artifact download or unnecessary dependency causing sequential execution instead of parallelization. Additionally, the permission-checking job in testflight-on-comment.yml uses the 8-core runner for minimal API work, which is wasteful but not blocking. The macOS macos-latest-xlarge runner works automatically, but the custom Ubuntu runner must be created before merge as documented.
  • .github/workflows/android-build.yml requires verification that the dependency between build-rust and build-frontend is intentional

Important Files Changed

File Analysis

Filename Score Overview
.github/workflows/android-build.yml 4/5 Restructures Android CI to use ubuntu-latest-8-cores runners with parallel job architecture; splits builds into frontend, Gradle generation, parallel Rust targets, and packaging stages

Sequence Diagram

sequenceDiagram
    participant Trigger as Workflow Trigger
    participant macOS as macos-latest-xlarge<br/>(M2, 5 cores, 14GB)
    participant Linux as ubuntu-latest-8-cores<br/>(8 CPU, 32GB, 300GB SSD)
    
    Note over Trigger,Linux: Desktop & Mobile Builds
    Trigger->>macOS: "Build universal macOS app"
    Trigger->>Linux: "Build Linux app"
    macOS-->>Trigger: "DMG + .app artifacts"
    Linux-->>Trigger: "AppImage + .deb artifacts"
    
    Note over Trigger,Linux: Android Build (Parallel Architecture)
    Trigger->>Linux: "Build frontend once"
    Linux->>Linux: "Generate Gradle files"
    par Parallel Rust Compilation
        Linux->>Linux: "Build aarch64 target"
        Linux->>Linux: "Build armv7 target"
        Linux->>Linux: "Build i686 target"
        Linux->>Linux: "Build x86_64 target"
    end
    Linux->>Linux: "Package APK + AAB"
    Linux-->>Trigger: "Android artifacts"
    
    Note over Trigger,Linux: Release Workflow
    Trigger->>macOS: "Build signed macOS release"
    Trigger->>Linux: "Build signed Linux release"
    Trigger->>Linux: "Build signed Android release"
    macOS-->>Trigger: "macOS release assets"
    Linux-->>Trigger: "Linux + Android assets"
    Linux->>Linux: "Generate latest.json"
Loading

check-comment:
if: github.event.issue.pull_request && contains(github.event.comment.body, 'testflight build')
runs-on: ubuntu-latest
runs-on: ubuntu-latest-8-cores
Copy link
Contributor

Choose a reason for hiding this comment

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

The check-comment job uses ubuntu-latest-8-cores, which will require the custom runner to be created in the organization settings as noted in the PR description. This job performs minimal work (permission checking via GitHub API), so the 8-core runner may be overkill here—the standard ubuntu-latest would likely suffice and avoid the setup requirement for this specific job.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: .github/workflows/testflight-on-comment.yml
Line: 15:15

Comment:
The `check-comment` job uses `ubuntu-latest-8-cores`, which will require the custom runner to be created in the organization settings as noted in the PR description. This job performs minimal work (permission checking via GitHub API), so the 8-core runner may be overkill here—the standard `ubuntu-latest` would likely suffice and avoid the setup requirement for this specific job.

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

How can I resolve this? If you propose a fix, please make it concise.

# Build each Rust target in parallel
build-rust:
runs-on: ubuntu-latest-8-cores
needs: build-frontend
Copy link
Contributor

Choose a reason for hiding this comment

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

The build-rust job depends on build-frontend but doesn't download or use the frontend artifact. If the frontend build is required for the Rust build to succeed, add a download step. If not needed, consider removing the dependency to allow both jobs to run in parallel for faster execution.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: .github/workflows/android-build.yml
Line: 120:120

Comment:
The `build-rust` job depends on `build-frontend` but doesn't download or use the frontend artifact. If the frontend build is required for the Rust build to succeed, add a download step. If not needed, consider removing the dependency to allow both jobs to run in parallel for faster execution.

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

How can I resolve this? If you propose a fix, please make it concise.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
.github/workflows/android-build.yml (1)

120-120: Remove unnecessary dependency on build-frontend.

The build-rust job declares needs: build-frontend but never downloads or uses the frontend-dist artifact. This dependency forces sequential execution and delays the Rust builds.

Consider removing this dependency to allow build-rust and build-frontend to run in parallel, reducing overall CI time.

♻️ Proposed fix
   build-rust:
     runs-on: ubuntu-latest-8-cores
-    needs: build-frontend
     strategy:
       fail-fast: false
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e7670fb and bec0bbe.

📒 Files selected for processing (1)
  • .github/workflows/android-build.yml
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: AnthonyRonning
Repo: OpenSecretCloud/Maple PR: 372
File: frontend/src-tauri/Cargo.toml:9-9
Timestamp: 2026-01-08T17:09:40.432Z
Learning: The OpenSecretCloud/Maple repository has comprehensive GitHub Actions workflows (e.g., desktop-build.yml) that automatically validate compilation and testing, so manual compilation verification reminders are unnecessary when reviewing version bumps or dependency updates.
📚 Learning: 2026-01-08T17:09:40.432Z
Learnt from: AnthonyRonning
Repo: OpenSecretCloud/Maple PR: 372
File: frontend/src-tauri/Cargo.toml:9-9
Timestamp: 2026-01-08T17:09:40.432Z
Learning: The OpenSecretCloud/Maple repository has comprehensive GitHub Actions workflows (e.g., desktop-build.yml) that automatically validate compilation and testing, so manual compilation verification reminders are unnecessary when reviewing version bumps or dependency updates.

Applied to files:

  • .github/workflows/android-build.yml
📚 Learning: 2025-12-08T19:55:33.330Z
Learnt from: CR
Repo: OpenSecretCloud/Maple PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-08T19:55:33.330Z
Learning: Applies to src-tauri/**/*.rs : Run `just rust-lint` after modifying Rust code to ensure proper formatting, compilation, and linting (runs fmt + clippy strict)

Applied to files:

  • .github/workflows/android-build.yml
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: build-rust (i686-linux-android, x86, i686)
  • GitHub Check: build-rust (armv7-linux-androideabi, armeabi-v7a, armv7)
  • GitHub Check: build-rust (aarch64-linux-android, arm64-v8a, aarch64)
  • GitHub Check: build-macos (universal-apple-darwin)
  • GitHub Check: generate-gradle-files
  • GitHub Check: build-linux
  • GitHub Check: build-ios
  • GitHub Check: Cloudflare Pages
🔇 Additional comments (8)
.github/workflows/android-build.yml (8)

9-12: Good environment variable setup for multi-environment builds.

The conditional environment variables correctly route PRs to dev endpoints and pushes to production endpoints.


184-226: Cross-compilation environment setup is comprehensive.

The per-target configuration of LINKER, AR, CC, CXX, and RANLIB environment variables correctly sets up cross-compilation for each Android ABI.


160-177: Good caching strategy with per-target sccache isolation.

The per-target sccache caching (using matrix.short_name) prevents cache conflicts between different Android ABIs while the shared cargo registry cache reduces redundant downloads.


246-246: Correct job dependencies ensure artifact availability.

The package-android job properly depends on both build-rust (which completes after all matrix targets) and generate-gradle-files, ensuring all required artifacts are available before packaging.


294-307: Native library placement logic is clear and verifiable.

The step systematically places each target's .so files into the correct JNI ABI directories and includes verification output for debugging.


335-342: Parallel Gradle build optimizes packaging time.

Using --parallel for both assembleUniversalRelease and bundleUniversalRelease takes advantage of Gradle's parallel execution capabilities to reduce packaging time.


17-17: Verify custom runner availability before merging.

The workflow uses ubuntu-latest-8-cores on lines 17, 53, 119, and 245, which is a custom GitHub-hosted runner that must be created in organization settings before merging (8 CPU, 32GB RAM, 300GB SSD, Ubuntu 22.04 or 24.04).


88-108: Consider updating Tauri CLI and investigating dedicated gradle initialization.

The workflow uses Tauri CLI 2.9.2, but the latest stable version is 2.9.5 (released Nov 30, 2025). Additionally, a dedicated tauri android init command exists in the Tauri CLI, though it's primarily documented for Android project initialization rather than gradle file regeneration. Verify whether this command can replace the current approach of running a build and letting it fail after gradle file generation.

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

Greptile Overview

Greptile Summary

This PR upgrades all GitHub Actions workflows to use larger runners for faster builds. The changes include straightforward runner upgrades across 5 workflows, plus a major architectural refactoring of the Android build pipeline.

Key Changes:

  • Runner Upgrades: All workflows now use ubuntu-latest-8-cores (8 CPU, 32GB RAM) for Linux and macos-latest-xlarge (12 CPU, 30GB RAM) for macOS
  • Android Build Refactoring: Transformed from a single monolithic job to a 4-job pipeline with parallel Rust compilation per Android ABI (aarch64, armv7, i686, x86_64)
  • Artifact-based Build: Frontend, gradle files, and native libraries are now shared via GitHub artifacts between jobs

Runner Setup Clarification Needed:
The PR description instructs to "create the Ubuntu larger runner in your org settings," suggesting manual setup of an organization-hosted runner. However, ubuntu-latest-8-cores and macos-latest-xlarge are official GitHub-hosted larger runner names that work automatically on GitHub Team/Enterprise plans. Please clarify:

  • Are you using GitHub-hosted larger runners (no setup needed)?
  • Or creating custom organization-hosted runners with these names (setup required)?

If using GitHub-hosted larger runners, the setup instructions in the PR description are unnecessary and could cause confusion.

Confidence Score: 4/5

  • This PR is safe to merge with minor clarification needed on runner setup
  • The code changes are well-structured and the Android build refactoring is architecturally sound. Runner upgrades are consistent across all workflows. The main concern is ambiguity in the PR description about whether these are GitHub-hosted or organization-hosted runners, which could lead to confusion during setup. The android-build.yml has one minor inefficiency (duplicate frontend builds), but this doesn't affect correctness.
  • Pay attention to .github/workflows/android-build.yml due to its major architectural changes and complexity

Important Files Changed

File Analysis

Filename Score Overview
.github/workflows/android-build.yml 4/5 Major refactoring from single job to multi-job pipeline with parallel builds. Upgraded to ubuntu-latest-8-cores. Architecture is sound but has minor workflow duplication issues.
.github/workflows/desktop-build.yml 5/5 Upgraded macOS job to macos-latest-xlarge and Linux job to ubuntu-latest-8-cores. Straightforward runner upgrades with no logic changes.
.github/workflows/release.yml 5/5 Upgraded all jobs to larger runners (macos-latest-xlarge and ubuntu-latest-8-cores). Updated platform conditionals consistently throughout. No issues found.

Sequence Diagram

sequenceDiagram
    participant Trigger as GitHub Event
    participant Frontend as build-frontend
    participant Gradle as generate-gradle-files
    participant Rust1 as build-rust (aarch64)
    participant Rust2 as build-rust (armv7)
    participant Rust3 as build-rust (i686)
    participant Rust4 as build-rust (x86_64)
    participant Package as package-android
    participant Artifacts as GitHub Artifacts

    Trigger->>Frontend: Start (ubuntu-latest-8-cores)
    Trigger->>Gradle: Start (ubuntu-latest-8-cores)
    
    Note over Frontend: Install Bun deps<br/>Build frontend dist
    Frontend->>Artifacts: Upload frontend-dist
    
    Note over Gradle: Install deps & Tauri CLI<br/>Run cargo tauri android build<br/>Extract gradle files
    Gradle->>Artifacts: Upload tauri-gradle-files
    
    Frontend->>Rust1: Trigger (ubuntu-latest-8-cores)
    Frontend->>Rust2: Trigger (ubuntu-latest-8-cores)
    Frontend->>Rust3: Trigger (ubuntu-latest-8-cores)
    Frontend->>Rust4: Trigger (ubuntu-latest-8-cores)
    
    Note over Rust1,Rust4: Parallel Rust compilation<br/>with sccache per-target
    
    Rust1->>Artifacts: Upload android-lib-aarch64
    Rust2->>Artifacts: Upload android-lib-armv7
    Rust3->>Artifacts: Upload android-lib-i686
    Rust4->>Artifacts: Upload android-lib-x86_64
    
    Rust1->>Package: Complete
    Rust2->>Package: Complete
    Rust3->>Package: Complete
    Rust4->>Package: Complete
    Gradle->>Package: Complete
    
    Package->>Artifacts: Download frontend-dist
    Package->>Artifacts: Download all android-lib-*
    Package->>Artifacts: Download tauri-gradle-files
    
    Note over Package: Place files in correct dirs<br/>Setup signing<br/>Run Gradle build
    
    Package->>Artifacts: Upload maple-android-apk
    Package->>Artifacts: Upload maple-android-aab
Loading

Comment on lines 95 to 99
- name: Generate Tauri gradle files
working-directory: ./frontend
run: |
# Run tauri android build - it will generate gradle files then fail at Rust compile (expected)
cargo tauri android build 2>&1 || true
Copy link
Contributor

Choose a reason for hiding this comment

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

The generate-gradle-files job duplicates work by building the frontend independently, while build-frontend runs in parallel. Since Tauri's android build command internally builds the frontend before generating gradle files, this job will waste CI time rebuilding what's already built in build-frontend.

Consider making generate-gradle-files depend on build-frontend and downloading the frontend-dist artifact, or accept the duplication as a tradeoff for parallel execution speed.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: .github/workflows/android-build.yml
Line: 95:99

Comment:
The `generate-gradle-files` job duplicates work by building the frontend independently, while `build-frontend` runs in parallel. Since Tauri's android build command internally builds the frontend before generating gradle files, this job will waste CI time rebuilding what's already built in `build-frontend`.

Consider making `generate-gradle-files` depend on `build-frontend` and downloading the frontend-dist artifact, or accept the duplication as a tradeoff for parallel execution speed.

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

How can I resolve this? If you propose a fix, please make it concise.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In @.github/workflows/android-build.yml:
- Around line 15-50: The upload-artifact step for "Upload frontend dist"
currently may warn instead of failing when frontend/dist is missing; update that
step (the actions/upload-artifact@v4 usage named "Upload frontend dist") to
include the if-no-files-found: error option so the job fails fast if the built
artifact directory isn't present.
- Around line 243-248: The upload steps "Upload native library" and "Upload
Android AAB" currently allow missing files silently; add if-no-files-found:
error to both upload-artifact actions so the job fails fast on missing
artifacts. Also relocate the "Cache Gradle" step so it runs after the "Place
Tauri gradle files" step (ensuring the cache key hashes the populated gradle
files rather than an empty directory) to avoid sticky/incorrect cache hits; keep
the same cache key logic but compute it after the gradle files are placed.
- Around line 75-123: The cache/restore-keys and version check are too weak:
tighten the cache key and implement an explicit version check for cargo-tauri
instead of just command -v; replace the loose restore-keys prefix with a
deterministic key including the tauri-cli version (e.g.,
runner.os-cargo-bin-tauri-cli-2.9.2) or remove restore-keys, then change the
install step to run cargo-tauri --version and compare its output to the expected
"tauri-cli 2.9.2" (if it differs or command fails, run cargo install tauri-cli
--version "2.9.2" --locked --force); finally add if-no-files-found: error to the
actions/upload-artifact@v4 step to fail early when
frontend/src-tauri/gen/android files are missing.
🧹 Nitpick comments (2)
.github/workflows/android-build.yml (2)

9-12: Make the “PR vs push” endpoint selection explicit (and future-proof).
The &&/|| expression works, but it’s easy to misread/extend later (e.g., adding workflow_dispatch). Consider a tiny comment or switching to a clearer expression style to avoid accidental prod/dev flips.


124-129: Unblock parallelism: don’t gate build-rust on build-frontend; instead make package-android depend on all producers explicitly.
Right now build-rust waits on build-frontend even though it doesn’t consume its artifact; it’s only enforcing ordering indirectly.

Proposed needs-graph tweak
   build-rust:
     runs-on: ubuntu-latest-8-cores
-    needs: build-frontend
     strategy:
       fail-fast: false
@@
   package-android:
     runs-on: ubuntu-latest-8-cores
-    needs: [build-rust, generate-gradle-files]
+    needs: [build-frontend, build-rust, generate-gradle-files]

Also applies to: 250-254

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bec0bbe and d2006e2.

📒 Files selected for processing (1)
  • .github/workflows/android-build.yml
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: AnthonyRonning
Repo: OpenSecretCloud/Maple PR: 372
File: frontend/src-tauri/Cargo.toml:9-9
Timestamp: 2026-01-08T17:09:40.432Z
Learning: The OpenSecretCloud/Maple repository has comprehensive GitHub Actions workflows (e.g., desktop-build.yml) that automatically validate compilation and testing, so manual compilation verification reminders are unnecessary when reviewing version bumps or dependency updates.
📚 Learning: 2026-01-08T17:09:40.432Z
Learnt from: AnthonyRonning
Repo: OpenSecretCloud/Maple PR: 372
File: frontend/src-tauri/Cargo.toml:9-9
Timestamp: 2026-01-08T17:09:40.432Z
Learning: The OpenSecretCloud/Maple repository has comprehensive GitHub Actions workflows (e.g., desktop-build.yml) that automatically validate compilation and testing, so manual compilation verification reminders are unnecessary when reviewing version bumps or dependency updates.

Applied to files:

  • .github/workflows/android-build.yml
📚 Learning: 2025-12-08T19:55:33.330Z
Learnt from: CR
Repo: OpenSecretCloud/Maple PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-08T19:55:33.330Z
Learning: Applies to src-tauri/**/*.rs : Run `just rust-lint` after modifying Rust code to ensure proper formatting, compilation, and linting (runs fmt + clippy strict)

Applied to files:

  • .github/workflows/android-build.yml
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: build-rust (aarch64-linux-android, arm64-v8a, aarch64)
  • GitHub Check: build-rust (x86_64-linux-android, x86_64, x86_64)
  • GitHub Check: build-rust (armv7-linux-androideabi, armeabi-v7a, armv7)
  • GitHub Check: build-rust (i686-linux-android, x86, i686)
  • GitHub Check: build-linux
  • GitHub Check: build-macos (universal-apple-darwin)
  • GitHub Check: generate-gradle-files
  • GitHub Check: build-ios
  • GitHub Check: Cloudflare Pages

Comment on lines 15 to 50
# Build frontend once, shared by all jobs
build-frontend:
runs-on: ubuntu-latest-8-cores
steps:
- uses: actions/checkout@v4

- name: Setup Java
uses: actions/setup-java@v4
- name: Setup Bun
uses: oven-sh/setup-bun@v1
with:
distribution: 'temurin'
java-version: '21'
bun-version: 1.2.2

- name: Setup Android SDK
uses: android-actions/setup-android@v3
- name: Cache Bun dependencies
uses: actions/cache@v4
with:
path: |
~/.bun/install/cache
frontend/node_modules
key: ${{ runner.os }}-bun-${{ hashFiles('frontend/bun.lockb') }}
restore-keys: |
${{ runner.os }}-bun-
- name: Install frontend dependencies
working-directory: ./frontend
run: bun install

- name: Build frontend
working-directory: ./frontend
run: bun run build

- name: Upload frontend dist
uses: actions/upload-artifact@v4
with:
name: frontend-dist
path: frontend/dist
retention-days: 1

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

ubuntu-latest-8-cores GitHub Actions runner label

💡 Result:

"ubuntu-latest-8-cores" is a GitHub-hosted larger runner label that schedules your job on an Ubuntu image with 8 CPU cores (the "ubuntu – latest" image). Add it to a workflow with runs-on: ubuntu-latest-8-cores. This label is provided via GitHub's "larger runners" offering (available to customers/organizations who have the larger-runner group enabled). [1][2]

Example:
runs-on: ubuntu-latest-8-cores

Sources: GitHub changelog (larger hosted runners) and GitHub Actions docs. [1] [2]


🏁 Script executed:

# Check if there are any runner group or custom runner configurations in the repository
git ls-files | grep -E "(runner|actions)" | head -20

Repository: OpenSecretCloud/Maple

Length of output: 47


🏁 Script executed:

# Look for any GitHub Actions workflow configuration files
fd -e yml -e yaml .github/workflows/ | head -20

Repository: OpenSecretCloud/Maple

Length of output: 442


🏁 Script executed:

# Check other workflow files for runner usage patterns
rg "runs-on:" .github/workflows/ -A 1 -B 1

Repository: OpenSecretCloud/Maple

Length of output: 2624


🌐 Web query:

actions/upload-artifact@v4 if-no-files-found parameter documentation

💡 Result:

The actions/upload-artifact@v4 input if-no-files-found controls what happens when the provided path matches no files. Valid values:

  • warn (default): emit a warning but do not fail the step. [1][2]
  • error: fail the step with an error. [1][2]
  • ignore: suppress warnings/errors and do not fail the step. [1][2]

Example usage:

  • uses: actions/upload-artifact@v4
    with:
    name: my-artifact
    path: path/to/artifact/
    if-no-files-found: error. [1]

Sources: GitHub actions/upload-artifact README and GitHub Actions docs. [1][2]


🏁 Script executed:

# Check the android-build.yml file to see full context of upload-artifact usage
cat -n .github/workflows/android-build.yml | sed -n '40,50p'

Repository: OpenSecretCloud/Maple

Length of output: 415


Consider failing fast if the frontend build output is missing.

Add if-no-files-found: error to the upload-artifact step to ensure the workflow fails immediately if frontend/dist isn't generated, rather than silently warning.

Proposed change
       - name: Upload frontend dist
         uses: actions/upload-artifact@v4
         with:
           name: frontend-dist
           path: frontend/dist
           retention-days: 1
+          if-no-files-found: error
🤖 Prompt for AI Agents
In @.github/workflows/android-build.yml around lines 15 - 50, The
upload-artifact step for "Upload frontend dist" currently may warn instead of
failing when frontend/dist is missing; update that step (the
actions/upload-artifact@v4 usage named "Upload frontend dist") to include the
if-no-files-found: error option so the job fails fast if the built artifact
directory isn't present.

Comment on lines 75 to 185
- name: Cache Cargo bin (Tauri CLI)
uses: actions/cache@v4
with:
targets: aarch64-linux-android,armv7-linux-androideabi,x86_64-linux-android,i686-linux-android
path: ~/.cargo/bin/
key: ${{ runner.os }}-cargo-bin-tauri-cli-2.9.2
restore-keys: |
${{ runner.os }}-cargo-bin-tauri-cli-
- name: Install frontend dependencies
working-directory: ./frontend
run: bun install

- name: Install Tauri CLI
run: |
if ! command -v cargo-tauri &> /dev/null; then
cargo install tauri-cli --version "2.9.2" --locked
fi
env:
CARGO_CFG_TARGET_OS: linux

- name: Generate Tauri gradle files
working-directory: ./frontend
run: |
# Run tauri android build - it will generate gradle files then fail at Rust compile (expected)
cargo tauri android build 2>&1 || true
# Verify the files were generated
if [ -f "src-tauri/gen/android/tauri.settings.gradle" ] && [ -f "src-tauri/gen/android/app/tauri.build.gradle.kts" ]; then
echo "=== tauri.settings.gradle ==="
cat src-tauri/gen/android/tauri.settings.gradle
echo ""
echo "=== tauri.build.gradle.kts ==="
cat src-tauri/gen/android/app/tauri.build.gradle.kts
else
echo "ERROR: Tauri gradle files were not generated"
ls -la src-tauri/gen/android/
ls -la src-tauri/gen/android/app/ || true
exit 1
fi
- name: Upload Tauri gradle files
uses: actions/upload-artifact@v4
with:
name: tauri-gradle-files
path: |
frontend/src-tauri/gen/android/tauri.settings.gradle
frontend/src-tauri/gen/android/app/tauri.build.gradle.kts
retention-days: 1

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

Tauri CLI 2.9.x cargo tauri android build gradle file generation requirements Java Android SDK NDK

💡 Result:

Short answer — for Tauri CLI v2.9.x (cargo tauri android build) you need:

  • Android Studio (use its bundled JBR or a standalone JDK). Set JAVA_HOME to the JBR/JDK path. Java 17 is the recommended runtime (templates updated to Gradle 8.9/AGP 8.5.1 add Java 21 support, but Java 17 remains recommended). [1][2]

  • Android SDK components (install via Android Studio SDK Manager): Android SDK Platform(s), Platform-Tools, Build-Tools, and Command-line Tools. Configure ANDROID_HOME to your SDK path. [1]

  • Android NDK (Side-by-side). Install via SDK Manager and set NDK_HOME to the NDK folder (e.g. $ANDROID_HOME/ndk/). Tauri expects an NDK side-by-side installation. [1]

  • Rust Android targets added via rustup: aarch64-linux-android, armv7-linux-androideabi, i686-linux-android, x86_64-linux-android. [1]

  • Gradle / Android Gradle Plugin used in templates: Gradle 8.9 and AGP 8.5.1 (so use a matching Android Studio and toolchain). [2]

Notes:

  • Min Android API default is 24 (Android 7.0); you can change minSdkVersion in tauri config if needed. [3]

Sources:
[1] Tauri Prerequisites — Android (official docs).
[2] Tauri CLI release notes — Gradle/AGP update (templates).
[3] Tauri Google Play / Android build notes (minSdkVersion).


🌐 Web query:

actions/cache restore-keys how does it work version mismatch risk

💡 Result:

Short answer

  • restore-keys are tried only after the primary key misses, checked in the listed order, and matched by prefix (most-recent matching cache wins). [1]
  • caches include a cache "version" (metadata based on the cached paths and compression/OS) so a cache created with a different compression/tool or different paths/OS will not be considered a valid match — this prevents decompress/format mismatches. [1][2]

How restore-keys matching works (behavior)

    1. Look for an exact match of key in the branch; 2) look for prefix matches of key; 3) if still none, check each restore-key in order for exact and then prefix matches; 4) if nothing in the branch, repeat the same search on the default branch. If multiple partial matches exist for a restore-key, the most-recent cache is used. The action sets outputs.cache-hit = true only for an exact key match. [1]

Version-mismatch risk

  • The cache "version" stamps the archive with the compression/tool and the set of paths. If the version differs (e.g., cache created on Windows vs Linux, different compression, or different paths), the cache will be considered a different entry and won't restore — preventing decompression/incompatibility errors. However, using broad restore-keys can still restore an older cache that is functionally incompatible (stale dependencies, different toolchain versions), which can cause build or runtime failures. [1][2]

Mitigations

  • Include file hashes or tool versions in your primary key (e.g., hashFiles('package-lock.json') or ${runner.os}-${{ hashFiles('...') }}). [1]
  • Add explicit tool/version strings to the key (node/npm/python versions), or separate caches per OS.
  • Use restore-keys only for safe, backward-compatible fallbacks (e.g., to speed installs), not for binary/compiled artifacts across major-version changes.
  • Check steps.cache.outputs.cache-hit and run a fresh install or rebuild when cache-hit != 'true', or validate restored artifacts before reuse.
  • Pin/update actions/cache runner compatibility (use recent actions/cache versions) to avoid service/format changes. [1][2]

Sources
[1] GitHub Docs — Dependency caching reference / Cache key matching.
[2] actions/cache repository / cache version explanation.


🌐 Web query:

cargo install force reinstall version check behavior

💡 Result:

Short answer: cargo install normally skips rebuilding if it thinks the installed package is "up‑to‑date" (it compares package version & source, installed binary names, chosen features, profile/target, etc.). Using --force makes cargo unconditionally rebuild and overwrite the installed binaries (i.e., it will reinstall even the same version). There is also an unstable install-upgrade mode that changes the up‑to‑date behavior. [1][2][3]

Sources:

  • cargo install docs / man page (behavior & --force). [1][2]
  • note about the unstable -Z install-upgrade behavior. [3]

Fix tauri-cli cache/version mismatch risk. Broad restore-keys prefixes can resurrect older ~/.cargo/bin/ caches, and checking only command -v cargo-tauri won't detect a stale version.

Replace the version check with an explicit version comparison:

Proposed fix
      - name: Install Tauri CLI
        run: |
-          if ! command -v cargo-tauri &> /dev/null; then
-            cargo install tauri-cli --version "2.9.2" --locked
-          fi
+          set -euo pipefail
+          WANT="2.9.2"
+          HAVE="$(cargo tauri --version 2>/dev/null || true)"
+          if ! command -v cargo-tauri >/dev/null 2>&1 || ! printf '%s' "$HAVE" | grep -q "$WANT"; then
+            cargo install tauri-cli --version "$WANT" --locked --force
+          fi
         env:
           CARGO_CFG_TARGET_OS: linux

Also add if-no-files-found: error to the gradle artifact upload to catch generation failures earlier:

Proposed hardening
      - name: Upload Tauri gradle files
        uses: actions/upload-artifact@v4
        with:
          name: tauri-gradle-files
          path: |
            frontend/src-tauri/gen/android/tauri.settings.gradle
            frontend/src-tauri/gen/android/app/tauri.build.gradle.kts
           retention-days: 1
+          if-no-files-found: error
🤖 Prompt for AI Agents
In @.github/workflows/android-build.yml around lines 75 - 123, The
cache/restore-keys and version check are too weak: tighten the cache key and
implement an explicit version check for cargo-tauri instead of just command -v;
replace the loose restore-keys prefix with a deterministic key including the
tauri-cli version (e.g., runner.os-cargo-bin-tauri-cli-2.9.2) or remove
restore-keys, then change the install step to run cargo-tauri --version and
compare its output to the expected "tauri-cli 2.9.2" (if it differs or command
fails, run cargo install tauri-cli --version "2.9.2" --locked --force); finally
add if-no-files-found: error to the actions/upload-artifact@v4 step to fail
early when frontend/src-tauri/gen/android files are missing.

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

Greptile Overview

Greptile Summary

This PR successfully upgrades GitHub Actions workflows to use larger runners for improved build performance:

Runner Upgrades:

  • Linux: ubuntu-latestubuntu-latest-8-cores (8 CPU, 32GB RAM)
  • macOS: macos-latestmacos-latest-xlarge (M2, 5 cores + 8 GPU, 14GB RAM)

Key Changes:

  1. Android Build Refactor - The most significant change is the complete restructuring of android-build.yml from a single monolithic job to 4 parallel jobs:

    • build-frontend: Builds frontend once using Bun
    • generate-gradle-files: Generates Tauri Android metadata
    • build-rust: Builds native libraries for 4 ABIs in parallel (aarch64, armv7, i686, x86_64)
    • package-android: Assembles everything into APK/AAB with Gradle

    This architecture significantly improves parallelization and should drastically reduce build times.

  2. RustPlugin.kt Enhancement - Adds skipRustBuild flag support to allow Gradle packaging without Rust compilation, which is essential for the new parallel workflow.

  3. Environment Variables - Moves VITE_* environment variables to workflow-level scope in android-build.yml for better organization.

Setup Required:
As noted in the PR description, the ubuntu-latest-8-cores runner must be manually created in the organization's GitHub Actions settings before merging. The macos-latest-xlarge runner is available by default.

Issues Found:

  • Missing SCCACHE_DIR environment variable in release.yml build-tauri job (affects Linux caching)
  • Implicit dependency in android-build.yml package-android job (style issue, not blocking)

Confidence Score: 4/5

  • This PR is safe to merge after addressing the sccache configuration issue in release.yml
  • Score reflects one logic issue (missing SCCACHE_DIR in release.yml) that should be fixed before merging, and one minor style issue (implicit dependency). The Android workflow refactor is well-designed and the RustPlugin changes are correct. The runner upgrades are straightforward. Main concern is ensuring the ubuntu-latest-8-cores runner is created before merging.
  • Pay attention to .github/workflows/release.yml - the missing SCCACHE_DIR will impact Linux build caching effectiveness

Important Files Changed

File Analysis

Filename Score Overview
.github/workflows/android-build.yml 4/5 Major refactor: splits Android build into 4 parallel jobs (frontend, gradle generation, Rust builds per ABI, packaging). Adds top-level env vars and uses ubuntu-latest-8-cores runners. Package job has implicit dependency on build-frontend.
.github/workflows/release.yml 3/5 Upgrades all jobs to larger runners. Updates matrix platform references. Missing SCCACHE_DIR in build-tauri job for Linux (line 104-108), which could impact caching effectiveness.
frontend/src-tauri/gen/android/buildSrc/src/main/java/cloud/opensecret/maple/kotlin/RustPlugin.kt 5/5 Adds skipRustBuild flag support to allow Gradle packaging without Rust compilation. Correctly checks property in 3 different formats (String, Boolean, hasProperty). Essential for new parallel Android build workflow.

Sequence Diagram

sequenceDiagram
    participant GH as GitHub Event
    participant BF as build-frontend
    participant GG as generate-gradle-files
    participant BR as build-rust (4x parallel)
    participant PA as package-android
    
    GH->>BF: Trigger on push/PR
    GH->>GG: Trigger on push/PR
    
    BF->>BF: Install Bun deps
    BF->>BF: Build frontend
    BF->>BF: Upload frontend-dist artifact
    
    GG->>GG: Install Tauri CLI
    GG->>GG: Run tauri android build (fails intentionally)
    GG->>GG: Verify gradle files generated
    GG->>GG: Upload tauri-android-generated artifact
    
    BF-->>BR: Dependency (needs: build-frontend)
    BR->>BR: Download frontend-dist
    BR->>BR: Build Rust for aarch64/armv7/i686/x86_64
    BR->>BR: Upload android-lib-* artifacts (4x)
    
    BR-->>PA: Dependency
    GG-->>PA: Dependency
    PA->>PA: Download frontend-dist (implicit dep via build-rust)
    PA->>PA: Download all native libraries (4x)
    PA->>PA: Download gradle generated files
    PA->>PA: Place files in correct locations
    PA->>PA: Run Gradle with -PskipRustBuild=true
    PA->>PA: Upload APK and AAB
Loading

Comment on lines +283 to +327
package-android:
runs-on: ubuntu-latest-8-cores
needs: [build-rust, generate-gradle-files]
Copy link
Contributor

Choose a reason for hiding this comment

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

The package-android job downloads the frontend-dist artifact (line 328) but doesn't explicitly declare build-frontend as a dependency. While this works because build-rust depends on build-frontend, creating an implicit dependency chain, it's fragile.

If someone later modifies the workflow and removes the build-rust -> build-frontend dependency, the package-android job would fail when trying to download the artifact.

Consider making the dependency explicit:

Suggested change
package-android:
runs-on: ubuntu-latest-8-cores
needs: [build-rust, generate-gradle-files]
package-android:
runs-on: ubuntu-latest-8-cores
needs: [build-frontend, build-rust, generate-gradle-files]

This makes the workflow more maintainable and the dependency graph clearer.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: .github/workflows/android-build.yml
Line: 283:285

Comment:
The `package-android` job downloads the `frontend-dist` artifact (line 328) but doesn't explicitly declare `build-frontend` as a dependency. While this works because `build-rust` depends on `build-frontend`, creating an implicit dependency chain, it's fragile.

If someone later modifies the workflow and removes the `build-rust -> build-frontend` dependency, the `package-android` job would fail when trying to download the artifact.

Consider making the dependency explicit:

```suggestion
  package-android:
    runs-on: ubuntu-latest-8-cores
    needs: [build-frontend, build-rust, generate-gradle-files]
```

This makes the workflow more maintainable and the dependency graph clearer.

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

How can I resolve this? If you propose a fix, please make it concise.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Jan 9, 2026

Additional Comments (1)

.github/workflows/release.yml
The "Configure sccache" step is missing the SCCACHE_DIR environment variable for Linux builds. This is inconsistent with other workflows and could impact caching effectiveness.

The cache path is set conditionally on line 84 to use ~/.cache/sccache for Linux, but sccache on Linux needs SCCACHE_DIR to be explicitly set to use this path. Without it, sccache may use a different default location, causing cache misses.

Compare with:

  • build-android job in this file (line 262) which correctly sets SCCACHE_DIR
  • desktop-build.yml Linux job (line 180-183) which sets SCCACHE_DIR
  • android-build.yml all jobs which set SCCACHE_DIR
      - name: Configure sccache
        run: |
          echo "RUSTC_WRAPPER=sccache" >> $GITHUB_ENV
          echo "SCCACHE_CACHE_SIZE=2G" >> $GITHUB_ENV
          echo "SCCACHE_DIR=$HOME/.cache/sccache" >> $GITHUB_ENV

Note: macOS doesn't need this because sccache defaults to ~/Library/Caches/Mozilla.sccache on macOS, but we could add it conditionally if needed for clarity.

Prompt To Fix With AI
This is a comment left during a code review.
Path: .github/workflows/release.yml
Line: 104:108

Comment:
The "Configure sccache" step is missing the `SCCACHE_DIR` environment variable for Linux builds. This is inconsistent with other workflows and could impact caching effectiveness.

The cache path is set conditionally on line 84 to use `~/.cache/sccache` for Linux, but sccache on Linux needs `SCCACHE_DIR` to be explicitly set to use this path. Without it, sccache may use a different default location, causing cache misses.

Compare with:
- `build-android` job in this file (line 262) which correctly sets `SCCACHE_DIR`
- `desktop-build.yml` Linux job (line 180-183) which sets `SCCACHE_DIR`
- `android-build.yml` all jobs which set `SCCACHE_DIR`

```suggestion
      - name: Configure sccache
        run: |
          echo "RUSTC_WRAPPER=sccache" >> $GITHUB_ENV
          echo "SCCACHE_CACHE_SIZE=2G" >> $GITHUB_ENV
          echo "SCCACHE_DIR=$HOME/.cache/sccache" >> $GITHUB_ENV
```

Note: macOS doesn't need this because sccache defaults to `~/Library/Caches/Mozilla.sccache` on macOS, but we could add it conditionally if needed for clarity.

How can I resolve this? If you propose a fix, please make it concise.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

Caution

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

⚠️ Outside diff range comments (1)
.github/workflows/android-build.yml (1)

368-379: Gate signing and Release builds to push events only, or build unsigned Debug on PRs.

The package-android job runs on both push and pull_request (lines 3-7) without conditionals. The "Setup Android signing" step (lines 368-379) references secrets that are unavailable on fork PRs, causing the Release build (assembleUniversalRelease bundleUniversalRelease) to fail. External contributor PRs cannot access repository secrets, so this will consistently fail on forks.

This also applies to lines 394-402.

🤖 Fix all issues with AI agents
In @.github/workflows/android-build.yml:
- Around line 315-324: The cache key currently uses
hashFiles('frontend/src-tauri/gen/android/**/*.gradle*',
'frontend/src-tauri/gen/android/gradle-wrapper.properties') which can be empty
because generated Gradle files are produced later; change the key to rely only
on stable inputs (e.g., the gradle wrapper properties and any checked-in
lockfiles) and remove the generated globs from hashFiles so the key is stable on
first run—update the actions/cache step (the "Cache Gradle" job using
actions/cache@v4) to compute key from runner.os plus a hash of stable files like
gradle-wrapper.properties and repository lockfiles (or fall back to
runner.os-gradle- as needed) and keep restore-keys as-is.
- Around line 275-280: The upload step "Upload native library" currently uses
actions/upload-artifact@v4 with a glob that can match nothing and silently
continue; update the step to fail fast by adding the with key if-no-files-found
set to "error" (i.e., set if-no-files-found: error) so the job fails immediately
when frontend/src-tauri/target/${{ matrix.target }}/release/*.so is missing,
making root cause visible during CI.
- Around line 9-12: The environment variables VITE_OPEN_SECRET_API_URL and
VITE_MAPLE_BILLING_API_URL currently switch based on github.event_name ==
'pull_request', which may mis-route builds for pushes, workflow_dispatch, or
other events; either move these values to GitHub repository/environment
variables (recommended) or change the conditional to explicitly detect the
production branch (e.g., check github.ref == 'refs/heads/master' or a dedicated
prod branch) and handle workflow_dispatch/other events explicitly so that
VITE_OPEN_SECRET_API_URL and VITE_MAPLE_BILLING_API_URL resolve to the intended
prod or dev endpoints consistently.
- Around line 72-115: The step "Generate Tauri Android files" currently runs
"cargo tauri android build 2>&1 || true" which hides all failures; change it to
capture the command exit code (e.g., run "cargo tauri android build 2>&1;
build_exit=$?"), then verify the REQUIRED_FILES array (refer to REQUIRED_FILES
and the four generated paths) and only tolerate a non-zero build exit code if
all REQUIRED_FILES exist; otherwise fail with the original build exit code (or
exit 1) so genuine CLI/config errors are not swallowed.
- Around line 191-207: Replace the manual "Install sccache" run step with the
official action by using mozilla-actions/sccache-action@v0.0.9 and pass version:
"0.8.2" (remove the curl/tar/mv/chmod sequence), keep or adapt the existing
"Cache sccache" step as needed for ~/.cache/sccache, and ensure downstream build
steps set the environment variables SCCACHE_GHA_ENABLED: "true" and
RUSTC_WRAPPER: "sccache" so the action enables sccache as the rustc wrapper.
🧹 Nitpick comments (3)
.github/workflows/android-build.yml (3)

16-50: Frontend build looks solid; consider making installs deterministic.
bun install in CI is usually safer with --frozen-lockfile (prevents surprise lock changes / resolution drift).

Proposed diff
       - name: Install frontend dependencies
         working-directory: ./frontend
-        run: bun install
+        run: bun install --frozen-lockfile

75-94: Caching all of ~/.cargo/bin is broader than needed (can interact oddly with other Cargo-installed tools).
Since you only need the cargo-tauri shim, caching just that binary is typically enough.

Proposed diff
       - name: Cache Cargo bin (Tauri CLI)
         uses: actions/cache@v4
         with:
-          path: ~/.cargo/bin/
+          path: ~/.cargo/bin/cargo-tauri
           key: ${{ runner.os }}-cargo-bin-tauri-cli-2.9.2
           restore-keys: |
             ${{ runner.os }}-cargo-bin-tauri-cli-

208-217: Matrix cache contention: all 4 Rust jobs try to save the same Cargo registry cache key.
This often produces “cache already exists” noise and reduces cache usefulness. Consider making the key per-target, or use restore-only in matrix jobs and save once in a single job.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d2006e2 and 94a06d6.

⛔ Files ignored due to path filters (1)
  • frontend/src-tauri/gen/android/buildSrc/src/main/java/cloud/opensecret/maple/kotlin/RustPlugin.kt is excluded by !**/gen/**
📒 Files selected for processing (1)
  • .github/workflows/android-build.yml
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: AnthonyRonning
Repo: OpenSecretCloud/Maple PR: 372
File: frontend/src-tauri/Cargo.toml:9-9
Timestamp: 2026-01-08T17:09:40.432Z
Learning: The OpenSecretCloud/Maple repository has comprehensive GitHub Actions workflows (e.g., desktop-build.yml) that automatically validate compilation and testing, so manual compilation verification reminders are unnecessary when reviewing version bumps or dependency updates.
📚 Learning: 2026-01-08T17:09:40.432Z
Learnt from: AnthonyRonning
Repo: OpenSecretCloud/Maple PR: 372
File: frontend/src-tauri/Cargo.toml:9-9
Timestamp: 2026-01-08T17:09:40.432Z
Learning: The OpenSecretCloud/Maple repository has comprehensive GitHub Actions workflows (e.g., desktop-build.yml) that automatically validate compilation and testing, so manual compilation verification reminders are unnecessary when reviewing version bumps or dependency updates.

Applied to files:

  • .github/workflows/android-build.yml
📚 Learning: 2025-12-08T19:55:33.330Z
Learnt from: CR
Repo: OpenSecretCloud/Maple PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-08T19:55:33.330Z
Learning: Applies to src-tauri/**/*.rs : Run `just rust-lint` after modifying Rust code to ensure proper formatting, compilation, and linting (runs fmt + clippy strict)

Applied to files:

  • .github/workflows/android-build.yml
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: generate-gradle-files
  • GitHub Check: build-frontend
  • GitHub Check: build-macos (universal-apple-darwin)
  • GitHub Check: build-linux
  • GitHub Check: build-ios
  • GitHub Check: Cloudflare Pages
🔇 Additional comments (1)
.github/workflows/android-build.yml (1)

17-17: The ubuntu-latest-8-cores runner label syntax is correct per GitHub Actions documentation. This is a valid, built-in label for GitHub-hosted larger runners (8-core Ubuntu) and requires no syntax changes. Ensure your organization has a paid GitHub Actions plan with larger runners provisioned; otherwise, jobs will fail due to runner unavailability rather than label syntax errors.

Likely an incorrect or invalid review comment.

Comment on lines 191 to 243
- name: Install sccache
run: |
SCCACHE_VERSION=0.8.2
SCCACHE_URL="https://github.com/mozilla/sccache/releases/download/v${SCCACHE_VERSION}/sccache-v${SCCACHE_VERSION}-x86_64-unknown-linux-musl.tar.gz"
curl -L "$SCCACHE_URL" | tar xz
sudo mv sccache-v${SCCACHE_VERSION}-x86_64-unknown-linux-musl/sccache /usr/local/bin/
chmod +x /usr/local/bin/sccache
sccache --version
- name: Cache sccache
uses: actions/cache@v4
with:
path: ~/.cache/sccache
key: ${{ runner.os }}-sccache-android-${{ hashFiles('**/Cargo.lock') }}
key: ${{ runner.os }}-sccache-android-${{ matrix.short_name }}-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-sccache-android-${{ matrix.short_name }}-
${{ runner.os }}-sccache-android-
${{ runner.os }}-sccache-
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

mozilla sccache official GitHub Action installation

💡 Result:

Short answer — use the Mozilla Actions sccache-action. Example installation and usage:

  • Add the action step to your workflow (replace version as desired):

    • name: Run sccache-cache
      uses: mozilla-actions/sccache-action@v0.0.9
      with:
      version: "v0.10.0" # optional — >= v0.10.0 recommended
  • Enable sccache for Rust (set env in the job/step):
    env:
    SCCACHE_GHA_ENABLED: "true"
    RUSTC_WRAPPER: "sccache"

  • For C/C++ set SCCACHE_GHA_ENABLED and configure compiler launchers:
    env:
    SCCACHE_GHA_ENABLED: "true"
    and pass to CMake:
    -DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache
    or to configure:
    CC="sccache gcc" CXX="sccache gcc" (or clang)

  • On GHES provide a token via input if needed:
    with:
    token: ${{ secrets.MY_GITHUB_TOKEN }}

Sources: Mozilla-Actions sccache-action README and GitHub Marketplace listing. [1][2]

References
[1] mozilla-actions/sccache-action README.
[2] sccache Action — GitHub Marketplace.


🌐 Web query:

sccache release checksum verification GitHub Actions

💡 Result:

Yes — sccache releases on GitHub publish per-asset SHA256 lines (files named *.sha256). You can download the release asset and its .sha256, extract the hex hash, and verify with sha256sum in a GitHub Actions step.

Example (replace URLs with the release asset URL and its .sha256):

  • download asset and checksum
    • curl -L -o sccache.tar.gz "<ASSET_URL>"
    • curl -L -o sccache.tar.gz.sha256 "<ASSET_URL>.sha256"
  • extract 64‑char hash and verify against the downloaded file
    • HASH=$(grep -oE '[a-f0-9]{64}' sccache.tar.gz.sha256)
    • echo "$HASH sccache.tar.gz" > sum.txt
    • sha256sum --check sum.txt

GitHub Actions step example:

  • name: Download and verify sccache
    run: |
    curl -L -o sccache.tar.gz "<ASSET_URL>"
    curl -L -o sccache.tar.gz.sha256 "<ASSET_URL>.sha256"
    HASH=$(grep -oE '[a-f0-9]{64}' sccache.tar.gz.sha256)
    echo "$HASH sccache.tar.gz" > sum.txt
    sha256sum --check sum.txt

References: sccache releases (example shows .sha256 files and their contents). [1]
If you prefer using the official sccache Action for installation, see its repo/usage. [2]

Sources:
[1] mozilla/sccache releases (assets include .sha256 files).
[2] Mozilla-Actions/sccache-action (GitHub Action usage).


🏁 Script executed:

# Check if there are other sccache installations in the repository
rg -i "sccache" --type yaml

Repository: OpenSecretCloud/Maple

Length of output: 47


🏁 Script executed:

# Search more broadly for sccache references
rg "sccache" -i

Repository: OpenSecretCloud/Maple

Length of output: 47


🏁 Script executed:

# Also check the specific file to understand the context
cat -n .github/workflows/android-build.yml | sed -n '180,220p'

Repository: OpenSecretCloud/Maple

Length of output: 1773


Use the official mozilla-actions/sccache-action instead of manual download.
The current implementation downloads sccache v0.8.2 without integrity verification, creating a supply-chain risk. Mozilla maintains an official GitHub Action that handles installation securely; replace this step with:

- name: Install sccache
  uses: mozilla-actions/sccache-action@v0.0.9
  with:
    version: "0.8.2"

Then configure the environment variables (SCCACHE_GHA_ENABLED: "true" and RUSTC_WRAPPER: "sccache") for the build steps that follow.

🤖 Prompt for AI Agents
In @.github/workflows/android-build.yml around lines 191 - 207, Replace the
manual "Install sccache" run step with the official action by using
mozilla-actions/sccache-action@v0.0.9 and pass version: "0.8.2" (remove the
curl/tar/mv/chmod sequence), keep or adapt the existing "Cache sccache" step as
needed for ~/.cache/sccache, and ensure downstream build steps set the
environment variables SCCACHE_GHA_ENABLED: "true" and RUSTC_WRAPPER: "sccache"
so the action enables sccache as the rustc wrapper.

Comment on lines +275 to +322
- name: Upload native library
uses: actions/upload-artifact@v4
with:
name: android-lib-${{ matrix.short_name }}
path: frontend/src-tauri/target/${{ matrix.target }}/release/*.so
retention-days: 1
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fail fast if .so artifacts are missing.
upload-artifact can warn and continue if the glob matches nothing; failing here makes root cause clearer than later cp errors in packaging.

Proposed diff
       - name: Upload native library
         uses: actions/upload-artifact@v4
         with:
           name: android-lib-${{ matrix.short_name }}
           path: frontend/src-tauri/target/${{ matrix.target }}/release/*.so
+          if-no-files-found: error
           retention-days: 1
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Upload native library
uses: actions/upload-artifact@v4
with:
name: android-lib-${{ matrix.short_name }}
path: frontend/src-tauri/target/${{ matrix.target }}/release/*.so
retention-days: 1
- name: Upload native library
uses: actions/upload-artifact@v4
with:
name: android-lib-${{ matrix.short_name }}
path: frontend/src-tauri/target/${{ matrix.target }}/release/*.so
if-no-files-found: error
retention-days: 1
🤖 Prompt for AI Agents
In @.github/workflows/android-build.yml around lines 275 - 280, The upload step
"Upload native library" currently uses actions/upload-artifact@v4 with a glob
that can match nothing and silently continue; update the step to fail fast by
adding the with key if-no-files-found set to "error" (i.e., set
if-no-files-found: error) so the job fails immediately when
frontend/src-tauri/target/${{ matrix.target }}/release/*.so is missing, making
root cause visible during CI.

Comment on lines +315 to +366
- name: Cache Gradle
uses: actions/cache@v4
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('frontend/src-tauri/gen/android/**/*.gradle*', 'frontend/src-tauri/gen/android/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Gradle cache key likely hashes files that don’t exist yet in this job.
Because generated Gradle files are only placed later (after artifact download), hashFiles(...) here may be empty/unstable. Consider keying off stable inputs (e.g., wrapper properties + lockfiles) instead.

🤖 Prompt for AI Agents
In @.github/workflows/android-build.yml around lines 315 - 324, The cache key
currently uses hashFiles('frontend/src-tauri/gen/android/**/*.gradle*',
'frontend/src-tauri/gen/android/gradle-wrapper.properties') which can be empty
because generated Gradle files are produced later; change the key to rely only
on stable inputs (e.g., the gradle wrapper properties and any checked-in
lockfiles) and remove the generated globs from hashFiles so the key is stable on
first run—update the actions/cache step (the "Cache Gradle" job using
actions/cache@v4) to compute key from runner.os plus a hash of stable files like
gradle-wrapper.properties and repository lockfiles (or fall back to
runner.os-gradle- as needed) and keep restore-keys as-is.

@AnthonyRonning AnthonyRonning changed the title ci: upgrade to larger GitHub Actions runners ci: Refactor android build pipeline Jan 9, 2026
Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

Greptile Overview

Greptile Summary

This PR refactors the CI/CD pipeline to use larger GitHub-hosted runners and restructures the Android build into a multi-stage pipeline for improved performance.

Key Changes

Runner Upgrades (All Workflows)

  • Linux: ubuntu-latestubuntu-latest-8-cores (2 CPU → 8 CPU, 7GB → 32GB RAM)
  • macOS: macos-latestmacos-latest-xlarge (3-core M1 → 5-core M2 + 8 GPU, 7GB → 14GB RAM)

Android Build Pipeline Refactor (android-build.yml)
The monolithic Android build job has been split into 4 stages:

  1. build-frontend: Builds frontend once with Bun, uploads frontend-dist artifact
  2. generate-gradle-files: Runs cargo tauri android init --ci to generate Gradle configuration and Kotlin sources (runs parallel with stage 3)
  3. build-rust: Matrix job building 4 Android ABIs in parallel (aarch64, armv7, i686, x86_64), each with dedicated sccache
  4. package-android: Assembles artifacts, runs Gradle with -PskipRustBuild=true to create APK/AAB

Supporting Changes

  • Added top-level environment variables in android-build.yml for API endpoints and client ID
  • Modified RustPlugin.kt to support skipRustBuild flag, allowing Gradle to skip Rust compilation when native libs are pre-built
  • Consistent runner upgrades across all 6 workflows

Architecture Benefits

The refactored Android pipeline enables:

  • Parallel Rust compilation: 4 ABIs build simultaneously instead of sequentially
  • Improved caching: Each ABI has its own sccache scope
  • Faster iteration: Frontend builds once and is reused across all Rust builds
  • Build separation: Gradle packaging decoupled from Rust compilation

The new pipeline leverages the 8-core Linux runners effectively by parallelizing the most expensive operations (Rust cross-compilation for 4 targets).

Confidence Score: 2/5

  • NOT safe to merge - contains a critical path handling bug that will cause build failures
  • There is a [P0] blocking issue in the artifact placement logic in android-build.yml. The cp -R tauri-android-generated/. frontend/src-tauri/gen/android/ command will create nested duplicate paths because upload-artifact@v4 preserves full paths. This will cause the package-android job to fail when it tries to find files in the wrong locations. The runner upgrades themselves are fine, but this critical bug prevents the Android build from working.
  • .github/workflows/android-build.yml requires immediate fix to artifact placement logic (line 348)

Important Files Changed

File Analysis

Filename Score Overview
.github/workflows/android-build.yml 4/5 Major refactor: split single job into 4-stage pipeline (frontend build, gradle generation, parallel Rust builds per ABI, packaging). Uses ubuntu-latest-8-cores runners and new skipRustBuild flag.
.github/workflows/claude.yml 5/5 Runner upgrade: ubuntu-latest → ubuntu-latest-8-cores
.github/workflows/desktop-build.yml 5/5 Runner upgrades: macos-latest → macos-latest-xlarge, ubuntu-latest → ubuntu-latest-8-cores
.github/workflows/mobile-build.yml 5/5 Runner upgrade: macos-latest → macos-latest-xlarge for iOS builds
.github/workflows/release.yml 4/5 Runner upgrades across all jobs: macos-latest → macos-latest-xlarge, ubuntu-latest → ubuntu-latest-8-cores. Missing SCCACHE_DIR on Linux builds (noted in existing comment).
.github/workflows/testflight-on-comment.yml 5/5 Runner upgrades: ubuntu-latest → ubuntu-latest-8-cores, macos-latest → macos-latest-xlarge
frontend/src-tauri/gen/android/buildSrc/src/main/java/cloud/opensecret/maple/kotlin/RustPlugin.kt 5/5 Added skipRustBuild flag detection to skip Rust build tasks when -PskipRustBuild is passed, enabling pre-built native libs workflow.

Sequence Diagram

sequenceDiagram
    participant GH as GitHub Actions
    participant FE as build-frontend
    participant GEN as generate-gradle-files
    participant BR1 as build-rust (aarch64)
    participant BR2 as build-rust (armv7)
    participant BR3 as build-rust (i686)
    participant BR4 as build-rust (x86_64)
    participant PKG as package-android
    
    GH->>FE: Trigger (ubuntu-latest-8-cores)
    GH->>GEN: Trigger (ubuntu-latest-8-cores)
    
    FE->>FE: Setup Bun, install deps
    FE->>FE: Build frontend (bun run build)
    FE->>GH: Upload frontend-dist artifact
    
    par Generate Gradle files
        GEN->>GEN: Install Rust & Tauri CLI
        GEN->>GEN: cargo fetch (get deps)
        GEN->>GEN: cargo tauri android init --ci
        GEN->>GEN: Verify generated files
        GEN->>GH: Upload tauri-android-generated artifact
    end
    
    GH->>BR1: Trigger after build-frontend
    GH->>BR2: Trigger after build-frontend
    GH->>BR3: Trigger after build-frontend
    GH->>BR4: Trigger after build-frontend
    
    par Parallel Rust builds (4 ABIs)
        BR1->>GH: Download frontend-dist
        BR1->>BR1: Setup NDK, sccache, toolchain
        BR1->>BR1: cargo build --target aarch64-linux-android
        BR1->>GH: Upload android-lib-aarch64
        
        BR2->>GH: Download frontend-dist
        BR2->>BR2: Setup NDK, sccache, toolchain
        BR2->>BR2: cargo build --target armv7-linux-androideabi
        BR2->>GH: Upload android-lib-armv7
        
        BR3->>GH: Download frontend-dist
        BR3->>BR3: Setup NDK, sccache, toolchain
        BR3->>BR3: cargo build --target i686-linux-android
        BR3->>GH: Upload android-lib-i686
        
        BR4->>GH: Download frontend-dist
        BR4->>BR4: Setup NDK, sccache, toolchain
        BR4->>BR4: cargo build --target x86_64-linux-android
        BR4->>GH: Upload android-lib-x86_64
    end
    
    GH->>PKG: Trigger after build-rust & generate-gradle-files
    PKG->>GH: Download frontend-dist
    PKG->>GH: Download all android-lib-* (pattern)
    PKG->>GH: Download tauri-android-generated
    PKG->>PKG: Place generated files (cp -R)
    PKG->>PKG: Place .so files in jniLibs
    PKG->>PKG: Setup Android signing
    PKG->>PKG: Generate tauri.properties
    PKG->>PKG: ./gradlew assembleUniversalRelease bundleUniversalRelease -PskipRustBuild
    PKG->>GH: Upload maple-android-apk
    PKG->>GH: Upload maple-android-aab
Loading

Comment on lines +345 to +388
- name: Place Tauri Android generated files
run: |
# Copy generated files into the checked out Android project
cp -R tauri-android-generated/. frontend/src-tauri/gen/android/
Copy link
Contributor

Choose a reason for hiding this comment

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

[P0] The artifact placement will create nested duplicate paths. When uploading artifacts with actions/upload-artifact@v4, the full paths are preserved (e.g., frontend/src-tauri/gen/android/tauri.settings.gradle). After downloading to tauri-android-generated, the structure becomes tauri-android-generated/frontend/src-tauri/gen/android/.... Copying with cp -R tauri-android-generated/. frontend/src-tauri/gen/android/ creates frontend/src-tauri/gen/android/frontend/src-tauri/gen/android/... (nested duplicate). Fix: cp -R tauri-android-generated/frontend/src-tauri/gen/android/. frontend/src-tauri/gen/android/

Prompt To Fix With AI
This is a comment left during a code review.
Path: .github/workflows/android-build.yml
Line: 345:348

Comment:
[P0] The artifact placement will create nested duplicate paths. When uploading artifacts with `actions/upload-artifact@v4`, the full paths are preserved (e.g., `frontend/src-tauri/gen/android/tauri.settings.gradle`). After downloading to `tauri-android-generated`, the structure becomes `tauri-android-generated/frontend/src-tauri/gen/android/...`. Copying with `cp -R tauri-android-generated/. frontend/src-tauri/gen/android/` creates `frontend/src-tauri/gen/android/frontend/src-tauri/gen/android/...` (nested duplicate). Fix: `cp -R tauri-android-generated/frontend/src-tauri/gen/android/. frontend/src-tauri/gen/android/`

How can I resolve this? If you propose a fix, please make it concise.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In @.github/workflows/android-build.yml:
- Around line 317-325: The Cache Gradle step's key uses hashFiles(...) against
generated Gradle files (the key expression after "key:"), which are created at
runtime and may be missing; replace that hashFiles(...) call with a hash of a
stable source like Cargo.lock (e.g., use hashFiles('Cargo.lock')) so the Gradle
cache key is stable and driven by actual dependency changes.
- Around line 219-267: The workflow redundantly creates filesystem symlinks for
ranlib (the multiple sudo ln -sf lines) despite already exporting RANLIB_*
environment variables (e.g., RANLIB_aarch64_linux_android,
RANLIB_armv7_linux_androideabi, RANLIB_i686_linux_android,
RANLIB_x86_64_linux_android); remove all sudo ln -sf ... llvm-ranlib
/usr/local/bin/... and /usr/bin/... lines from the Configure build environment
case branches and keep only the echo lines that set the CARGO_TARGET_* and
RANLIB_* environment variables so Cargo uses the configured ranlib paths.
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 94a06d6 and a6fd455.

📒 Files selected for processing (1)
  • .github/workflows/android-build.yml
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: AnthonyRonning
Repo: OpenSecretCloud/Maple PR: 372
File: frontend/src-tauri/Cargo.toml:9-9
Timestamp: 2026-01-08T17:09:40.432Z
Learning: The OpenSecretCloud/Maple repository has comprehensive GitHub Actions workflows (e.g., desktop-build.yml) that automatically validate compilation and testing, so manual compilation verification reminders are unnecessary when reviewing version bumps or dependency updates.
📚 Learning: 2026-01-08T17:09:40.432Z
Learnt from: AnthonyRonning
Repo: OpenSecretCloud/Maple PR: 372
File: frontend/src-tauri/Cargo.toml:9-9
Timestamp: 2026-01-08T17:09:40.432Z
Learning: The OpenSecretCloud/Maple repository has comprehensive GitHub Actions workflows (e.g., desktop-build.yml) that automatically validate compilation and testing, so manual compilation verification reminders are unnecessary when reviewing version bumps or dependency updates.

Applied to files:

  • .github/workflows/android-build.yml
📚 Learning: 2025-12-08T19:55:33.330Z
Learnt from: CR
Repo: OpenSecretCloud/Maple PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-08T19:55:33.330Z
Learning: Applies to src-tauri/**/*.rs : Handle errors with `anyhow` crate or explicit Result types in Rust code

Applied to files:

  • .github/workflows/android-build.yml
📚 Learning: 2025-12-08T19:55:33.330Z
Learnt from: CR
Repo: OpenSecretCloud/Maple PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-08T19:55:33.330Z
Learning: Applies to src-tauri/**/*.rs : Run `just rust-lint` after modifying Rust code to ensure proper formatting, compilation, and linting (runs fmt + clippy strict)

Applied to files:

  • .github/workflows/android-build.yml
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: build-frontend
  • GitHub Check: build-macos (universal-apple-darwin)
  • GitHub Check: build-linux
  • GitHub Check: build-ios
  • GitHub Check: Cloudflare Pages
🔇 Additional comments (13)
.github/workflows/android-build.yml (13)

92-95: Good defensive programming with cargo fetch before android init.

Running cargo fetch --locked before cargo tauri android init ensures all Tauri plugin crates are available in the registry, which prevents potential failures during the generation of tauri.settings.gradle. The --ci and --force flags are appropriate for CI environments.


97-131: Excellent validation and debugging output for generated files.

The comprehensive validation of required generated files (Gradle files, Kotlin sources, ProGuard rules) with detailed error messages and debugging output significantly improves troubleshooting. This is particularly valuable for a complex code generation step that could fail silently.


146-165: Well-structured matrix strategy for parallel Android target builds.

The matrix configuration correctly maps each Android target to its corresponding JNI directory and provides a short name for cache keys. Using fail-fast: false ensures all targets complete even if one fails, which is appropriate for CI visibility.


195-202: Good: Per-target sccache with fallback cache keys.

The sccache cache configuration correctly uses target-specific keys (android-${{ matrix.short_name }}) with appropriate fallback restore keys. This allows cache sharing across targets while maintaining target-specific caches.


345-354: Good artifact placement with verification.

The script correctly copies generated Tauri files from the downloaded artifact into the Android project structure and verifies that key files exist. This provides good visibility into the artifact handoff.


355-368: Verify native library artifact names match matrix short_name.

The script assumes specific artifact names (android-lib-aarch64, android-lib-armv7, etc.) that must exactly match the names used in the build-rust job's upload step (line 280). Any mismatch will cause the copy commands to fail.

The artifact names correctly match the matrix short_name values, ensuring proper artifact handoff across jobs.


396-403: Good: Parallel Gradle build with skip Rust flag.

Using --parallel for Gradle and -PskipRustBuild=true is correct since the Rust libraries were already built in the previous job. This prevents Gradle from attempting to rebuild Rust code.


14-417: Excellent refactoring: Multi-stage pipeline with parallelization.

The refactored workflow structure provides several benefits:

  1. Parallelization: Frontend builds once, then generate-gradle-files and build-rust (matrix) run in parallel, maximizing the use of the 8-core runner.
  2. Clear separation of concerns: Each job has a single responsibility (build frontend, generate files, compile Rust, package).
  3. Artifact-based communication: Clean handoff of build artifacts between jobs reduces coupling and improves maintainability.
  4. Fail-fast disabled on matrix: Allows all Android targets to complete even if one fails, providing complete CI visibility.

This is a well-designed CI pipeline that should significantly speed up Android builds.


49-49: Appropriate artifact retention policies.

The workflow uses:

  • retention-days: 1 for intermediate artifacts (frontend-dist, tauri-android-generated, android-lib-*)
  • retention-days: 5 for final outputs (APK, AAB)

This balances storage costs with the need to keep intermediate artifacts for debugging failed builds and preserving final outputs for distribution.

Also applies to: 144-144, 282-282, 410-410, 417-417


17-17: The runner ubuntu-latest-8-cores is already in use across multiple existing workflows (testflight-on-comment.yml, release.yml, desktop-build.yml, claude.yml) and therefore already exists in the organization's settings. No action is needed to create it for this workflow.

Likely an incorrect or invalid review comment.


9-12: Environment variables are correctly propagated by default.

Top-level env variables in GitHub Actions workflows are automatically inherited by all jobs—no explicit propagation is needed. The frontend build step (bun run build) executes with access to VITE_OPEN_SECRET_API_URL, VITE_MAPLE_BILLING_API_URL, and VITE_CLIENT_ID, and the frontend code correctly accesses them via the standard Vite pattern (import.meta.env.VITE_*). The workflow is properly configured.


269-272: Building only --lib is correct for Tauri Android.

Tauri's Android build process expects pre-built native Rust libraries (.so files) to be compiled separately and then provided to Gradle for packaging into the APK. The cargo build --release --target ${{ matrix.target }} --lib command correctly produces these JNI libraries. Ensure that the Rust crate in ./frontend/src-tauri has crate-type = ["cdylib"] configured in its Cargo.toml for proper JNI library generation, and that the resulting .so files are copied to the correct Gradle source directory (e.g., android/app/src/main/jniLibs/<ABI>/).


51-53: No changes needed. The generate-gradle-files job does not require the frontend dist artifact. The cargo tauri android init command generates only Android project structure and Gradle configuration files; it does not need built frontend assets. The frontendDist config setting is consumed during app packaging, not during initialization.

Comment on lines +219 to +307
- name: Configure build environment
run: |
echo "RUSTC_WRAPPER=sccache" >> $GITHUB_ENV
echo "SCCACHE_DIR=$HOME/.cache/sccache" >> $GITHUB_ENV
echo "SCCACHE_CACHE_SIZE=2G" >> $GITHUB_ENV
NDK_HOME=${{ steps.setup-ndk.outputs.ndk-path }}
# Set up cross-compilation environment for this target (CC, AR, RANLIB, and LINKER)
case "${{ matrix.target }}" in
aarch64-linux-android)
echo "CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER=$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android24-clang" >> $GITHUB_ENV
echo "AR_aarch64_linux_android=$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ar" >> $GITHUB_ENV
echo "CC_aarch64_linux_android=$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android24-clang" >> $GITHUB_ENV
echo "CXX_aarch64_linux_android=$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android24-clang++" >> $GITHUB_ENV
echo "RANLIB_aarch64_linux_android=$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ranlib" >> $GITHUB_ENV
sudo ln -sf $NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ranlib /usr/local/bin/aarch64-linux-android-ranlib
sudo ln -sf $NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ranlib /usr/bin/aarch64-linux-android-ranlib
;;
armv7-linux-androideabi)
echo "CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_LINKER=$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi24-clang" >> $GITHUB_ENV
echo "AR_armv7_linux_androideabi=$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ar" >> $GITHUB_ENV
echo "CC_armv7_linux_androideabi=$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi24-clang" >> $GITHUB_ENV
echo "CXX_armv7_linux_androideabi=$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi24-clang++" >> $GITHUB_ENV
echo "RANLIB_armv7_linux_androideabi=$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ranlib" >> $GITHUB_ENV
sudo ln -sf $NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ranlib /usr/local/bin/armv7a-linux-androideabi-ranlib
sudo ln -sf $NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ranlib /usr/local/bin/arm-linux-androideabi-ranlib
sudo ln -sf $NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ranlib /usr/bin/armv7a-linux-androideabi-ranlib
sudo ln -sf $NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ranlib /usr/bin/arm-linux-androideabi-ranlib
;;
i686-linux-android)
echo "CARGO_TARGET_I686_LINUX_ANDROID_LINKER=$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/i686-linux-android24-clang" >> $GITHUB_ENV
echo "AR_i686_linux_android=$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ar" >> $GITHUB_ENV
echo "CC_i686_linux_android=$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/i686-linux-android24-clang" >> $GITHUB_ENV
echo "CXX_i686_linux_android=$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/i686-linux-android24-clang++" >> $GITHUB_ENV
echo "RANLIB_i686_linux_android=$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ranlib" >> $GITHUB_ENV
sudo ln -sf $NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ranlib /usr/local/bin/i686-linux-android-ranlib
sudo ln -sf $NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ranlib /usr/bin/i686-linux-android-ranlib
;;
x86_64-linux-android)
echo "CARGO_TARGET_X86_64_LINUX_ANDROID_LINKER=$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/x86_64-linux-android24-clang" >> $GITHUB_ENV
echo "AR_x86_64_linux_android=$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ar" >> $GITHUB_ENV
echo "CC_x86_64_linux_android=$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/x86_64-linux-android24-clang" >> $GITHUB_ENV
echo "CXX_x86_64_linux_android=$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/x86_64-linux-android24-clang++" >> $GITHUB_ENV
echo "RANLIB_x86_64_linux_android=$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ranlib" >> $GITHUB_ENV
sudo ln -sf $NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ranlib /usr/local/bin/x86_64-linux-android-ranlib
sudo ln -sf $NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ranlib /usr/bin/x86_64-linux-android-ranlib
;;
esac
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, understand the repo structure
echo "=== Repository Structure ==="
git ls-files | head -20
echo ""
echo "=== Frontend/Tauri directory structure ==="
fd -type d "src-tauri|frontend" | head -10

Repository: OpenSecretCloud/Maple

Length of output: 844


🏁 Script executed:

#!/bin/bash
# Run the verification script to check for hardcoded ranlib references
echo "=== Searching for ranlib references in build scripts ==="
fd -e rs -e toml -e sh | xargs grep -l -i "ranlib" 2>/dev/null || echo "No ranlib references found"
echo ""
echo "=== Checking for .cargo/config.toml ==="
find . -name ".cargo" -type d 2>/dev/null | head -5
echo ""
echo "=== Cargo config files ==="
find . -path "*/.cargo/config*" 2>/dev/null

Repository: OpenSecretCloud/Maple

Length of output: 217


🏁 Script executed:

#!/bin/bash
# Let's look at the actual workflow file to understand context
echo "=== Android build workflow file context ==="
if [ -f ".github/workflows/android-build.yml" ]; then
  wc -l ".github/workflows/android-build.yml"
  echo ""
  echo "=== Lines around the ranlib configuration (lines 200-270) ==="
  sed -n '200,270p' ".github/workflows/android-build.yml" | cat -n
fi

Repository: OpenSecretCloud/Maple

Length of output: 6155


🏁 Script executed:

#!/bin/bash
# Search more comprehensively for build scripts and native dependencies
echo "=== Searching for build.rs files ==="
find . -name "build.rs" -type f 2>/dev/null

echo ""
echo "=== Searching for any Cargo.toml files ==="
find . -name "Cargo.toml" -type f 2>/dev/null | head -10

echo ""
echo "=== Check if frontend/src-tauri exists ==="
ls -la frontend/ 2>/dev/null || echo "No frontend directory"
ls -la src-tauri/ 2>/dev/null || echo "No src-tauri directory"

Repository: OpenSecretCloud/Maple

Length of output: 1833


🏁 Script executed:

#!/bin/bash
# Search for any reference to tool names in the entire codebase
echo "=== Searching for any hardcoded tool paths (ar, clang, etc.) ==="
grep -r "aarch64-linux-android\|armv7a-linux-android\|i686-linux-android\|x86_64-linux-android" . --include="*.rs" --include="*.toml" --include="*.sh" 2>/dev/null | grep -v ".git" | head -20

echo ""
echo "=== Git history of android-build.yml ==="
git log --oneline .github/workflows/android-build.yml 2>/dev/null | head -10

echo ""
echo "=== Check when these symlinks were added ==="
git log -p --all -- ".github/workflows/android-build.yml" 2>/dev/null | grep -A 5 -B 5 "ranlib" | head -40

Repository: OpenSecretCloud/Maple

Length of output: 279


🏁 Script executed:

#!/bin/bash
# Check the actual Cargo.toml in src-tauri if it exists
echo "=== Looking for all Rust project files ==="
find . -type f -name "Cargo.toml" 2>/dev/null

echo ""
echo "=== Check if there are references to native-build or build scripts ==="
find . -type f \( -name "Cargo.toml" -o -name "build.rs" \) 2>/dev/null -exec echo "File: {}" \; -exec head -30 {} \;

Repository: OpenSecretCloud/Maple

Length of output: 1148


🏁 Script executed:

#!/bin/bash
# Let's check if there are any comments or documentation about why these symlinks exist
echo "=== Check for any Tauri-specific documentation ==="
find . -name "*.md" -type f 2>/dev/null | xargs grep -l "ranlib\|NDK\|symlink" 2>/dev/null

echo ""
echo "=== Check if there's a .cargo directory created at runtime ==="
find . -path "*/.cargo" -type d 2>/dev/null

echo ""
echo "=== Check Tauri version and Android specific config ==="
grep -i "tauri" ./frontend/src-tauri/Cargo.toml

Repository: OpenSecretCloud/Maple

Length of output: 715


🏁 Script executed:

#!/bin/bash
# Check the Android platform analysis document for relevant information
echo "=== Android platform checks analysis ==="
cat ./docs/android-platform-checks-analysis.md | head -100

Repository: OpenSecretCloud/Maple

Length of output: 4243


Remove unnecessary ranlib symlink workarounds.

The configuration sets RANLIB_* environment variables for each Android target (aarch64, armv7, i686, x86_64), which is the standard approach for Cargo cross-compilation. The subsequent sudo ln -sf commands creating symlinks in /usr/local/bin and /usr/bin are redundant—the Cargo toolchain respects the environment variables without requiring filesystem symlinks.

No code in the repository (including the minimal build.rs which only calls tauri_build::build()) references these tool names by path. This pattern appears to be a conservative holdover from Tauri's android init scaffold.

Simplify by removing all sudo ln -sf commands and rely solely on the RANLIB_* environment variables already being set.

🤖 Prompt for AI Agents
In @.github/workflows/android-build.yml around lines 219 - 267, The workflow
redundantly creates filesystem symlinks for ranlib (the multiple sudo ln -sf
lines) despite already exporting RANLIB_* environment variables (e.g.,
RANLIB_aarch64_linux_android, RANLIB_armv7_linux_androideabi,
RANLIB_i686_linux_android, RANLIB_x86_64_linux_android); remove all sudo ln -sf
... llvm-ranlib /usr/local/bin/... and /usr/bin/... lines from the Configure
build environment case branches and keep only the echo lines that set the
CARGO_TARGET_* and RANLIB_* environment variables so Cargo uses the configured
ranlib paths.

Comment on lines +317 to +365
- name: Cache Gradle
uses: actions/cache@v4
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('frontend/src-tauri/gen/android/**/*.gradle*', 'frontend/src-tauri/gen/android/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Gradle cache key may not work correctly for generated files.

The Gradle cache key includes paths like frontend/src-tauri/gen/android/**/*.gradle*, but these files are generated in the generate-gradle-files job and not present in the repository. On first run or with a clean cache, the hashFiles() function will return an empty hash for non-existent files, which could lead to suboptimal cache behavior.

♻️ Suggested fix: Use Cargo.lock for Gradle cache key

Since the Gradle files are generated from Cargo dependencies, using the Cargo.lock hash is more reliable:

       - name: Cache Gradle
         uses: actions/cache@v4
         with:
           path: |
             ~/.gradle/caches
             ~/.gradle/wrapper
-          key: ${{ runner.os }}-gradle-${{ hashFiles('frontend/src-tauri/gen/android/**/*.gradle*', 'frontend/src-tauri/gen/android/gradle-wrapper.properties') }}
+          key: ${{ runner.os }}-gradle-android-${{ hashFiles('**/Cargo.lock') }}
           restore-keys: |
             ${{ runner.os }}-gradle-
🤖 Prompt for AI Agents
In @.github/workflows/android-build.yml around lines 317 - 325, The Cache Gradle
step's key uses hashFiles(...) against generated Gradle files (the key
expression after "key:"), which are created at runtime and may be missing;
replace that hashFiles(...) call with a hash of a stable source like Cargo.lock
(e.g., use hashFiles('Cargo.lock')) so the Gradle cache key is stable and driven
by actual dependency changes.

AnthonyRonning and others added 8 commits January 9, 2026 12:47
Split Android build into parallel jobs:
1. build-frontend: Build frontend once (shared)
2. build-rust (4x parallel): Compile each target separately
   - aarch64-linux-android
   - armv7-linux-androideabi
   - i686-linux-android
   - x86_64-linux-android
3. package-android: Download all .so files, run Gradle

Each Rust job has its own sccache keyed by target for better cache hits.
Expected improvement: ~10min -> ~5min (parallel Rust + Gradle)

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
The linker must be explicitly set when using cargo build directly
(vs cargo tauri android build which sets it internally).

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
- Generate tauri.settings.gradle in aarch64 job (contains Tauri plugin paths)
- Upload as artifact and download in package-android job
- Remove unnecessary bun install/setup from package-android (Gradle doesn't need node_modules)

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
- Add generate-gradle-files job that runs in parallel with build-rust
- Keeps build-rust jobs clean (just Rust compilation)
- package-android now depends on both build-rust and generate-gradle-files

Job structure:
  build-frontend ─┬─> build-rust (4x parallel) ─┬─> package-android
                  └─> generate-gradle-files ────┘

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Tauri generates two gradle files that are needed for packaging:
- tauri.settings.gradle (in gen/android/)
- tauri.build.gradle.kts (in gen/android/app/)

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

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

⚠️ Outside diff range comments (1)
.github/workflows/android-build.yml (1)

382-394: Invalid comment syntax in .properties file.

The generated tauri.properties uses // for comments, but .properties files use # or ! for comments. The // will be treated as an invalid property line and may cause Gradle to emit warnings or fail to parse correctly.

🐛 Fix comment syntax
      - name: Generate tauri.properties
        run: |
          VERSION=$(jq -r '.version' frontend/src-tauri/tauri.conf.json)
          VERSION_CODE=$(jq -r '.bundle.android.versionCode' frontend/src-tauri/tauri.conf.json)
          
          mkdir -p frontend/src-tauri/gen/android/app
          cat > frontend/src-tauri/gen/android/app/tauri.properties << EOF
-          // THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+          # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
          tauri.android.versionName=$VERSION
          tauri.android.versionCode=$VERSION_CODE
          EOF
🧹 Nitpick comments (2)
.github/workflows/android-build.yml (2)

187-193: Consider verifying sccache binary integrity.

The sccache installation downloads a binary from GitHub releases without checksum verification. While this is a common pattern, for supply chain security, consider verifying the download using checksums provided in the release.

♻️ Optional: Add checksum verification
      - name: Install sccache
        run: |
          SCCACHE_VERSION=0.8.2
          SCCACHE_URL="https://github.com/mozilla/sccache/releases/download/v${SCCACHE_VERSION}/sccache-v${SCCACHE_VERSION}-x86_64-unknown-linux-musl.tar.gz"
+         SCCACHE_SHA256="<expected-sha256-here>"
          curl -L "$SCCACHE_URL" | tar xz
+         # Alternatively, download first and verify
+         # curl -L "$SCCACHE_URL" -o sccache.tar.gz
+         # echo "$SCCACHE_SHA256 sccache.tar.gz" | sha256sum -c
+         # tar xzf sccache.tar.gz
          sudo mv sccache-v${SCCACHE_VERSION}-x86_64-unknown-linux-musl/sccache /usr/local/bin/
          chmod +x /usr/local/bin/sccache

355-368: Native library placement could benefit from explicit verification.

The copy commands assume the artifact downloads succeeded. While job dependencies should ensure this, adding explicit checks would make failures more debuggable:

♻️ Optional: Add pre-copy verification
      - name: Place native libraries in jniLibs
        run: |
+         set -euo pipefail
+         
+         # Verify all expected artifacts exist
+         for lib in aarch64 armv7 i686 x86_64; do
+           if [ ! -d "native-libs/android-lib-$lib" ]; then
+             echo "ERROR: Missing native library artifact for $lib"
+             exit 1
+           fi
+         done
+         
          # Create jniLibs directories
          mkdir -p frontend/src-tauri/gen/android/app/src/main/jniLibs/{arm64-v8a,armeabi-v7a,x86,x86_64}
          
          # Copy libraries to correct locations
          cp native-libs/android-lib-aarch64/*.so frontend/src-tauri/gen/android/app/src/main/jniLibs/arm64-v8a/
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a6fd455 and e1b2c4c.

📒 Files selected for processing (1)
  • .github/workflows/android-build.yml
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: AnthonyRonning
Repo: OpenSecretCloud/Maple PR: 372
File: frontend/src-tauri/Cargo.toml:9-9
Timestamp: 2026-01-08T17:09:40.432Z
Learning: The OpenSecretCloud/Maple repository has comprehensive GitHub Actions workflows (e.g., desktop-build.yml) that automatically validate compilation and testing, so manual compilation verification reminders are unnecessary when reviewing version bumps or dependency updates.
📚 Learning: 2026-01-08T17:09:40.432Z
Learnt from: AnthonyRonning
Repo: OpenSecretCloud/Maple PR: 372
File: frontend/src-tauri/Cargo.toml:9-9
Timestamp: 2026-01-08T17:09:40.432Z
Learning: The OpenSecretCloud/Maple repository has comprehensive GitHub Actions workflows (e.g., desktop-build.yml) that automatically validate compilation and testing, so manual compilation verification reminders are unnecessary when reviewing version bumps or dependency updates.

Applied to files:

  • .github/workflows/android-build.yml
📚 Learning: 2025-12-08T19:55:33.330Z
Learnt from: CR
Repo: OpenSecretCloud/Maple PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-08T19:55:33.330Z
Learning: Applies to src-tauri/**/*.rs : Handle errors with `anyhow` crate or explicit Result types in Rust code

Applied to files:

  • .github/workflows/android-build.yml
📚 Learning: 2025-12-08T19:55:33.330Z
Learnt from: CR
Repo: OpenSecretCloud/Maple PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-08T19:55:33.330Z
Learning: Applies to src-tauri/**/*.rs : Run `just rust-lint` after modifying Rust code to ensure proper formatting, compilation, and linting (runs fmt + clippy strict)

Applied to files:

  • .github/workflows/android-build.yml
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: build-rust (x86_64-linux-android, x86_64, x86_64)
  • GitHub Check: build-rust (aarch64-linux-android, arm64-v8a, aarch64)
  • GitHub Check: build-rust (armv7-linux-androideabi, armeabi-v7a, armv7)
  • GitHub Check: build-rust (i686-linux-android, x86, i686)
  • GitHub Check: build-macos (universal-apple-darwin)
  • GitHub Check: build-ios
  • GitHub Check: build-linux
  • GitHub Check: Cloudflare Pages
🔇 Additional comments (6)
.github/workflows/android-build.yml (6)

9-13: Environment configuration looks good.

The conditional logic correctly differentiates between PR (dev) and push-to-master (production) environments. The client ID is a public OAuth identifier and is appropriately hardcoded.


15-50: Frontend build job is well-structured.

The job correctly:

  • Uses the larger runner for faster builds
  • Pins Bun version for reproducibility
  • Caches dependencies using the lockfile hash
  • Uses 1-day retention for intermediate artifacts (appropriate for CI)

The top-level env vars will be inherited, so the frontend build will pick up the correct VITE_* environment variables.


86-131: Thorough validation of generated files.

The explicit verification of required Tauri Android files is excellent defensive programming. This prevents downstream failures in the package-android job by catching missing files early.

One minor observation: the Kotlin source check on line 118 uses find ... | grep -q . which works but could be simplified:

if ! find "$GENERATED_DIR" -type f -name "*.kt" | head -1 | grep -q .; then

However, the current approach is functionally correct.


219-267: Comprehensive cross-compilation environment setup.

The per-target configuration correctly sets up:

  • Cargo linker paths for each Android target
  • C/C++ compiler and archiver paths
  • Symlinks for ranlib (handles different naming conventions in build systems)

The use of Android API level 24 (Android 7.0 Nougat) provides good device coverage while supporting modern NDK features.


269-283: Rust library build and artifact upload looks correct.

The build command correctly:

  • Uses --lib to build only the library crate
  • Targets the specific Android architecture from the matrix
  • Builds in release mode

The wildcard *.so pattern should capture the native library. The sccache stats step is helpful for monitoring cache hit rates.


396-403: Gradle build configuration is correct.

The -PskipRustBuild=true flag is essential since the Rust libraries were compiled separately in the matrix job. Building both APK and AAB with --parallel in a single invocation is efficient.

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

Greptile Overview

Greptile Summary

Upgrades all GitHub Actions workflows to use larger runners for faster builds: macos-latest-xlarge (M2, 5 cores, 14GB) and ubuntu-latest-8-cores (8 CPU, 32GB).

Major Changes

  • Android build pipeline refactored into 4 parallel jobs: frontend build, Gradle file generation, Rust compilation per-ABI (4 parallel), and packaging
  • RustPlugin.kt now supports -PskipRustBuild flag for Gradle packaging stage
  • Environment variables moved to top-level in android-build.yml for consistency
  • All other workflows have simple runner upgrades with no functional changes

Critical Issues Found

  • Artifact path bug in android-build.yml will cause nested directory structure, breaking the build (see inline comment)
  • The PR description notes that ubuntu-latest-8-cores runner must be created in org settings before merging, or all Linux/Android builds will fail immediately

Confidence Score: 2/5

  • This PR has a critical artifact path bug that will break Android CI builds
  • The android-build.yml refactor contains a logical error in artifact upload/download paths that will cause files to be placed in nested directories, breaking the Gradle build. Most other changes are straightforward runner upgrades with low risk. The missing SCCACHE_DIR in release.yml was already noted in previous review.
  • .github/workflows/android-build.yml requires immediate fix for artifact path handling before merging

Important Files Changed

File Analysis

Filename Score Overview
.github/workflows/android-build.yml 2/5 Major refactor splits Android build into 4 parallel jobs (frontend, gradle files, rust builds per-target, packaging). Critical bug: artifact paths will create nested directories causing build failure.
.github/workflows/desktop-build.yml 5/5 Simple runner upgrade from macos-latest to macos-latest-xlarge and ubuntu-latest to ubuntu-latest-8-cores. No functional changes.
.github/workflows/mobile-build.yml 5/5 Simple runner upgrade from macos-latest to macos-latest-xlarge for iOS builds. No functional changes.
.github/workflows/release.yml 5/5 Runner upgrades across all jobs. Already noted issue with missing SCCACHE_DIR for Linux builds in previous review thread.
.github/workflows/testflight-on-comment.yml 5/5 Runner upgrades for both check-comment job (ubuntu-latest-8-cores) and deploy-testflight job (macos-latest-xlarge). No functional changes.
.github/workflows/claude.yml 5/5 Simple runner upgrade from ubuntu-latest to ubuntu-latest-8-cores. No functional changes.
frontend/src-tauri/gen/android/buildSrc/src/main/java/cloud/opensecret/maple/kotlin/RustPlugin.kt 5/5 Adds skipRustBuild property support to allow Gradle to skip Rust compilation when pre-built libraries are provided. Required for new parallel build strategy.

Sequence Diagram

sequenceDiagram
    participant Trigger as Workflow Trigger
    participant Frontend as build-frontend
    participant Gradle as generate-gradle-files
    participant Rust1 as build-rust (aarch64)
    participant Rust2 as build-rust (armv7)
    participant Rust3 as build-rust (i686)
    participant Rust4 as build-rust (x86_64)
    participant Package as package-android
    participant Artifacts as GitHub Artifacts

    Trigger->>Frontend: Start CI build
    Trigger->>Gradle: Start CI build
    
    Note over Frontend: Build frontend with Bun<br/>Uses VITE env vars
    Frontend->>Artifacts: Upload frontend-dist
    
    Note over Gradle: Generate Tauri Android files<br/>cargo tauri android init --ci
    Gradle->>Artifacts: Upload tauri-android-generated
    
    Artifacts->>Rust1: Download frontend-dist
    Artifacts->>Rust2: Download frontend-dist
    Artifacts->>Rust3: Download frontend-dist
    Artifacts->>Rust4: Download frontend-dist
    
    Note over Rust1,Rust4: Parallel Rust compilation<br/>for each Android ABI<br/>with sccache
    
    Rust1->>Artifacts: Upload android-lib-aarch64
    Rust2->>Artifacts: Upload android-lib-armv7
    Rust3->>Artifacts: Upload android-lib-i686
    Rust4->>Artifacts: Upload android-lib-x86_64
    
    Artifacts->>Package: Download frontend-dist
    Artifacts->>Package: Download tauri-android-generated
    Artifacts->>Package: Download all android-lib-*
    
    Note over Package: Place files in jniLibs<br/>Run Gradle with -PskipRustBuild=true<br/>Build APK and AAB
    
    Package->>Artifacts: Upload APK & AAB
Loading

Comment on lines +137 to +184
path: |
frontend/src-tauri/gen/android/tauri.settings.gradle
frontend/src-tauri/gen/android/.tauri/**
frontend/src-tauri/gen/android/app/tauri.build.gradle.kts
frontend/src-tauri/gen/android/app/src/main/assets/tauri.conf.json
frontend/src-tauri/gen/android/app/src/main/java/**/generated/**
frontend/src-tauri/gen/android/app/proguard-tauri.pro
retention-days: 1
Copy link
Contributor

Choose a reason for hiding this comment

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

Artifact upload paths will cause incorrect directory structure when downloaded.

When uploading with paths like frontend/src-tauri/gen/android/tauri.settings.gradle, the artifact preserves this full path. When downloaded to tauri-android-generated, it extracts as tauri-android-generated/frontend/src-tauri/gen/android/....

Then at line 348, cp -R tauri-android-generated/. frontend/src-tauri/gen/android/ copies all contents, resulting in nested paths: frontend/src-tauri/gen/android/frontend/src-tauri/gen/android/...

Fix by adding working-directory:

Suggested change
path: |
frontend/src-tauri/gen/android/tauri.settings.gradle
frontend/src-tauri/gen/android/.tauri/**
frontend/src-tauri/gen/android/app/tauri.build.gradle.kts
frontend/src-tauri/gen/android/app/src/main/assets/tauri.conf.json
frontend/src-tauri/gen/android/app/src/main/java/**/generated/**
frontend/src-tauri/gen/android/app/proguard-tauri.pro
retention-days: 1
- name: Upload Tauri Android generated files
working-directory: ./frontend
uses: actions/upload-artifact@v4
with:
name: tauri-android-generated
path: |
src-tauri/gen/android/tauri.settings.gradle
src-tauri/gen/android/.tauri/**
src-tauri/gen/android/app/tauri.build.gradle.kts
src-tauri/gen/android/app/src/main/assets/tauri.conf.json
src-tauri/gen/android/app/src/main/java/**/generated/**
src-tauri/gen/android/app/proguard-tauri.pro

Then update line 348 to: cp -R tauri-android-generated/src-tauri/gen/android/. frontend/src-tauri/gen/android/

Prompt To Fix With AI
This is a comment left during a code review.
Path: .github/workflows/android-build.yml
Line: 137:144

Comment:
Artifact upload paths will cause incorrect directory structure when downloaded.

When uploading with paths like `frontend/src-tauri/gen/android/tauri.settings.gradle`, the artifact preserves this full path. When downloaded to `tauri-android-generated`, it extracts as `tauri-android-generated/frontend/src-tauri/gen/android/...`.

Then at line 348, `cp -R tauri-android-generated/. frontend/src-tauri/gen/android/` copies all contents, resulting in nested paths: `frontend/src-tauri/gen/android/frontend/src-tauri/gen/android/...`

Fix by adding `working-directory`:

```suggestion
      - name: Upload Tauri Android generated files
        working-directory: ./frontend
        uses: actions/upload-artifact@v4
        with:
          name: tauri-android-generated
          path: |
            src-tauri/gen/android/tauri.settings.gradle
            src-tauri/gen/android/.tauri/**
            src-tauri/gen/android/app/tauri.build.gradle.kts
            src-tauri/gen/android/app/src/main/assets/tauri.conf.json
            src-tauri/gen/android/app/src/main/java/**/generated/**
            src-tauri/gen/android/app/proguard-tauri.pro
```

Then update line 348 to: `cp -R tauri-android-generated/src-tauri/gen/android/. frontend/src-tauri/gen/android/`

How can I resolve this? If you propose a fix, please make it concise.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
@AnthonyRonning AnthonyRonning force-pushed the upgrade-github-actions-runners branch from e1b2c4c to 79e2d43 Compare January 9, 2026 19:04
Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

Greptile Overview

Greptile Summary

Refactors Android build pipeline into four modular jobs that run in parallel for faster builds. Frontend is built once and shared, Rust compilation happens in parallel for all 4 Android targets (aarch64, armv7, i686, x86_64), and Gradle packages everything together.

Major changes:

  • Frontend build separated into its own job with artifact sharing
  • New generate-gradle-files job creates Tauri Android metadata without compiling Rust
  • Rust builds parallelized using matrix strategy with per-target caching
  • RustPlugin.kt updated with -PskipRustBuild flag to skip Rust compilation during Gradle packaging
  • Cross-compilation environment setup moved to individual build-rust jobs

Issues found:

  • Missing environment variables during frontend build (lines 40-42) - Vite won't embed VITE_* vars
  • cargo fetch missing --locked flag (line 92) - may cause dependency inconsistencies
  • skipRustBuild property logic flaw (RustPlugin.kt:20-23) - would activate even with -PskipRustBuild=false
  • No verification that .so files exist before upload/copy operations
  • Artifact download patterns may create unexpected directory nesting

Confidence Score: 3/5

  • This PR has significant build improvements but contains critical bugs that will cause build failures
  • The refactoring is well-structured with good parallelization strategy, but has two critical issues: (1) missing environment variables during frontend build will produce incorrect builds, and (2) the skipRustBuild property check logic is flawed. Additional concerns include missing error handling for artifact operations and potential directory structure mismatches.
  • Pay close attention to .github/workflows/android-build.yml lines 40-42 (missing env vars) and 92 (cargo fetch), and RustPlugin.kt lines 20-23 (property check logic)

Important Files Changed

File Analysis

Filename Score Overview
.github/workflows/android-build.yml 3/5 Refactored Android build into modular jobs with parallel Rust builds. Found issues with env vars, artifact handling, and error checking.
frontend/src-tauri/gen/android/buildSrc/src/main/java/cloud/opensecret/maple/kotlin/RustPlugin.kt 3/5 Added skipRustBuild flag to allow Gradle packaging without rebuilding Rust. Logic has potential issue with property checking.

Sequence Diagram

sequenceDiagram
    participant GH as GitHub Actions
    participant Frontend as build-frontend
    participant Gradle as generate-gradle-files
    participant Rust1 as build-rust (aarch64)
    participant Rust2 as build-rust (armv7)
    participant Rust3 as build-rust (i686)
    participant Rust4 as build-rust (x86_64)
    participant Package as package-android
    
    GH->>Frontend: Start frontend build
    GH->>Gradle: Start Gradle file generation
    Frontend->>Frontend: bun install & build
    Frontend->>GH: Upload frontend/dist artifact
    
    Gradle->>Gradle: cargo tauri android init --ci
    Gradle->>Gradle: Copy generated files to stable path
    Gradle->>GH: Upload tauri-android-generated artifact
    
    Frontend->>GH: Frontend complete
    GH->>Rust1: Start Rust build (parallel)
    GH->>Rust2: Start Rust build (parallel)
    GH->>Rust3: Start Rust build (parallel)
    GH->>Rust4: Start Rust build (parallel)
    
    Rust1->>Rust1: Download frontend/dist
    Rust1->>Rust1: cargo build --target aarch64
    Rust1->>GH: Upload android-lib-aarch64 artifact
    
    Rust2->>Rust2: Download frontend/dist
    Rust2->>Rust2: cargo build --target armv7
    Rust2->>GH: Upload android-lib-armv7 artifact
    
    Rust3->>Rust3: Download frontend/dist
    Rust3->>Rust3: cargo build --target i686
    Rust3->>GH: Upload android-lib-i686 artifact
    
    Rust4->>Rust4: Download frontend/dist
    Rust4->>Rust4: cargo build --target x86_64
    Rust4->>GH: Upload android-lib-x86_64 artifact
    
    Gradle->>GH: Gradle complete
    Rust1->>GH: All Rust builds complete
    
    GH->>Package: Start packaging
    Package->>Package: Download frontend/dist
    Package->>Package: Download all android-lib-* artifacts
    Package->>Package: Download tauri-android-generated
    Package->>Package: Place generated files & .so libs
    Package->>Package: gradlew assembleUniversalRelease -PskipRustBuild=true
    Package->>GH: Upload APK and AAB artifacts
Loading

Comment on lines +40 to +42
- name: Build frontend
working-directory: ./frontend
run: bun run build
Copy link
Contributor

Choose a reason for hiding this comment

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

Frontend build doesn't use the workflow-level env vars. The VITE_* environment variables defined at lines 10-12 need to be available during the build step for Vite to embed them.

Suggested change
- name: Build frontend
working-directory: ./frontend
run: bun run build
- name: Build frontend
working-directory: ./frontend
run: bun run build
env:
VITE_OPEN_SECRET_API_URL: ${{ env.VITE_OPEN_SECRET_API_URL }}
VITE_MAPLE_BILLING_API_URL: ${{ env.VITE_MAPLE_BILLING_API_URL }}
VITE_CLIENT_ID: ${{ env.VITE_CLIENT_ID }}
Prompt To Fix With AI
This is a comment left during a code review.
Path: .github/workflows/android-build.yml
Line: 40:42

Comment:
Frontend build doesn't use the workflow-level env vars. The `VITE_*` environment variables defined at lines 10-12 need to be available during the build step for Vite to embed them.

```suggestion
      - name: Build frontend
        working-directory: ./frontend
        run: bun run build
        env:
          VITE_OPEN_SECRET_API_URL: ${{ env.VITE_OPEN_SECRET_API_URL }}
          VITE_MAPLE_BILLING_API_URL: ${{ env.VITE_MAPLE_BILLING_API_URL }}
          VITE_CLIENT_ID: ${{ env.VITE_CLIENT_ID }}
```

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +373 to +377
- name: Download all native libraries
uses: actions/download-artifact@v4
with:
pattern: android-lib-*
path: native-libs
Copy link
Contributor

Choose a reason for hiding this comment

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

Artifact download with pattern: android-lib-* creates nested directory structure. Each artifact will be in its own subdirectory under native-libs/, potentially like native-libs/android-lib-aarch64/android-lib-aarch64/*.so. Verify the actual structure matches expectations at line 401-404.

Prompt To Fix With AI
This is a comment left during a code review.
Path: .github/workflows/android-build.yml
Line: 373:377

Comment:
Artifact download with `pattern: android-lib-*` creates nested directory structure. Each artifact will be in its own subdirectory under `native-libs/`, potentially like `native-libs/android-lib-aarch64/android-lib-aarch64/*.so`. Verify the actual structure matches expectations at line 401-404.

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +317 to +322
- name: Upload native library
uses: actions/upload-artifact@v4
with:
name: android-lib-${{ matrix.short_name }}
path: frontend/src-tauri/target/${{ matrix.target }}/release/*.so
retention-days: 1
Copy link
Contributor

Choose a reason for hiding this comment

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

No verification that .so files exist before upload. If cargo build succeeds but doesn't produce expected libraries, upload will fail silently or upload nothing.

Add verification:

- name: Verify library was built
  run: |
    if [ ! -f frontend/src-tauri/target/${{ matrix.target }}/release/*.so ]; then
      echo "ERROR: No .so files found"
      exit 1
    fi
    ls -la frontend/src-tauri/target/${{ matrix.target }}/release/*.so
Prompt To Fix With AI
This is a comment left during a code review.
Path: .github/workflows/android-build.yml
Line: 317:322

Comment:
No verification that `.so` files exist before upload. If cargo build succeeds but doesn't produce expected libraries, upload will fail silently or upload nothing.

Add verification:
```
- name: Verify library was built
  run: |
    if [ ! -f frontend/src-tauri/target/${{ matrix.target }}/release/*.so ]; then
      echo "ERROR: No .so files found"
      exit 1
    fi
    ls -la frontend/src-tauri/target/${{ matrix.target }}/release/*.so
```

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +395 to +408
- name: Place native libraries in jniLibs
run: |
# Create jniLibs directories
mkdir -p frontend/src-tauri/gen/android/app/src/main/jniLibs/{arm64-v8a,armeabi-v7a,x86,x86_64}
# Copy libraries to correct locations
cp native-libs/android-lib-aarch64/*.so frontend/src-tauri/gen/android/app/src/main/jniLibs/arm64-v8a/
cp native-libs/android-lib-armv7/*.so frontend/src-tauri/gen/android/app/src/main/jniLibs/armeabi-v7a/
cp native-libs/android-lib-i686/*.so frontend/src-tauri/gen/android/app/src/main/jniLibs/x86/
cp native-libs/android-lib-x86_64/*.so frontend/src-tauri/gen/android/app/src/main/jniLibs/x86_64/
# Verify
echo "=== Native libraries placed ==="
find frontend/src-tauri/gen/android/app/src/main/jniLibs -name "*.so" -exec ls -la {} \;
Copy link
Contributor

Choose a reason for hiding this comment

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

No error handling if .so files are missing from any architecture. The cp commands will fail but won't stop execution without set -e.

Add verification before Gradle build:

- name: Verify all native libraries
  run: |
    MISSING=0
    for arch in arm64-v8a armeabi-v7a x86 x86_64; do
      if [ ! -f frontend/src-tauri/gen/android/app/src/main/jniLibs/$arch/*.so ]; then
        echo "ERROR: Missing .so for $arch"
        MISSING=1
      fi
    done
    if [ $MISSING -eq 1 ]; then exit 1; fi
Prompt To Fix With AI
This is a comment left during a code review.
Path: .github/workflows/android-build.yml
Line: 395:408

Comment:
No error handling if `.so` files are missing from any architecture. The `cp` commands will fail but won't stop execution without `set -e`.

Add verification before Gradle build:
```
- name: Verify all native libraries
  run: |
    MISSING=0
    for arch in arm64-v8a armeabi-v7a x86 x86_64; do
      if [ ! -f frontend/src-tauri/gen/android/app/src/main/jniLibs/$arch/*.so ]; then
        echo "ERROR: Missing .so for $arch"
        MISSING=1
      fi
    done
    if [ $MISSING -eq 1 ]; then exit 1; fi
```

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +86 to +95
- name: Generate Tauri Android files
working-directory: ./frontend
run: |
set -euo pipefail
# Ensure all Tauri + plugin crates exist in the cargo registry so tauri.settings.gradle can be generated.
cargo fetch --locked --manifest-path src-tauri/Cargo.toml
# Generate Android project + required autogen files without compiling Rust.
cargo tauri android init --ci
Copy link
Contributor

Choose a reason for hiding this comment

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

Missing --locked flag on cargo fetch. Without it, Cargo may update dependencies beyond what's in Cargo.lock, causing inconsistencies between this job and the build-rust jobs.

Suggested change
- name: Generate Tauri Android files
working-directory: ./frontend
run: |
set -euo pipefail
# Ensure all Tauri + plugin crates exist in the cargo registry so tauri.settings.gradle can be generated.
cargo fetch --locked --manifest-path src-tauri/Cargo.toml
# Generate Android project + required autogen files without compiling Rust.
cargo tauri android init --ci
# Ensure all Tauri + plugin crates exist in the cargo registry so tauri.settings.gradle can be generated.
cargo fetch --locked --manifest-path src-tauri/Cargo.toml
# Generate Android project + required autogen files without compiling Rust.
cargo tauri android init --ci
Prompt To Fix With AI
This is a comment left during a code review.
Path: .github/workflows/android-build.yml
Line: 86:95

Comment:
Missing `--locked` flag on `cargo fetch`. Without it, Cargo may update dependencies beyond what's in `Cargo.lock`, causing inconsistencies between this job and the `build-rust` jobs.

```suggestion
          # Ensure all Tauri + plugin crates exist in the cargo registry so tauri.settings.gradle can be generated.
          cargo fetch --locked --manifest-path src-tauri/Cargo.toml

          # Generate Android project + required autogen files without compiling Rust.
          cargo tauri android init --ci
```

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +20 to +23
val skipRustBuild =
(findProperty("skipRustBuild") as? String)?.toBooleanStrictOrNull() == true ||
(findProperty("skipRustBuild") as? Boolean) == true ||
hasProperty("skipRustBuild")
Copy link
Contributor

Choose a reason for hiding this comment

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

hasProperty("skipRustBuild") on line 23 returns true even if property is explicitly set to "false". Using -PskipRustBuild=false would still skip the build.

Suggested change
val skipRustBuild =
(findProperty("skipRustBuild") as? String)?.toBooleanStrictOrNull() == true ||
(findProperty("skipRustBuild") as? Boolean) == true ||
hasProperty("skipRustBuild")
val skipRustBuild =
(findProperty("skipRustBuild") as? String)?.toBooleanStrictOrNull() == true ||
(findProperty("skipRustBuild") as? Boolean) == true
Prompt To Fix With AI
This is a comment left during a code review.
Path: frontend/src-tauri/gen/android/buildSrc/src/main/java/cloud/opensecret/maple/kotlin/RustPlugin.kt
Line: 20:23

Comment:
`hasProperty("skipRustBuild")` on line 23 returns true even if property is explicitly set to "false". Using `-PskipRustBuild=false` would still skip the build.

```suggestion
        val skipRustBuild =
            (findProperty("skipRustBuild") as? String)?.toBooleanStrictOrNull() == true ||
                (findProperty("skipRustBuild") as? Boolean) == true
```

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +385 to +393
- name: Place Tauri Android generated files
run: |
# Copy generated files into the checked out Android project
cp -R tauri-android-generated/. frontend/src-tauri/gen/android/
echo "=== Tauri generated files placed ==="
ls -la frontend/src-tauri/gen/android/tauri.settings.gradle
ls -la frontend/src-tauri/gen/android/app/tauri.build.gradle.kts
ls -la frontend/src-tauri/gen/android/app/src/main/assets/tauri.conf.json
Copy link
Contributor

Choose a reason for hiding this comment

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

Using cp -R with trailing /. copies directory contents, but if the source path structure doesn't match expectations (e.g., if tauri-android-generated has an extra nesting level), files may not end up in the right place. Add verification after copy to ensure critical files exist.

Prompt To Fix With AI
This is a comment left during a code review.
Path: .github/workflows/android-build.yml
Line: 385:393

Comment:
Using `cp -R` with trailing `/.` copies directory contents, but if the source path structure doesn't match expectations (e.g., if `tauri-android-generated` has an extra nesting level), files may not end up in the right place. Add verification after copy to ensure critical files exist.

How can I resolve this? If you propose a fix, please make it concise.

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.

2 participants