| name | android-build |
| description | Build, test, debug, and preview Android apps inside a ShipIt session. Load when a task touches an Android/Gradle project (Kotlin/Java, build.gradle(.kts), AndroidManifest.xml, res/ layouts, Compose UI). Covers the baked toolchain (JDK/SDK/Gradle), the build → lint → snapshot-test loop, reading snapshot PNG diffs, on-demand SDK installs, and the emulator-as-Compose-service for a live device over adb. |
| user-invocable | true |
Android build, test & preview
The Android toolchain is baked into every session container — JDK 17, the
Android SDK, and Gradle 8.7 are always on PATH. A repo declares nothing to
build (no shipit.yaml Android fields). Full command reference and env paths:
/shipit-docs/android.md — read it for anything not covered here.
There are two surfaces: headless (build/lint/test/snapshot — this container,
no device) and a running app (an emulator Compose service over adb).
Find the Gradle root first
Run Gradle from the dir with settings.gradle(.kts) + gradlew. In a monorepo
(web app + an android/ module) just cd into it. If there are several Android
app modules, flavors, or Gradle roots, list them and pick — or ask the user which
to target. Don't guess off stray .gradle files.
cd android
./gradlew projects
./gradlew tasks --all
The core loop: build → lint → snapshot
./gradlew assembleDebug
./gradlew lint
./gradlew test
Prefer the committed ./gradlew (pins the Gradle version). It downloads its dist
once per fresh container; run the baked gradle to skip that if needed.
Reading the output is most of debugging — compile errors, lint findings, and
test stack traces are in the command output. Fix the root cause from them.
"Did my UI/inset/padding change render right?" → a snapshot test
This is the key habit: visual verification is a snapshot test, not a
screenshot of a running app. Snapshot tests (Paparazzi via layoutlib,
Roborazzi via Robolectric) render a screen to a PNG on the JVM with no
device, and the test sets up the data it renders — then diffs against a
committed golden.
./gradlew verifyPaparazziDebug
./gradlew recordPaparazziDebug
Loop when you touch UI:
verify… → on failure it writes a diff PNG under the module's build/
(e.g. build/paparazzi/failures/).
- Read the diff PNG, and
present it to the user (see
/shipit-docs/present.md) so they see before/after with no device.
- Intended change →
record… to update the golden, and commit it.
If the repo has no snapshot tests and you're changing UI, adding a small one
for the affected screen is how you make the change verifiable. Pin the snapshot
library to the repo's AGP. (layoutlib can't render a WebView — cover native
chrome instead.)
Missing SDK component?
A build that needs an off-matrix compileSdk, an NDK, or CMake fails with a
precise "missing package" error. Install exactly what it names and re-run — these
are ephemeral (re-installed on demand after a restart):
sdkmanager --install "platforms;android-33"
sdkmanager --install "ndk;26.1.10909125"
Want a live device? Add an emulator Compose service
For runtime logs, touch interaction, or an interactive preview, add an emulator
to docker-compose.yml with devices: ["/dev/kvm:/dev/kvm"] (needs /dev/kvm
on the host). That exact mapping is the only device ShipIt allows — anything
else is rejected, and an operator can disable even it with
SESSION_ALLOW_DEV_KVM=0. The recipe and the full adb debug/drive triad
(logcat, uiautomator dump = snapshot, input tap = press, screencap =
screenshot, the Playwright-tools analog) are in /shipit-docs/android.md.
Its web UI becomes the interactive preview via x-shipit-preview. No host KVM →
Firebase Test Lab for batch test runs.
Don't
- Don't add Android fields to
shipit.yaml — the toolchain is ambient; there are
none.
- Don't reach for the emulator to check a layout — a snapshot test is faster,
deterministic, and needs no KVM.
- Don't run repo-root Gradle in a monorepo — scope to the Android module's dir so
the web build is untouched.