1# The toolchain, the environment and the tasks of this repository: the one place 2# every tool version lives. Nothing here needs nix, nix-darwin or root: 3# 4# mise trust && mise install the toolchain (a C compiler and git must exist) 5# mise tasks what can be run 6# mise run check the fast gates 7# mise run android:debug the APK 8# 9# `tools/check-versions.sh` (part of `mise run check`) fails when a version 10# written elsewhere (Cargo.toml's wasm-bindgen pin, the Gradle build's SDK 11# levels, hk.pkl's hk release) differs from the one here. The nix flake only 12# wraps this file; it holds no version of its own. 13min_version = "2026.10.0" 14 15[settings] 16# `[daemons]` below needs it. 17experimental = true 18 19[tools] 20# The Rust core, the service and the wasm Workers. Android targets for the app, 21# wasm for the website and the backend Worker. 22rust = { version = "1.99.0", profile = "minimal", components = "rustfmt,clippy", targets = "aarch64-linux-android,armv7-linux-androideabi,x86_64-linux-android,wasm32-unknown-unknown" } 23"cargo:cargo-ndk" = "4.1.2" 24# The wasm-bindgen CLI must equal the `wasm-bindgen` crate pinned in web/Cargo.toml and 25# backend/worker/Cargo.toml (checked by tools/check-versions.sh). 26"cargo:wasm-bindgen-cli" = "0.2.127" 27# worker-build links OpenSSL on Linux. A system copy (and pkg-config) is not assumed: the same OpenSSL from 28# conda-forge is installed first and linked from where mise put it. 29"conda:openssl" = "3.5.9" 30"cargo:worker-build" = { version = "0.8.6", depends = ["conda:openssl"], install_env = { OPENSSL_DIR = "{{ env.MISE_DATA_DIR | default(value=xdg_data_home ~ '/mise') }}/installs/conda-openssl/3.5.9", RUSTFLAGS = "-C link-arg=-Wl,-rpath,{{ env.MISE_DATA_DIR | default(value=xdg_data_home ~ '/mise') }}/installs/conda-openssl/3.5.9/lib" } } 31 32# The Android app. No Gradle wrapper by design: the version is here. 33java = "temurin-21.0.12+101" 34gradle = "9.7.1" 35# The SDK's command-line tools; the platform, build tools and NDK are installed by 36# `mise run android:sdk` at the levels in [vars]. 37android-sdk = "13.0" 38 39# The website (web/) and the backend Worker. 40node = "24.20.0" 41"npm:wrangler" = "4.129.0" 42# celld, the self-hosted Durable Objects host, for the backend Worker. 43"github:denoland/celld" = "0.6.1" 44ffmpeg = "9.0.2" 45resvg = "0.48.1" 46 47# The jdx tools: git hooks, supervised dev daemons, secrets, CLI specs. 48hk = "2.5.0" 49pkl = "0.32.1" 50pitchfork = "2.29.0" 51fnox = "1.36.0" 52usage = "6.12.0" 53 54[vars] 55# Android SDK levels. build.gradle.kts must agree (tools/check-versions.sh). 56android_platform = "37.0" 57android_build_tools = "36.0.0" 58android_ndk = "29.0.14206865" 59# Only for `mise run android:emulator`. 60android_emulator_image = "system-images;android-35;google_apis;x86_64" 61 62[env] 63# Where the SDK and NDK are (the android-sdk tool sets ANDROID_HOME to its own directory; 64# tasks put the SDK packages there). Scripts read these from the environment and never 65# assume mise or nix; export them yourself if you bring your own SDK. 66ANDROID_NDK_HOME = { value = "{{ env.ANDROID_HOME }}/ndk/{{ vars.android_ndk }}", tools = true } 67WRANGLER_SEND_METRICS = "false" 68# hk runs its hooks through mise, so the hook finds hk, pkl and cargo whatever the shell of the commit. 69HK_MISE = "1" 70 71# What the tasks read, all optional. Secrets are never put in this file: whiskersd's keys come 72# from the environment, or from fnox (see fnox.toml and tools/README.md). 73# WHISKERS_ADDR address whiskersd listens on (private network address, not 0.0.0.0) 74# WHISKERS_MODEL_URL the model gateway whiskersd proxies to (required by whiskersd) 75# WHISKERS_HOST default service address baked into an APK (-Pwhiskers.host) 76# WHISKERS_VERSION_NAME / WHISKERS_VERSION_CODE / WHISKERS_APPLICATION_ID / WHISKERS_ABIS 77# the other -Pwhiskers.* properties (see android/README.md) 78# WHISKERS_KEYSTORE, WHISKERS_KEYSTORE_PASSWORD, WHISKERS_KEY_ALIAS, WHISKERS_KEY_PASSWORD 79# sign with your own key (all four or none) 80# WHISKERS_SERVICE address preflight checks (host:port) 81# ADB, ANDROID_SERIAL, WHISKERS_ANDROID_USER see tools/install-android.sh 82WHISKERS_ADDR = { value = "127.0.0.1:47900" } 83 84# ---------------------------------------------------------------- setup 85 86[tasks.submodules] 87description = "Fetch the git submodules (third-party/jevcrates)" 88run = "git submodule update --init --recursive" 89 90[tasks."hooks:install"] 91description = "Install the git hooks (hk.pkl): check-private and clippy before a commit, the web tests before a push" 92run = "hk install" 93 94[tasks."android:sdk"] 95description = "Install the Android platform, build tools, platform tools and NDK at the levels in [vars]" 96run = "tools/android-sdk.sh" 97env = { WHISKERS_ANDROID_PLATFORM = "{{ vars.android_platform }}", WHISKERS_ANDROID_BUILD_TOOLS = "{{ vars.android_build_tools }}", WHISKERS_ANDROID_NDK = "{{ vars.android_ndk }}" } 98 99[tasks."android:emulator"] 100description = "Also install the emulator and an x86_64 system image (large)" 101depends = ["android:sdk"] 102run = 'yes | sdkmanager --install emulator "{{ vars.android_emulator_image }}"' 103 104# ---------------------------------------------------------------- checks 105 106[tasks."check-private"] 107description = "Fail if a term from .private-terms appears in a tracked file" 108run = "tools/check-private.sh" 109 110[tasks."check-versions"] 111description = "Fail if a version written outside mise.toml differs from it" 112run = "tools/check-versions.sh" 113 114[tasks."check-wasm"] 115description = "The portable crates (ports, service, conformance) still build for wasm32 (a Worker)" 116run = "cargo check --target wasm32-unknown-unknown -p whiskers-ports -p whiskers-service -p whiskers-conformance" 117 118[tasks.clippy] 119description = "cargo clippy over the core workspace (an error-level lint fails; warnings are shown)" 120run = "cargo clippy --workspace" 121 122[tasks."test:core"] 123description = "The Rust workspace's tests" 124run = "cargo test --workspace" 125 126[tasks."test:web"] 127description = "The website's tests (its own workspace, natively)" 128run = "cargo test --workspace --manifest-path web/Cargo.toml" 129 130[tasks."test:backend"] 131description = "The backend Worker's pure tests, natively" 132dir = "backend/worker" 133run = "cargo test" 134 135[tasks.test] 136description = "All native tests: the core and the website" 137depends = ["test:core", "test:web"] 138 139[tasks.check] 140description = "The fast gates: private terms, versions, wasm build, clippy, core and website tests" 141depends = ["check-private", "check-versions", "check-wasm", "clippy", "test"] 142 143[tasks.preflight] 144description = "Check everything the app talks to (WHISKERS_SERVICE=host:port or pass it as an argument)" 145run = "tools/preflight.sh" 146 147# ---------------------------------------------------------------- android 148 149[tasks.core] 150description = "Build the Rust core for the tablet and generate the Kotlin bindings and the art" 151depends = ["android:sdk"] 152run = "tools/build-core.sh" 153 154[tasks."android:debug"] 155description = "Build the debug APK (optional -Pwhiskers.* from the WHISKERS_* variables)" 156depends = ["android:sdk"] 157run = "tools/android-gradle.sh :app:assembleDebug" 158 159[tasks."android:lite"] 160description = "Build the debug APK without the offline voice (about 90 MB smaller)" 161depends = ["android:sdk"] 162run = "tools/android-gradle.sh :app:assembleDebug -Pwhiskers.offlineVoice=false" 163 164[tasks."android:test"] 165description = "The app's JVM unit tests" 166depends = ["android:sdk"] 167run = "tools/android-gradle.sh :app:testDebugUnitTest" 168 169[tasks."android:record-goldens"] 170description = "Rewrite the screenshot goldens (look at the PNGs afterwards)" 171depends = ["android:sdk"] 172run = "tools/android-gradle.sh :app:recordRoborazziDebug" 173 174# record and verify in one Gradle invocation race, so they are separate tasks. 175[tasks."android:verify-goldens"] 176description = "Fail if a screenshot drifted from its golden" 177depends = ["android:sdk"] 178run = "tools/android-gradle.sh :app:verifyRoborazziDebug" 179 180[tasks."android:fetch-voices"] 181description = "Fetch the offline speech model and the offline neural voice (Gradle does this before a build)" 182run = ["tools/fetch-speech-model.sh", "tools/fetch-offline-voice.sh"] 183 184[tasks."android:install"] 185description = "Install the debug APK on a connected device and start it" 186depends = ["android:sdk"] 187run = "tools/install-android.sh" 188 189[tasks."android:inspect"] 190description = "Print who signed an APK, what it asks for and which ABIs it carries" 191depends = ["android:sdk"] 192run = "tools/inspect-apk.sh" 193 194[tasks."release:apks"] 195description = "Build the three release APKs into ~/whiskers-release/<version>/ (arguments: version code)" 196depends = ["android:sdk"] 197run = "tools/release-apks.sh" 198 199# ---------------------------------------------------------------- the service 200 201[tasks.whiskersd] 202description = "Run the service in the foreground (secrets from fnox when it is set up, else the environment)" 203run = "tools/with-secrets.sh cargo run --release -p whiskersd -- \"$WHISKERS_ADDR\"" 204 205[tasks."whiskersd:service"] 206description = "Run the service as a transient systemd user service that comes back if it dies" 207run = "cargo build --release -p whiskersd && tools/with-secrets.sh tools/run-whiskersd.sh" 208 209# ---------------------------------------------------------------- the website 210 211[tasks."web:dev"] 212description = "The website under wrangler dev --local on http://localhost:8788/ (builds the Worker and the link preview)" 213dir = "web/worker" 214run = "wrangler dev --local --port 8788" 215 216[tasks."web:preview"] 217description = "Rebuild the link preview's PNG and MP4" 218run = "web/tools/preview" 219 220[tasks."web:check-preview"] 221description = "Check the link preview the way a scraper would" 222run = "web/tools/check-preview" 223 224# ---------------------------------------------------------------- the backend Worker 225 226[tasks."backend:build"] 227description = "Build the backend Worker (build/index.js and index_bg.wasm), the one build both hosts run" 228dir = "backend/worker" 229run = "worker-build --release" 230 231[tasks."backend:celld"] 232description = "The backend Worker on celld, self-hosted, on port 8801 (state in backend/worker/.celld/dev)" 233dir = "backend/worker" 234run = "celld dev . --port 8801" 235 236[tasks."backend:wrangler"] 237description = "The backend Worker under wrangler dev --local on port 8802" 238dir = "backend/worker" 239run = "wrangler dev --local --port 8802 --persist-to .wrangler/state" 240 241[tasks."backend:host"] 242description = "Start, stop or restart one backend host: native, wrangler or celld (arguments: host action)" 243run = "backend/worker/tools/host" 244 245[tasks."backend:probe"] 246description = "Show what each backend host says to a few odd requests (argument: native, wrangler or celld)" 247run = "backend/worker/tools/probe" 248 249[tasks."backend:crosscheck"] 250description = "Run the whole comparison of the three backend hosts (see backend/README.md)" 251run = "backend/worker/tools/crosscheck" 252 253# ---------------------------------------------------------------- daemons 254 255# Supervised by pitchfork, started and stopped through mise. `mise run dev` starts both and 256# waits until they answer; `mise daemons status`, `mise daemons logs`, `mise daemons stop`. 257[daemons.whiskersd] 258run = "exec tools/with-secrets.sh target/release/whiskersd \"$WHISKERS_ADDR\"" 259init = "cargo build --release -p whiskersd" 260ready_port = 47900 261 262[daemons.site] 263run = "cd web/worker && exec wrangler dev --local --port 8788" 264ready_http = "http://127.0.0.1:8788/" 265 266[tasks.dev] 267description = "Start the dev daemons (whiskersd and the website) and wait until they answer" 268daemons = true 269run = "echo 'whiskersd on '\"$WHISKERS_ADDR\"', the website on http://localhost:8788/; mise daemons stop to stop them'"