From 798cbdd250819dbbce0306aa7f8a95e7e90b8d3d Mon Sep 17 00:00:00 2001 From: Tony Giorgio Date: Fri, 9 Jan 2026 13:59:32 -0600 Subject: [PATCH 1/5] ci: parallelize Android Rust builds Split Android CI into frontend build, per-ABI Rust builds, and final Gradle packaging using prebuilt libraries. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> --- .github/workflows/android-build.yml | 259 +++++++++++++----- .../opensecret/maple/kotlin/BuildTask.kt | 30 ++ 2 files changed, 220 insertions(+), 69 deletions(-) diff --git a/.github/workflows/android-build.yml b/.github/workflows/android-build.yml index 3f411f3de..76530c897 100644 --- a/.github/workflows/android-build.yml +++ b/.github/workflows/android-build.yml @@ -7,20 +7,11 @@ on: branches: [ master ] jobs: - build-android: + frontend-build: runs-on: ubuntu-latest-8-cores steps: - uses: actions/checkout@v4 - - name: Setup Java - uses: actions/setup-java@v4 - with: - distribution: 'temurin' - java-version: '21' - - - name: Setup Android SDK - uses: android-actions/setup-android@v3 - - name: Setup Bun uses: oven-sh/setup-bun@v1 with: @@ -36,10 +27,87 @@ jobs: 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 + env: + VITE_OPEN_SECRET_API_URL: ${{ github.event_name == 'pull_request' && 'https://enclave.secretgpt.ai' || 'https://enclave.trymaple.ai' }} + VITE_MAPLE_BILLING_API_URL: ${{ github.event_name == 'pull_request' && 'https://billing-dev.opensecret.cloud' || 'https://billing.opensecret.cloud' }} + VITE_CLIENT_ID: ba5a14b5-d915-47b1-b7b1-afda52bc5fc6 + + - name: Upload frontend dist + uses: actions/upload-artifact@v4 + with: + name: frontend-dist + path: frontend/dist + retention-days: 1 + + rust-build: + needs: frontend-build + runs-on: ubuntu-latest-8-cores + strategy: + fail-fast: false + matrix: + include: + - rust_target: aarch64-linux-android + target_env: aarch64_linux_android + cargo_target_env: AARCH64_LINUX_ANDROID + abi: arm64-v8a + cc: aarch64-linux-android24-clang + cxx: aarch64-linux-android24-clang++ + ranlib_link: aarch64-linux-android-ranlib + - rust_target: armv7-linux-androideabi + target_env: armv7_linux_androideabi + cargo_target_env: ARMV7_LINUX_ANDROIDEABI + abi: armeabi-v7a + cc: armv7a-linux-androideabi24-clang + cxx: armv7a-linux-androideabi24-clang++ + ranlib_link: armv7a-linux-androideabi-ranlib + - rust_target: i686-linux-android + target_env: i686_linux_android + cargo_target_env: I686_LINUX_ANDROID + abi: x86 + cc: i686-linux-android24-clang + cxx: i686-linux-android24-clang++ + ranlib_link: i686-linux-android-ranlib + - rust_target: x86_64-linux-android + target_env: x86_64_linux_android + cargo_target_env: X86_64_LINUX_ANDROID + abi: x86_64 + cc: x86_64-linux-android24-clang + cxx: x86_64-linux-android24-clang++ + ranlib_link: x86_64-linux-android-ranlib + + steps: + - uses: actions/checkout@v4 + + - name: Download frontend dist + uses: actions/download-artifact@v4 + with: + name: frontend-dist + path: frontend-dist + + - name: Restore frontend dist + run: | + rm -rf frontend/dist + mkdir -p frontend/dist + + if [ -d frontend-dist/dist ]; then + cp -a frontend-dist/dist/. frontend/dist/ + elif [ -d frontend-dist/frontend/dist ]; then + cp -a frontend-dist/frontend/dist/. frontend/dist/ + else + cp -a frontend-dist/. frontend/dist/ + fi + - name: Install Rust uses: dtolnay/rust-toolchain@stable with: - targets: aarch64-linux-android,armv7-linux-androideabi,x86_64-linux-android,i686-linux-android + targets: ${{ matrix.rust_target }} - name: Setup Android NDK uses: nttld/setup-ndk@v1 @@ -48,16 +116,6 @@ jobs: ndk-version: r27c add-to-path: true - - 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- - - name: Install sccache run: | SCCACHE_VERSION=0.8.2 @@ -71,11 +129,90 @@ jobs: uses: actions/cache@v4 with: path: ~/.cache/sccache - key: ${{ runner.os }}-sccache-android-${{ hashFiles('**/Cargo.lock') }} + key: ${{ runner.os }}-sccache-android-${{ matrix.rust_target }}-${{ hashFiles('**/Cargo.lock') }} restore-keys: | + ${{ runner.os }}-sccache-android-${{ matrix.rust_target }}- ${{ runner.os }}-sccache-android- ${{ runner.os }}-sccache- + - name: Install Linux dependencies + run: | + sudo apt-get update + sudo apt-get install -y libssl-dev + + - name: Configure sccache + run: | + echo "RUSTC_WRAPPER=sccache" >> $GITHUB_ENV + echo "SCCACHE_DIR=$HOME/.cache/sccache" >> $GITHUB_ENV + echo "SCCACHE_CACHE_SIZE=2G" >> $GITHUB_ENV + + - name: Build Rust library (${{ matrix.rust_target }}) + run: | + export NDK_HOME=${{ steps.setup-ndk.outputs.ndk-path }} + export PATH=$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH + + # C toolchain env for build scripts (e.g. openssl-sys) + export AR_${{ matrix.target_env }}=$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ar + export CC_${{ matrix.target_env }}=$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/${{ matrix.cc }} + export CXX_${{ matrix.target_env }}=$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/${{ matrix.cxx }} + export RANLIB_${{ matrix.target_env }}=$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ranlib + + # Explicit linker for cargo (belt + suspenders) + export CARGO_TARGET_${{ matrix.cargo_target_env }}_LINKER=$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/${{ matrix.cc }} + + # Create ranlib symlink that OpenSSL expects + sudo ln -sf $NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ranlib /usr/local/bin/${{ matrix.ranlib_link }} + + cargo build --manifest-path frontend/src-tauri/Cargo.toml --release --target ${{ matrix.rust_target }} + + mkdir -p artifacts + cp frontend/src-tauri/target/${{ matrix.rust_target }}/release/libapp_lib.so artifacts/libapp_lib-${{ matrix.abi }}.so + + - name: Show sccache stats + run: sccache --show-stats + + - name: Upload Rust library (${{ matrix.abi }}) + uses: actions/upload-artifact@v4 + with: + name: rust-lib-${{ matrix.abi }} + path: artifacts/libapp_lib-${{ matrix.abi }}.so + retention-days: 1 + + build-android: + needs: rust-build + runs-on: ubuntu-latest-8-cores + steps: + - uses: actions/checkout@v4 + + - name: Setup Java + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '21' + + - name: Setup Android SDK + uses: android-actions/setup-android@v3 + + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + + - name: Setup Android NDK + uses: nttld/setup-ndk@v1 + id: setup-ndk + with: + ndk-version: r27c + add-to-path: true + + - 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- + - name: Cache Cargo bin (Tauri CLI) uses: actions/cache@v4 with: @@ -90,10 +227,6 @@ jobs: sudo apt-get update sudo apt-get install -y libssl-dev - - name: Install frontend dependencies - working-directory: ./frontend - run: bun install - - name: Install Tauri CLI run: | if ! command -v cargo-tauri &> /dev/null; then @@ -104,6 +237,17 @@ jobs: env: CARGO_CFG_TARGET_OS: linux + - name: Initialize Tauri Android project + working-directory: ./frontend + run: | + export ANDROID_HOME=$ANDROID_SDK_ROOT + cargo tauri android init --ci --skip-targets-install + + - name: Verify Tauri Android Gradle files + run: | + test -f frontend/src-tauri/gen/android/tauri.settings.gradle + test -f frontend/src-tauri/gen/android/app/tauri.build.gradle.kts + - name: Setup Android signing env: ANDROID_KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }} @@ -132,56 +276,33 @@ jobs: echo "Generated tauri.properties with version $VERSION and versionCode $VERSION_CODE" - - name: Configure sccache + - name: Download Rust libraries + uses: actions/download-artifact@v4 + with: + pattern: rust-lib-* + merge-multiple: true + path: rust-libs + + - name: Install prebuilt Rust libraries run: | - echo "RUSTC_WRAPPER=sccache" >> $GITHUB_ENV - echo "SCCACHE_DIR=$HOME/.cache/sccache" >> $GITHUB_ENV - echo "SCCACHE_CACHE_SIZE=2G" >> $GITHUB_ENV + mkdir -p frontend/src-tauri/gen/android/app/src/main/jniLibs/arm64-v8a + mkdir -p frontend/src-tauri/gen/android/app/src/main/jniLibs/armeabi-v7a + mkdir -p frontend/src-tauri/gen/android/app/src/main/jniLibs/x86 + mkdir -p frontend/src-tauri/gen/android/app/src/main/jniLibs/x86_64 - - name: Build Tauri Android App (Signed Release) - working-directory: ./frontend + cp rust-libs/libapp_lib-arm64-v8a.so frontend/src-tauri/gen/android/app/src/main/jniLibs/arm64-v8a/libapp_lib.so + cp rust-libs/libapp_lib-armeabi-v7a.so frontend/src-tauri/gen/android/app/src/main/jniLibs/armeabi-v7a/libapp_lib.so + cp rust-libs/libapp_lib-x86.so frontend/src-tauri/gen/android/app/src/main/jniLibs/x86/libapp_lib.so + cp rust-libs/libapp_lib-x86_64.so frontend/src-tauri/gen/android/app/src/main/jniLibs/x86_64/libapp_lib.so + + - name: Build Android APK + AAB (Signed Release) + working-directory: ./frontend/src-tauri/gen/android run: | export ANDROID_HOME=$ANDROID_SDK_ROOT export NDK_HOME=${{ steps.setup-ndk.outputs.ndk-path }} - - # Set up Android NDK toolchain paths export PATH=$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH - # Set up cross-compilation environment variables for each target - export AR_aarch64_linux_android=$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ar - export CC_aarch64_linux_android=$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android24-clang - export CXX_aarch64_linux_android=$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android24-clang++ - export RANLIB_aarch64_linux_android=$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ranlib - - export AR_armv7_linux_androideabi=$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ar - export CC_armv7_linux_androideabi=$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi24-clang - export CXX_armv7_linux_androideabi=$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi24-clang++ - export RANLIB_armv7_linux_androideabi=$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ranlib - - export AR_x86_64_linux_android=$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ar - export CC_x86_64_linux_android=$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/x86_64-linux-android24-clang - export CXX_x86_64_linux_android=$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/x86_64-linux-android24-clang++ - export RANLIB_x86_64_linux_android=$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ranlib - - export AR_i686_linux_android=$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ar - export CC_i686_linux_android=$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/i686-linux-android24-clang - export CXX_i686_linux_android=$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/i686-linux-android24-clang++ - export RANLIB_i686_linux_android=$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ranlib - - # Create symlinks for ranlib that OpenSSL expects - 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/local/bin/armv7a-linux-androideabi-ranlib - 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/local/bin/i686-linux-android-ranlib - - cargo tauri android build - env: - VITE_OPEN_SECRET_API_URL: ${{ github.event_name == 'pull_request' && 'https://enclave.secretgpt.ai' || 'https://enclave.trymaple.ai' }} - VITE_MAPLE_BILLING_API_URL: ${{ github.event_name == 'pull_request' && 'https://billing-dev.opensecret.cloud' || 'https://billing.opensecret.cloud' }} - VITE_CLIENT_ID: ba5a14b5-d915-47b1-b7b1-afda52bc5fc6 - - - name: Show sccache stats - run: sccache --show-stats + ./gradlew :app:assembleUniversalRelease :app:bundleUniversalRelease -PskipRustBuild=true - name: Upload Android APK uses: actions/upload-artifact@v4 diff --git a/frontend/src-tauri/gen/android/buildSrc/src/main/java/cloud/opensecret/maple/kotlin/BuildTask.kt b/frontend/src-tauri/gen/android/buildSrc/src/main/java/cloud/opensecret/maple/kotlin/BuildTask.kt index 020dbae4b..2364a73d6 100644 --- a/frontend/src-tauri/gen/android/buildSrc/src/main/java/cloud/opensecret/maple/kotlin/BuildTask.kt +++ b/frontend/src-tauri/gen/android/buildSrc/src/main/java/cloud/opensecret/maple/kotlin/BuildTask.kt @@ -16,6 +16,19 @@ open class BuildTask : DefaultTask() { @TaskAction fun assemble() { + if (shouldSkipRustBuild()) { + val target = target ?: throw GradleException("target cannot be null") + val lib = expectedJniLib(target) + if (!lib.exists()) { + throw GradleException( + "skipRustBuild=true but prebuilt Rust library is missing at ${lib.absolutePath}" + ) + } + + project.logger.lifecycle("skipRustBuild=true; using prebuilt Rust library at ${lib.absolutePath}") + return + } + val userHome = System.getProperty("user.home") val executable = if (File("$userHome/.bun/bin/bun").exists()) { "$userHome/.bun/bin/bun" @@ -33,6 +46,23 @@ open class BuildTask : DefaultTask() { } } + private fun shouldSkipRustBuild(): Boolean { + val raw = project.findProperty("skipRustBuild")?.toString() ?: return false + return raw.equals("true", ignoreCase = true) || raw == "1" || raw.equals("yes", ignoreCase = true) + } + + private fun expectedJniLib(target: String): File { + val abi = when (target) { + "aarch64" -> "arm64-v8a" + "armv7" -> "armeabi-v7a" + "i686" -> "x86" + "x86_64" -> "x86_64" + else -> throw GradleException("Unknown target '$target'") + } + + return File(project.projectDir, "src/main/jniLibs/$abi/libapp_lib.so") + } + fun runTauriCli(executable: String) { val rootDirRel = rootDirRel ?: throw GradleException("rootDirRel cannot be null") val target = target ?: throw GradleException("target cannot be null") From 7e2213e7cc09bfb454a410694ba0b9674f5ac62c Mon Sep 17 00:00:00 2001 From: Tony Giorgio Date: Fri, 9 Jan 2026 14:19:42 -0600 Subject: [PATCH 2/5] ci: avoid building Android bin in rust matrix Build only the app_lib library in the per-target Rust jobs to avoid i686 linker failures. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> --- .github/workflows/android-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/android-build.yml b/.github/workflows/android-build.yml index 76530c897..d7e3a41c9 100644 --- a/.github/workflows/android-build.yml +++ b/.github/workflows/android-build.yml @@ -163,7 +163,7 @@ jobs: # Create ranlib symlink that OpenSSL expects sudo ln -sf $NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ranlib /usr/local/bin/${{ matrix.ranlib_link }} - cargo build --manifest-path frontend/src-tauri/Cargo.toml --release --target ${{ matrix.rust_target }} + cargo build --manifest-path frontend/src-tauri/Cargo.toml --release --target ${{ matrix.rust_target }} --lib mkdir -p artifacts cp frontend/src-tauri/target/${{ matrix.rust_target }}/release/libapp_lib.so artifacts/libapp_lib-${{ matrix.abi }}.so From ad92e007e5083ba177a0e77b59449d2605ab8edb Mon Sep 17 00:00:00 2001 From: Tony Giorgio Date: Fri, 9 Jan 2026 14:40:38 -0600 Subject: [PATCH 3/5] ci: generate Tauri Android gradle files in packaging job Replace cargo tauri android init + file existence check with a cargo metadata-based generator for tauri.settings.gradle and tauri.build.gradle.kts. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> --- .github/workflows/android-build.yml | 69 ++++++++++++++++++----------- 1 file changed, 44 insertions(+), 25 deletions(-) diff --git a/.github/workflows/android-build.yml b/.github/workflows/android-build.yml index d7e3a41c9..2f9cc0681 100644 --- a/.github/workflows/android-build.yml +++ b/.github/workflows/android-build.yml @@ -213,40 +213,59 @@ jobs: restore-keys: | ${{ runner.os }}-gradle- - - name: Cache Cargo bin (Tauri CLI) - uses: actions/cache@v4 - with: - path: ~/.cargo/bin/ - key: ${{ runner.os }}-cargo-bin-tauri-cli-2.9.2 - restore-keys: | - ${{ runner.os }}-cargo-bin-tauri-cli- - ${{ runner.os }}-cargo-bin- - - name: Install Linux dependencies run: | sudo apt-get update sudo apt-get install -y libssl-dev - - name: Install Tauri CLI + - name: Fetch Rust dependencies (for Android Gradle plugin sources) + run: cargo fetch --manifest-path frontend/src-tauri/Cargo.toml + + - name: Generate Tauri Android Gradle includes run: | - if ! command -v cargo-tauri &> /dev/null; then - cargo install tauri-cli --version "2.9.2" --locked - else - echo "Tauri CLI already installed" + set -euo pipefail + + META=$(cargo metadata --format-version 1 --manifest-path frontend/src-tauri/Cargo.toml) + + SETTINGS_FILE=frontend/src-tauri/gen/android/tauri.settings.gradle + BUILD_FILE=frontend/src-tauri/gen/android/app/tauri.build.gradle.kts + + mkdir -p "$(dirname "$SETTINGS_FILE")" "$(dirname "$BUILD_FILE")" + + echo "// THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY." > "$SETTINGS_FILE" + echo "// THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY." > "$BUILD_FILE" + echo "val implementation by configurations" >> "$BUILD_FILE" + echo "dependencies {" >> "$BUILD_FILE" + + TAURI_MANIFEST=$(echo "$META" | jq -r '.packages[] | select(.name=="tauri") | .manifest_path' | head -n1) + if [ -z "$TAURI_MANIFEST" ] || [ "$TAURI_MANIFEST" = "null" ]; then + echo "Failed to locate the 'tauri' crate via cargo metadata" + exit 1 fi - env: - CARGO_CFG_TARGET_OS: linux - - name: Initialize Tauri Android project - working-directory: ./frontend - run: | - export ANDROID_HOME=$ANDROID_SDK_ROOT - cargo tauri android init --ci --skip-targets-install + TAURI_DIR=$(dirname "$TAURI_MANIFEST") + TAURI_ANDROID_DIR="$TAURI_DIR/mobile/android" + if [ ! -d "$TAURI_ANDROID_DIR" ]; then + echo "Expected Tauri Android project dir not found: $TAURI_ANDROID_DIR" + exit 1 + fi - - name: Verify Tauri Android Gradle files - run: | - test -f frontend/src-tauri/gen/android/tauri.settings.gradle - test -f frontend/src-tauri/gen/android/app/tauri.build.gradle.kts + echo "include ':tauri-android'" >> "$SETTINGS_FILE" + echo "project(':tauri-android').projectDir = new File(\"$TAURI_ANDROID_DIR\")" >> "$SETTINGS_FILE" + echo " implementation(project(\":tauri-android\"))" >> "$BUILD_FILE" + + echo "$META" | jq -r '.packages[] | select(.name | startswith("tauri-plugin-")) | [.name, .manifest_path] | @tsv' | + while IFS=$'\t' read -r name manifest; do + dir=$(dirname "$manifest") + android_dir="$dir/android" + if [ -d "$android_dir" ]; then + echo "include ':$name'" >> "$SETTINGS_FILE" + echo "project(':$name').projectDir = new File(\"$android_dir\")" >> "$SETTINGS_FILE" + echo " implementation(project(\":$name\"))" >> "$BUILD_FILE" + fi + done + + echo "}" >> "$BUILD_FILE" - name: Setup Android signing env: From 620cf338c2c101e66353cfdec6886dfe325d3794 Mon Sep 17 00:00:00 2001 From: Tony Giorgio Date: Fri, 9 Jan 2026 14:59:06 -0600 Subject: [PATCH 4/5] ci: generate Android kotlin bindings for Gradle build Generate Wry/Tauri Kotlin sources and mobile assets (tauri.conf.json, proguard-tauri.pro) in the packaging job so we can skip Tauri CLI + Rust compilation. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> --- .github/workflows/android-build.yml | 74 +++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/.github/workflows/android-build.yml b/.github/workflows/android-build.yml index 2f9cc0681..0a7dc2075 100644 --- a/.github/workflows/android-build.yml +++ b/.github/workflows/android-build.yml @@ -267,6 +267,80 @@ jobs: echo "}" >> "$BUILD_FILE" + - name: Generate Android Kotlin bindings + assets (wry/tauri) + run: | + set -euo pipefail + + PACKAGE=$(jq -r '.identifier' frontend/src-tauri/tauri.conf.json) + if [ -z "$PACKAGE" ] || [ "$PACKAGE" = "null" ]; then + echo "Failed to read .identifier from frontend/src-tauri/tauri.conf.json" + exit 1 + fi + + PACKAGE_DIR=$(echo "$PACKAGE" | tr '.' '/') + GEN_KOTLIN_DIR="frontend/src-tauri/gen/android/app/src/main/java/${PACKAGE_DIR}/generated" + mkdir -p "$GEN_KOTLIN_DIR" + + # The library name (without lib prefix / .so suffix) that Wry/Tauri load via System.loadLibrary(). + LIB_NAME=app_lib + + META=$(cargo metadata --format-version 1 --manifest-path frontend/src-tauri/Cargo.toml) + TAURI_MANIFEST=$(echo "$META" | jq -r '.packages[] | select(.name=="tauri") | .manifest_path' | head -n1) + WRY_MANIFEST=$(echo "$META" | jq -r '.packages[] | select(.name=="wry") | .manifest_path' | head -n1) + + if [ -z "$TAURI_MANIFEST" ] || [ "$TAURI_MANIFEST" = "null" ]; then + echo "Failed to locate the 'tauri' crate via cargo metadata" + exit 1 + fi + if [ -z "$WRY_MANIFEST" ] || [ "$WRY_MANIFEST" = "null" ]; then + echo "Failed to locate the 'wry' crate via cargo metadata" + exit 1 + fi + + TAURI_DIR=$(dirname "$TAURI_MANIFEST") + WRY_DIR=$(dirname "$WRY_MANIFEST") + + render_template() { + local src="$1" + local dst="$2" + sed \ + -e "s/{{package}}/$PACKAGE/g" \ + -e "s/{{library}}/$LIB_NAME/g" \ + -e 's/{{class-init}}//g' \ + -e 's/{{class-extension}}//g' \ + "$src" > "$dst" + } + + # TauriActivity + TAURI_ACTIVITY_TEMPLATE="$TAURI_DIR/mobile/android-codegen/TauriActivity.kt" + if [ ! -f "$TAURI_ACTIVITY_TEMPLATE" ]; then + echo "TauriActivity template not found: $TAURI_ACTIVITY_TEMPLATE" + exit 1 + fi + render_template "$TAURI_ACTIVITY_TEMPLATE" "$GEN_KOTLIN_DIR/TauriActivity.kt" + + # Wry kotlin bindings + for f in Ipc.kt Logger.kt PermissionHelper.kt RustWebChromeClient.kt RustWebView.kt RustWebViewClient.kt WryActivity.kt; do + SRC="$WRY_DIR/src/android/kotlin/$f" + if [ ! -f "$SRC" ]; then + echo "Wry kotlin template not found: $SRC" + exit 1 + fi + render_template "$SRC" "$GEN_KOTLIN_DIR/$f" + done + + # tauri.conf.json asset + mkdir -p frontend/src-tauri/gen/android/app/src/main/assets + cp frontend/src-tauri/tauri.conf.json frontend/src-tauri/gen/android/app/src/main/assets/tauri.conf.json + + # Tauri proguard rules + TAURI_PROGUARD="$TAURI_DIR/mobile/proguard-tauri.pro" + if [ -f "$TAURI_PROGUARD" ]; then + cp "$TAURI_PROGUARD" frontend/src-tauri/gen/android/app/proguard-tauri.pro + else + echo "Tauri proguard file not found (skipping): $TAURI_PROGUARD" + fi + - name: Setup Android signing env: ANDROID_KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }} From c9ba7fecdfa1f49d8fd601dea29104509505264f Mon Sep 17 00:00:00 2001 From: Tony Giorgio Date: Fri, 9 Jan 2026 15:32:22 -0600 Subject: [PATCH 5/5] ci: cut Android build-android setup overhead Cache Cargo registry/git for Rust builds and remove NDK + libssl setup from build-android since we skip Rust compilation there. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> --- .github/workflows/android-build.yml | 39 ++++++++++++++++------------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/.github/workflows/android-build.yml b/.github/workflows/android-build.yml index 0a7dc2075..3ba69f1bb 100644 --- a/.github/workflows/android-build.yml +++ b/.github/workflows/android-build.yml @@ -109,6 +109,17 @@ jobs: with: targets: ${{ matrix.rust_target }} + - name: Cache Cargo (registry + git) + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + key: ${{ runner.os }}-cargo-${{ matrix.rust_target }}-${{ hashFiles('frontend/src-tauri/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-cargo-${{ matrix.rust_target }}- + ${{ runner.os }}-cargo- + - name: Setup Android NDK uses: nttld/setup-ndk@v1 id: setup-ndk @@ -129,17 +140,12 @@ jobs: uses: actions/cache@v4 with: path: ~/.cache/sccache - key: ${{ runner.os }}-sccache-android-${{ matrix.rust_target }}-${{ hashFiles('**/Cargo.lock') }} + key: ${{ runner.os }}-sccache-android-${{ matrix.rust_target }}-${{ hashFiles('frontend/src-tauri/Cargo.lock') }} restore-keys: | ${{ runner.os }}-sccache-android-${{ matrix.rust_target }}- ${{ runner.os }}-sccache-android- ${{ runner.os }}-sccache- - - name: Install Linux dependencies - run: | - sudo apt-get update - sudo apt-get install -y libssl-dev - - name: Configure sccache run: | echo "RUSTC_WRAPPER=sccache" >> $GITHUB_ENV @@ -196,12 +202,16 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@stable - - name: Setup Android NDK - uses: nttld/setup-ndk@v1 - id: setup-ndk + - name: Cache Cargo (registry + git) + uses: actions/cache@v4 with: - ndk-version: r27c - add-to-path: true + path: | + ~/.cargo/registry + ~/.cargo/git + key: ${{ runner.os }}-cargo-android-build-${{ hashFiles('frontend/src-tauri/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-cargo-android-build- + ${{ runner.os }}-cargo- - name: Cache Gradle uses: actions/cache@v4 @@ -213,11 +223,6 @@ jobs: restore-keys: | ${{ runner.os }}-gradle- - - name: Install Linux dependencies - run: | - sudo apt-get update - sudo apt-get install -y libssl-dev - - name: Fetch Rust dependencies (for Android Gradle plugin sources) run: cargo fetch --manifest-path frontend/src-tauri/Cargo.toml @@ -392,8 +397,6 @@ jobs: working-directory: ./frontend/src-tauri/gen/android run: | export ANDROID_HOME=$ANDROID_SDK_ROOT - export NDK_HOME=${{ steps.setup-ndk.outputs.ndk-path }} - export PATH=$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH ./gradlew :app:assembleUniversalRelease :app:bundleUniversalRelease -PskipRustBuild=true