The phone client is Tauri v2 mobile (operator decision), so it reuses the Vue frontend and the Rust store and sync engine that already exist rather than becoming a third implementation to keep in step by hand. gen/android is committed. tauri android init generated it, its own .gitignore already excludes the build outputs and every keystore file, and CI must not have to regenerate a project that manifest edits will accumulate in. What the scaffold confirms is that the image's JDK pin was load-bearing rather than incidental: Tauri templated Gradle 8.14.3 with AGP 8.11.0, and CI-android's versions.env records that JDK 25 needs Gradle 9.1.0+ and that anything older fails with an opaque "25.0.3" message. Picking 17 for ci-tauri-android avoided exactly that. namespace and applicationId came out as com.fabledsword.thoughtsync, matching the desktop identifier, so the app-data story stays consistent. The lane builds a DEBUG APK for arm64 only. Release APKs need signing, and the keystore has to be generated by the operator and never pass through CI logs or an agent session — the constraint recorded for the updater key applies unchanged. Gradle's throwaway debug keystore needs nothing from anyone, so this can prove the app compiles and packages today and grow a signed job when a key exists. arm64 is every real device; the image carries the other three ABIs, so widening is a word. Triggered by frontend/** as well as desktop/**, because generate_context! compiles the frontend into the app — the same reasoning that widened desktop.yml. Android, desktop and web are peers on one quality bar, and a frontend commit that skipped this lane would ship a stale phone build. Green here will mean it BUILT. A Linux runner cannot execute an APK, so nothing in this lane proves the app runs, renders, or is usable by finger. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
95 lines
3.9 KiB
YAML
95 lines
3.9 KiB
YAML
# 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 only because it is every real device; adding the armv7 and two x86
|
|
# emulator ABIs quadruples the build for coverage nothing currently checks.
|
|
# The image carries all four targets, so widening this is a one-word change.
|
|
- name: Tauri build (Android APK)
|
|
run: |
|
|
version="$(sh ../packaging/build-version.sh)"
|
|
echo "Building version $version"
|
|
cargo tauri android build \
|
|
--debug \
|
|
--target aarch64 \
|
|
--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
|