OXYGENHUB
# 8b7034c55402 oxygenflow / images / android / README.md
2.6 KiB · Markdown · last changed The Android image: JDK, SDK, NDK, Gradle and the app's dependencies, offline (closes #19) 9 days ago renderedsource blame history raw

The OxygenFlow Android image

A root file system for building and testing the OxygenHub Android app (oxygenhub-android) in a job with no network (oxygenflow#19):

  • OpenJDK 17 (JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64).
  • The Android SDK in /opt/android-sdk (ANDROID_HOME): command-line tools 23.0 (checked against Google's published SHA-1), platforms;android-37.0, build-tools;36.0.0 and ndk;30.0.16248370, licences accepted.
  • Gradle 9.6.0 as gradle (checked against its published SHA-256). Jobs run gradle --offline, not ./gradlew, which would download its distribution.
  • Rust 1.97.1 with the aarch64-linux-android and x86_64-linux-android targets and cargo-ndk 4.1.2, for the app's rust-core.
  • rust-core's crates, vendored in /opt/vendor; /.cargo/config.toml points cargo at them, as in the rust image.
  • The app's Gradle dependencies in /opt/gradle-home/caches, fetched by building the app once while the image is built.

The Gradle cache is read-only in the guest. A job points GRADLE_RO_DEP_CACHE at it and gives Gradle its own writable GRADLE_USER_HOME, so Gradle reads dependencies from the image and writes everything else under /tmp.

Building and importing

images/android/build.sh /path/to/oxygenflow-android.tar
scp /path/to/oxygenflow-android.tar claude@earth:/home/claude/flow/dl/
ssh claude@earth 'cd ~/flow && FLOW_HOME=~/flow bin/flow rootfs import dl/oxygenflow-android.tar'

build.sh expects the app next to this repository (../oxygenhub-android, subrepos checked out). It downloads and checks the command-line tools and Gradle into images/android/dl/, copies the app without its build output, vendors rust-core's crates, then builds the image with Docker and exports it as a plain tarball. The tarball is about 6 GB, so build it where the disk has room (Earth) and delete it once imported.

Rebuild and re-import when the app changes its SDK, NDK, Gradle or dependency versions, or rust-core/Cargo.lock changes, then pin the new hash in the app's pipeline. A dependency missing from the image fails the job with Gradle's "No cached version … available for offline mode", or cargo's "no matching package".

Using it

sandbox "android" {
    runtime "microvm"
    rootfs "cas://blob/<hash printed by flow rootfs import>"
    memory-limit "8GiB"
    cpu-limit 4
    network "isolated"
}

job "test" {
    sandbox "android"
    env {
        ANDROID_HOME "/opt/android-sdk"
        JAVA_HOME "/usr/lib/jvm/java-17-openjdk-amd64"
        GRADLE_RO_DEP_CACHE "/opt/gradle-home/caches"
        GRADLE_USER_HOME "/tmp/gradle"
    }
    exec "gradle" "--offline" "--no-daemon" ":app:testDebugUnitTest"
}