# Android (Tauri v2 mobile) — SEPARATE from desktop.yml because it is a different # surface with a different toolchain, not a different bundle of the same one. The # Rust core is cross-compiled with the NDK and then packaged by the Gradle project # in desktop/src-tauri/gen/android. # # Toolchain comes from the ci-tauri-android image (Rust + the Android ABIs + # SDK/NDK + JDK 17 + tauri-cli); runs-on is only a scheduling label (Label Model B). # # Android, desktop and web are peer surfaces on one quality bar, so this triggers on # the same shared frontend that desktop.yml does — the frontend is compiled INTO the # app by generate_context!, so a frontend commit that skipped this lane would ship a # stale phone build. name: Android (Tauri) on: push: branches: [dev, main] tags: ["v*"] paths: - "desktop/**" - "frontend/**" - ".forgejo/workflows/android.yml" workflow_dispatch: concurrency: group: android-${{ github.ref }} cancel-in-progress: ${{ !startsWith(github.ref, 'refs/tags/') }} permissions: contents: read jobs: build: name: Android APK (debug) runs-on: python-ci container: image: git.fabledsword.com/bvandeusen/ci-tauri-android:1.97 steps: - uses: actions/checkout@v6 # Same reason as the desktop lanes: generate_context! embeds the built # frontend at compile time, so it must exist before cargo runs. - name: Build the shared frontend run: npm ci && npm run build working-directory: frontend # The lockfile gate, matching the desktop lanes (issue 2102). Fetching for the # Android target also pre-warms exactly the crates the build will want. - name: Verify the lockfile and fetch dependencies run: cargo fetch --locked --target aarch64-linux-android working-directory: desktop/src-tauri # DEBUG, and arm64 only, deliberately. # # Debug because a release APK has to be signed, and the release keystore must # be generated by the operator and never pass through CI logs or an agent # session (the constraint recorded on task 2136 for the updater key applies # identically here). Gradle's throwaway debug keystore needs nothing from # anyone, so this lane can prove the app COMPILES and PACKAGES today and grow # a signed release job when a keystore exists. # # arm64 + x86_64. arm64 is every real device; x86_64 is what a desktop # emulator runs, and an arm64-only APK installs there and then dies unable to # load its native library — so a build nobody can try on an emulator is a # build nobody checks. armv7 and i686 are left out: 32-bit hardware we do not # target, and the image carries all four if that changes. - name: Tauri build (Android APK) run: | version="$(sh ../packaging/build-version.sh)" echo "Building version $version" cargo tauri android build \ --debug \ --target aarch64 \ --target x86_64 \ --config '{"build":{"beforeBuildCommand":""}}' \ --config "{\"version\":\"$version\"}" working-directory: desktop/src-tauri # Located rather than hardcoded: the output path depends on the target and # build type, and a wrong literal here would fail as "no files found" long # after the expensive step succeeded. - name: Locate the APK run: | apk="$(find desktop/src-tauri/gen/android/app/build/outputs/apk -name '*.apk' -type f | head -1)" [ -n "$apk" ] || { echo "ERROR: no APK produced" >&2; exit 1; } echo "Built $apk ($(du -h "$apk" | cut -f1))" echo "APK_PATH=$apk" >> "$GITHUB_ENV" # Mirrored action, never actions/upload-artifact — see desktop.yml's Upload # bundles step for the full reasoning. Pinned by SHA because the mirror # auto-syncs. - name: Upload the APK uses: https://git.fabledsword.com/bvandeusen/upload-artifact@cb8afe72b42edc798abfb8fcb556cf660d894245 with: name: thoughtsync-android-debug path: ${{ env.APK_PATH }} if-no-files-found: error