First Android run failed at plugin resolution: Plugin [id: 'io.gitlab.arturbosch.detekt', version: '2.0.0-alpha.3'] was not found in any of the following sources That version is published to neither Maven Central nor the plugin portal — the latest detekt anywhere is 1.23.8. It was copied from Minstrel's catalog, where it presumably resolves from a cached artifact; copying a pin without checking it exists is what made it my problem. Rather than chase a working plugin version, the analyzers now run from the CLIs ci-rust-android already ships. That was the point of putting them in the image in step 3, and going through Gradle plugins would have meant a SECOND pinned version of each tool, resolved at build time, kept in lockstep with the image's by hand. One less resolution step, and step 3's decision finally earns its keep. Also replaces the source-ordering hack while here. Kotlin has to compile after the bindings are generated, and the usual `tasks.withType<KotlinCompile>` cannot be written in this build at all — AGP 9's built-in Kotlin means that class is not on the buildscript classpath. Passing the TASK PROVIDERS to srcDir instead lets Gradle read their @OutputDirectory and infer the ordering itself, which is the idiomatic form and removes the dependsOn entirely. Good news from the failed run: the Gradle wrapper check passed, so Gradle 9.1.0 on the image's JDK 25 works — the toolchain decision from step 3 holds. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
117 lines
4.6 KiB
YAML
117 lines
4.6 KiB
YAML
name: Android
|
|
|
|
# The native Kotlin/Compose client over the shared Rust core (M12).
|
|
#
|
|
# Replaces the Tauri-mobile lane deleted in step 2. What changed is what this
|
|
# builds, not that Android has a lane: the UI is Compose, and the store and sync
|
|
# engine are `thoughtsync-core` cross-compiled by cargo-ndk and loaded through
|
|
# uniffi.
|
|
#
|
|
# CI can only prove this BUILDS. A Linux runner cannot execute an APK, so anything
|
|
# about feel, touch or on-device correctness is an operator pass on an emulator or
|
|
# phone.
|
|
|
|
on:
|
|
push:
|
|
branches: [dev, main]
|
|
paths:
|
|
- "android/**"
|
|
# The Rust the .so is built from. A core change reaches the phone exactly
|
|
# as it reaches the desktop, so this lane has to rebuild on it.
|
|
- "core/**"
|
|
- "Cargo.toml"
|
|
- "Cargo.lock"
|
|
- ".forgejo/workflows/android.yml"
|
|
workflow_dispatch:
|
|
|
|
concurrency:
|
|
group: android-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
env:
|
|
# Silences the JDK 22+ "restricted method in java.lang.System has been called"
|
|
# warning that Gradle 9.1's bundled native-platform jar trips at launch. This
|
|
# targets the LAUNCHER JVM, which is why org.gradle.jvmargs in
|
|
# gradle.properties is not enough on its own (Minstrel hit the same thing).
|
|
JAVA_TOOL_OPTIONS: "--enable-native-access=ALL-UNNAMED"
|
|
|
|
jobs:
|
|
build:
|
|
name: Kotlin + Rust (debug APK)
|
|
# runs-on is only a scheduling label (Label Model B). flutter-ci is the
|
|
# proven-working label that can pull our container images.
|
|
runs-on: flutter-ci
|
|
container:
|
|
# The image repurposed from ci-tauri-android in M12 step 3: Rust + the four
|
|
# Android ABIs + cargo-ndk + SDK/NDK + JDK 25 + ktlint + detekt.
|
|
image: git.fabledsword.com/bvandeusen/ci-rust-android:1.97
|
|
|
|
defaults:
|
|
run:
|
|
working-directory: android
|
|
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Cache Gradle and Cargo
|
|
uses: actions/cache@v4
|
|
with:
|
|
path: |
|
|
~/.gradle/caches
|
|
~/.gradle/wrapper
|
|
~/.kotlin
|
|
target
|
|
key: android-${{ hashFiles('android/gradle/wrapper/gradle-wrapper.properties', 'android/gradle/libs.versions.toml', 'android/**/*.gradle.kts', 'Cargo.lock') }}
|
|
restore-keys: |
|
|
android-
|
|
|
|
- name: Make gradlew executable
|
|
run: chmod +x ./gradlew
|
|
|
|
# Fails loudly here if the wrapper and the image's JDK disagree, rather
|
|
# than thirty seconds into a compile with an opaque version message.
|
|
- name: Gradle wrapper check
|
|
run: ./gradlew --version
|
|
|
|
# Cross-compiles the core for four ABIs and generates the Kotlin bindings
|
|
# from the built .so. Run as its own step so a Rust failure is legible as a
|
|
# Rust failure instead of arriving inside a Gradle stack trace.
|
|
- name: Build the native library and bindings
|
|
run: ./gradlew generateUniffiBindings
|
|
|
|
# The image's PINNED CLIs, not Gradle plugins. ci-rust-android carries both
|
|
# (M12 step 3) precisely so this lane needs no second image, and going
|
|
# through Gradle plugins would mean a second version of each tool resolved
|
|
# at build time and kept in lockstep with the image's by hand.
|
|
#
|
|
# Scoped to src/main: the generated uniffi bindings live under build/ and
|
|
# are not ours to style.
|
|
- name: ktlint
|
|
run: ktlint "app/src/main/**/*.kt"
|
|
|
|
- name: detekt
|
|
run: detekt --build-upon-default-config --config config/detekt.yml --input app/src/main/java
|
|
|
|
- name: Unit tests
|
|
# Host-JVM tests only. Anything touching the core needs an Android
|
|
# runtime to load the .so, so those are instrumented tests and belong on
|
|
# an emulator, not here — the Rust side is covered by the workspace
|
|
# tests in the desktop lane.
|
|
run: ./gradlew testDebugUnitTest
|
|
|
|
- name: Assemble debug APK
|
|
run: ./gradlew assembleDebug
|
|
|
|
- name: Upload debug APK
|
|
# Mirrored action, never actions/upload-artifact. @v4+ throws
|
|
# GHESNotSupportedError client-side on this hostname, and @v3 is worse —
|
|
# it reports success while Gitea serves artifacts back only through the
|
|
# v4 API, so the upload is stored and invisible. Pinned by SHA because
|
|
# the mirror auto-syncs; full URL because DEFAULT_ACTIONS_URL sends bare
|
|
# owner/repo to github.com. See Scribe issues 2255 / 2270.
|
|
uses: https://git.fabledsword.com/bvandeusen/upload-artifact@cb8afe72b42edc798abfb8fcb556cf660d894245
|
|
with:
|
|
name: thoughtsync-android-debug-${{ github.sha }}
|
|
path: android/app/build/outputs/apk/debug/app-debug.apk
|
|
if-no-files-found: error
|