whiskers.git / mise.toml
mise.tomlannotatedmise.tomlsource269 lines · 11.4 KB · raw

The toolchain, the environment and the tasks of this repository: the one place every tool version lives. Nothing here needs nix, nix-darwin or root:

mise trust && mise install      the toolchain (a C compiler and git must exist)
mise tasks                      what can be run
mise run check                  the fast gates
mise run android:debug          the APK

tools/check-versions.sh (part of mise run check) fails when a version written elsewhere (Cargo.toml's wasm-bindgen pin, the Gradle build's SDK levels, hk.pkl's hk release) differs from the one here. The nix flake only wraps this file; it holds no version of its own.

13min_version = "2026.10.0"
15[settings]

[daemons] below needs it.

17experimental = true
19[tools]

The Rust core, the service and the wasm Workers. Android targets for the app, 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"

The wasm-bindgen CLI must equal the wasm-bindgen crate pinned in web/Cargo.toml and backend/worker/Cargo.toml (checked by tools/check-versions.sh).

26"cargo:wasm-bindgen-cli" = "0.2.127"

worker-build links OpenSSL on Linux. A system copy (and pkg-config) is not assumed: the same OpenSSL from 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" } }

The Android app. No Gradle wrapper by design: the version is here.

33java = "temurin-21.0.12+101"
34gradle = "9.7.1"

The SDK's command-line tools; the platform, build tools and NDK are installed by mise run android:sdk at the levels in [vars].

37android-sdk = "13.0"

The website (web/) and the backend Worker.

40node = "24.20.0"
41"npm:wrangler" = "4.129.0"

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"

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"
54[vars]

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"

Only for mise run android:emulator.

60android_emulator_image = "system-images;android-35;google_apis;x86_64"
62[env]

Where the SDK and NDK are (the android-sdk tool sets ANDROID_HOME to its own directory; tasks put the SDK packages there). Scripts read these from the environment and never 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"

hk runs its hooks through mise, so the hook finds hk, pkl and cargo whatever the shell of the commit.

69HK_MISE = "1"

What the tasks read, all optional. Secrets are never put in this file: whiskersd's keys come from the environment, or from fnox (see fnox.toml and tools/README.md). WHISKERS_ADDR address whiskersd listens on (private network address, not 0.0.0.0) WHISKERS_MODEL_URL the model gateway whiskersd proxies to (required by whiskersd) WHISKERS_HOST default service address baked into an APK (-Pwhiskers.host) WHISKERS_VERSION_NAME / WHISKERS_VERSION_CODE / WHISKERS_APPLICATION_ID / WHISKERS_ABIS the other -Pwhiskers.* properties (see android/README.md) WHISKERS_KEYSTORE, WHISKERS_KEYSTORE_PASSWORD, WHISKERS_KEY_ALIAS, WHISKERS_KEY_PASSWORD sign with your own key (all four or none) WHISKERS_SERVICE address preflight checks (host:port) ADB, ANDROID_SERIAL, WHISKERS_ANDROID_USER see tools/install-android.sh

82WHISKERS_ADDR = { value = "127.0.0.1:47900" }

---------------------------------------------------------------- setup

86[tasks.submodules]
87description = "Fetch the git submodules (third-party/jevcrates)"
88run = "git submodule update --init --recursive"
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 }}"'

---------------------------------------------------------------- checks

106[tasks."check-private"]
107description = "Fail if a term from .private-terms appears in a tracked file"
108run = "tools/check-private.sh"
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"

---------------------------------------------------------------- android

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"
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"

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"
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"

---------------------------------------------------------------- the service

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\""
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"

---------------------------------------------------------------- the website

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"
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"

---------------------------------------------------------------- the backend Worker

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"
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"

---------------------------------------------------------------- daemons

Supervised by pitchfork, started and stopped through mise. mise run dev starts both and 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
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'"