From 3034171704815d45df88823b66615931ff90b100 Mon Sep 17 00:00:00 2001 From: Jacob Stephens Date: Sat, 6 Jun 2026 02:23:22 +0000 Subject: [PATCH 1/2] ci: Android build + release workflows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fills the Android CI gap (Windows/Apple each had a build workflow; Android had none) and automates the APK release that was previously built and uploaded by hand. - android.yml: compile check (cargo-ndk Rust core for all ABIs + Kotlin) on push to main / PR / dispatch — mirrors windows.yml and apple.yml. - release-android.yml: on a v*-android tag, builds the release APK and publishes it to a GitHub Release. Debug-signed (sideload), so no signing secrets needed. The publish is a separate job behind `environment: release`, so a required reviewer can gate every release behind a named human approval — same pattern as deploy-sync.yml. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/android.yml | 54 ++++++++++++++++ .github/workflows/release-android.yml | 92 +++++++++++++++++++++++++++ 2 files changed, 146 insertions(+) create mode 100644 .github/workflows/android.yml create mode 100644 .github/workflows/release-android.yml diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml new file mode 100644 index 0000000..1830b38 --- /dev/null +++ b/.github/workflows/android.yml @@ -0,0 +1,54 @@ +# Builds the Android app (Rust core via cargo-ndk for all ABIs + Kotlin) as a +# compile check. Mirrors windows.yml / apple.yml; the matching release lives in +# release-android.yml. Debug build for speed — the release workflow does the +# optimized, publishable APK. +name: Android build + +on: + push: + branches: [main] + paths: + - "apps/android/**" + - "crates/**" + - "Cargo.toml" + - "Cargo.lock" + - "apps/web/public/sounds/waterfall.ogg" + - ".github/workflows/android.yml" + pull_request: + paths: + - "apps/android/**" + - "crates/**" + - ".github/workflows/android.yml" + workflow_dispatch: + +permissions: + contents: read + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: "17" + - name: Set up Android SDK + NDK + uses: android-actions/setup-android@v3 + - name: Install NDK + platform + run: | + sdkmanager --install "ndk;26.3.11579264" "platforms;android-35" "build-tools;35.0.0" + echo "ANDROID_NDK_HOME=$ANDROID_HOME/ndk/26.3.11579264" >> "$GITHUB_ENV" + - name: Install Rust + Android targets + uses: dtolnay/rust-toolchain@stable + with: + targets: aarch64-linux-android,armv7-linux-androideabi,x86_64-linux-android + - uses: Swatinem/rust-cache@v2 + - name: Install cargo-ndk + uses: taiki-e/install-action@v2 + with: + tool: cargo-ndk + - name: Build debug APK + working-directory: apps/android + run: ./gradlew :app:assembleDebug --no-daemon diff --git a/.github/workflows/release-android.yml b/.github/workflows/release-android.yml new file mode 100644 index 0000000..c1e4253 --- /dev/null +++ b/.github/workflows/release-android.yml @@ -0,0 +1,92 @@ +# Build the Android APK and publish it as a GitHub Release. +# +# Trigger: pushing a tag like v0.2.0-android (the deliberate human act that +# starts a release). workflow_dispatch builds without releasing, for testing. +# +# The release build is debug-signed (see app/build.gradle.kts -> release +# signingConfig), so no signing secrets are needed: a fresh CI run generates a +# debug keystore on first build. The APK is a sideload build, not a Play upload. +# +# The publish step is split into its own job behind `environment: release`, so a +# required reviewer can be added in Settings -> Environments to gate every +# release behind a named human approval (mirrors deploy-sync.yml). +name: Android release + +on: + push: + tags: + - "v*-android" + workflow_dispatch: + +permissions: + contents: write + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: "17" + + - name: Set up Android SDK + NDK + uses: android-actions/setup-android@v3 + - name: Install NDK + platform + run: | + sdkmanager --install "ndk;26.3.11579264" "platforms;android-35" "build-tools;35.0.0" + echo "ANDROID_NDK_HOME=$ANDROID_HOME/ndk/26.3.11579264" >> "$GITHUB_ENV" + + - name: Install Rust + Android targets + uses: dtolnay/rust-toolchain@stable + with: + targets: aarch64-linux-android,armv7-linux-androideabi,x86_64-linux-android + - uses: Swatinem/rust-cache@v2 + - name: Install cargo-ndk + uses: taiki-e/install-action@v2 + with: + tool: cargo-ndk + + - name: Build release APK (Rust core for all ABIs + Kotlin) + working-directory: apps/android + run: ./gradlew :app:assembleRelease --no-daemon + + - name: Stage APK + run: | + src="apps/android/app/build/outputs/apk/release/app-release.apk" + test -f "$src" || { echo "APK not produced: $src"; exit 1; } + name="cascade-${GITHUB_REF_NAME//\//-}.apk" + cp "$src" "$name" + echo "APK_NAME=$name" >> "$GITHUB_ENV" + ls -la "$name" + + - name: Upload APK artifact + uses: actions/upload-artifact@v4 + with: + name: cascade-android-apk + path: ${{ env.APK_NAME }} + if-no-files-found: error + retention-days: 14 + + release: + needs: build + runs-on: ubuntu-latest + # Only publish on a tag; workflow_dispatch just builds (above) as a smoke test. + if: startsWith(github.ref, 'refs/tags/') + environment: release # add a required reviewer here to gate the publish + steps: + - name: Download APK + uses: actions/download-artifact@v4 + with: + name: cascade-android-apk + + - name: Create / update GitHub Release + uses: softprops/action-gh-release@v2 + with: + name: "Cascade ${{ github.ref_name }} (Android)" + files: cascade-*.apk + generate_release_notes: false + fail_on_unmatched_files: true From 18bb8d679ada323557f4b1d1ba6a8fa15b4b0840 Mon Sep 17 00:00:00 2001 From: Jacob Stephens Date: Sat, 6 Jun 2026 02:24:08 +0000 Subject: [PATCH 2/2] ci: install cargo-ndk via cargo install (reliable across runners) Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/android.yml | 4 +--- .github/workflows/release-android.yml | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index 1830b38..b7c3a75 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -46,9 +46,7 @@ jobs: targets: aarch64-linux-android,armv7-linux-androideabi,x86_64-linux-android - uses: Swatinem/rust-cache@v2 - name: Install cargo-ndk - uses: taiki-e/install-action@v2 - with: - tool: cargo-ndk + run: cargo install cargo-ndk --locked - name: Build debug APK working-directory: apps/android run: ./gradlew :app:assembleDebug --no-daemon diff --git a/.github/workflows/release-android.yml b/.github/workflows/release-android.yml index c1e4253..856514a 100644 --- a/.github/workflows/release-android.yml +++ b/.github/workflows/release-android.yml @@ -46,9 +46,7 @@ jobs: targets: aarch64-linux-android,armv7-linux-androideabi,x86_64-linux-android - uses: Swatinem/rust-cache@v2 - name: Install cargo-ndk - uses: taiki-e/install-action@v2 - with: - tool: cargo-ndk + run: cargo install cargo-ndk --locked - name: Build release APK (Rust core for all ABIs + Kotlin) working-directory: apps/android