Skip to content
Fran Gonzalez
← Back to blog
(updated Sep 30, 2026)·Clanker·4 min read

Managing the Android SDK as a mise dependency

Moving a Tauri Android setup from an Android Studio Mac to a headless Debian VM by pinning the SDK as a mise tool.

Some matmuls wrote this slop, sorry. My goal with this content is to document some work I (a real human bean) do while poking the Clanker, and try to learn something along the way.

Moving Tauri Android development from an Android Studio Mac to a headless Debian VM exposed how much of the setup lived outside the repository. The SDK came from Studio’s GUI, ANDROID_HOME came from dotfiles, and the first cargo build failed on missing system libraries. I replaced all of that with two mise tasks.

Note

Tested with mise 2026.9.11, android-sdk 19.0, and NDK 28.2.13676358 on Debian 13, 2026-09-13.

The Problem

The repository (CorrePlatano, a Tauri v2 + Rust + React run-tracking app) already had host-agnostic Android tooling: a run-android.ts wrapper that reads ANDROID_HOME, auto-discovers the NDK, and prepends platform-tools to PATH. But onboarding still assumed Android Studio. The docs said “Install Android Studio, then use its SDK Manager”, and dotfiles exported ANDROID_HOME per OS, pointing at a directory that didn’t exist on the new VM.

The standard headless alternative is an imperative dance: download Google’s command-line tools zip, unzip it into cmdline-tools/latest, run sdkmanager for each component. It works, but it’s per-machine knowledge that drifts, and nothing pins which NDK or build-tools a machine has.

What Changed

mise can own the command-line tools as a regular tool. The mise registry maps android-sdk to the vfox-android-sdk plugin, which downloads the cmdline-tools zip for the host platform (Linux, macOS, or Windows), rearranges it into the layout sdkmanager expects, and exports ANDROID_HOME/ANDROID_SDK_ROOT pointing at the install:

# mise.toml
[tools]
java = "temurin-17.0.18+8"
android-sdk = "19.0"

That’s now a lockfile-pinned dependency like node or rust. The components only exist inside sdkmanager’s repository metadata, so they live in an idempotent task with the versions pinned as strings:

# mise.toml
[tasks."android:sdk"]
description = "Install the pinned Android SDK components with sdkmanager (idempotent)"
run = """
set -eu
yes | sdkmanager --licenses >/dev/null
sdkmanager "platform-tools" "platforms;android-36" "build-tools;36.0.0" "ndk;28.2.13676358"
"""

[tasks."android:init"]
depends = ["android:sdk"]

android:doctor gained a sdkmanager --version check, and on this VM it reports the whole chain resolved from the mise-managed SDK:

openjdk version "17.0.18"      # mise tool
19.0                           # sdkmanager, mise tool
Android Debug Bridge version 1.0.41
Installed as /home/fran/.local/share/mise/installs/android-sdk/19.0/platform-tools/adb
tauri-cli 2.11.4

One precedence detail I tested: inside mise run/mise exec, mise’s plugin env overrides an ANDROID_HOME already exported by the shell, and the mise-managed SDK wins. The dotfiles exports still deserved deletion because they mislead any command run outside mise.

The desktop build needed a second task. Tauri on Linux links against the system GUI stack (WebKitGTK, GTK3, libdbus), so a fresh VM fails compiling libdbus-sys with a pkg-config error. Those are OS packages, so they now live in a guarded setup:linux task: Linux-only, apt-only, checks dpkg-query first, and only invokes sudo apt-get install for the missing set. It’s explicit opt-in, never wired into a build or test gate.

The docs also got a two-tier development loop, because most days need none of this: mise run dev:web plus mise run test covers shared React and Rust work. The Android loop only runs when a change touches WebView, plugins, permissions, or secure storage; then the phone connects over wireless debugging instead of a cable:

adb pair <phone-ip>:<pair-port>   # once, from Developer options
adb connect <phone-ip>:<port>
mise run test:android             # builds, installs, runs the Maestro smoke

Results

Onboarding on a fresh Debian 13 VM is now mise install && mise run android:init, and every quality gate that doesn’t need a device passes: mise run verify exits 0, covering format, lint, typecheck, tests, and builds. The Mac keeps the one job it’s uniquely required for: iOS and macOS desktop builds, whenever those start.

What I’d Do Differently

Three caveats I accepted knowingly. The version strings in the task are the pin, and sdkmanager has no lock concept. Uninstalling the android-sdk tool wipes everything sdkmanager installed into it, recoverable by rerunning the task. And the local emulator path would need an x86_64-linux-android Rust target on this host, since the repo builds ARM64 only; I skipped the emulator entirely because verifying on the phone is faster anyway.

References

This post was written with AI assistance.