justfile
| 1 | # opentracker task runner. `just --list` for the menu. |
| 2 | |
| 3 | sdk := env("ANDROID_HOME", env("HOME") / "android-sdk") |
| 4 | scratch := "dev" |
| 5 | |
| 6 | default: |
| 7 | @just --list |
| 8 | |
| 9 | # --- checks ------------------------------------------------------------------ |
| 10 | |
| 11 | # Everything CI would run. |
| 12 | check: test clippy fmt-check web-build android-test android-lint |
| 13 | |
| 14 | test: |
| 15 | cargo test --workspace |
| 16 | |
| 17 | clippy: |
| 18 | cargo clippy --workspace --all-targets -- -D warnings |
| 19 | |
| 20 | fmt: |
| 21 | cargo fmt --all |
| 22 | |
| 23 | fmt-check: |
| 24 | cargo fmt --all -- --check |
| 25 | |
| 26 | # --- protocol ---------------------------------------------------------------- |
| 27 | |
| 28 | # Regenerate the golden vectors that are the Rust <-> Kotlin contract. |
| 29 | # `just test` then verifies them, and the Android suite decodes the same file. |
| 30 | gen-vectors: |
| 31 | cargo run -p otproto --features serde --example gen_vectors |
| 32 | |
| 33 | # Coverage-guided fuzzing. Needs a nightly toolchain and cargo-fuzz: |
| 34 | # rustup toolchain install nightly && cargo install cargo-fuzz |
| 35 | fuzz target="decode" secs="60": |
| 36 | cd crates/otproto/fuzz && cargo +nightly fuzz run {{target}} -- -max_total_time={{secs}} -rss_limit_mb=4096 |
| 37 | |
| 38 | # --- server ------------------------------------------------------------------ |
| 39 | |
| 40 | # One-time: a server key and an admin account in ./{{scratch}}. |
| 41 | dev-setup user="sim" password="simsimsimsim": |
| 42 | mkdir -p {{scratch}} |
| 43 | test -f {{scratch}}/secret.key || (head -c32 /dev/urandom | base64 > {{scratch}}/secret.key && chmod 600 {{scratch}}/secret.key) |
| 44 | OT_SECRET_KEY={{scratch}}/secret.key OT_ADMIN_EMAIL=ops@example.net OT_DB_PATH={{scratch}}/ot.db \ |
| 45 | cargo run -p otserver -- --create-admin {{user}} {{password}} |
| 46 | |
| 47 | # The server plus an in-process fake phone walking a synthetic route over real |
| 48 | # OTP/1 on loopback. The fastest way to see the whole pipeline move. |
| 49 | # |
| 50 | # Serves the UI from web/dist if it has been built. For live reload, run |
| 51 | # `just web-dev` in a second terminal and use http://localhost:5173 instead. |
| 52 | dev: |
| 53 | OT_SECRET_KEY={{scratch}}/secret.key OT_ADMIN_EMAIL=ops@example.net OT_DB_PATH={{scratch}}/ot.db \ |
| 54 | OT_HTTP_ADDR=127.0.0.1:7372 OT_UDP_ADDR=127.0.0.1:7373 \ |
| 55 | cargo run -p otserver -- --dev --simulate-device |
| 56 | |
| 57 | # --- web --------------------------------------------------------------------- |
| 58 | |
| 59 | web-install: |
| 60 | cd web && bun install |
| 61 | |
| 62 | # Vite with hot reload on :5173, proxying /api to the backend on :7372. |
| 63 | web-dev: web-install |
| 64 | cd web && bun run dev |
| 65 | |
| 66 | # Type-check and bundle into web/dist, which the server embeds in release builds. |
| 67 | web-build: web-install |
| 68 | cd web && bun run build |
| 69 | |
| 70 | # --- docker ------------------------------------------------------------------ |
| 71 | |
| 72 | docker-build: |
| 73 | docker build -f Containerfile -t opentracker:dev . |
| 74 | |
| 75 | # The whole thing in one container, with the simulated phone walking its route: |
| 76 | # open http://127.0.0.1:7372 and sign in as {{user}}. |
| 77 | # |
| 78 | # 127.0.0.1, not localhost: the port is published on IPv4 only, and localhost |
| 79 | # resolves to ::1 first on this machine. |
| 80 | # |
| 81 | # The key below is a literal on purpose — this is a throwaway demo database. A |
| 82 | # real deployment passes OT_SECRET_KEY from a file or a secret store, and losing |
| 83 | # it makes every stored token key unrecoverable. |
| 84 | docker-demo user="sim" password="simsimsimsim": docker-build |
| 85 | docker volume create opentracker-demo >/dev/null |
| 86 | -docker run --rm -v opentracker-demo:/data \ |
| 87 | -e OT_SECRET_KEY=ZGVtby1vbmx5LW5vdC1hLXJlYWwtc2VjcmV0LWtleSE= -e OT_ADMIN_EMAIL=ops@example.net \ |
| 88 | opentracker:dev --create-admin {{user}} {{password}} |
| 89 | docker run --rm --name opentracker-demo -p 7372:7372 -p 7373:7373/udp -v opentracker-demo:/data \ |
| 90 | -e OT_SECRET_KEY=ZGVtby1vbmx5LW5vdC1hLXJlYWwtc2VjcmV0LWtleSE= -e OT_ADMIN_EMAIL=ops@example.net \ |
| 91 | -e OT_SIM_USER={{user}} -e OT_SIM_PASSWORD={{password}} \ |
| 92 | opentracker:dev --dev --simulate-device |
| 93 | |
| 94 | # Wipes the demo database so `just docker-demo` starts from nothing. |
| 95 | docker-demo-reset: |
| 96 | -docker rm -f opentracker-demo |
| 97 | docker volume rm opentracker-demo |
| 98 | |
| 99 | # --- android ----------------------------------------------------------------- |
| 100 | |
| 101 | # JVM tests, including the golden-vector cross-check against otproto. |
| 102 | android-test: |
| 103 | cd android && ANDROID_HOME={{sdk}} ./gradlew :app:testDebugUnitTest |
| 104 | |
| 105 | android-lint: |
| 106 | cd android && ANDROID_HOME={{sdk}} ./gradlew :app:lintDebug |
| 107 | |
| 108 | android-build: |
| 109 | cd android && ANDROID_HOME={{sdk}} ./gradlew :app:assembleDebug |
| 110 | |
| 111 | android-install: android-build |
| 112 | cd android && ANDROID_HOME={{sdk}} ./gradlew :app:installDebug |
| 113 | adb shell am start -n net.lexcom.opentracker.debug/net.lexcom.opentracker.MainActivity |
| 114 | |
| 115 | # Only our own tags, at verbose. Everything else silenced. |
| 116 | logcat: |
| 117 | adb logcat -c && adb logcat OpenTracker:V AndroidRuntime:E '*:S' |
| 118 | |
| 119 | # The Compose compiler plugin version must equal the KGP version AGP bundles. |
| 120 | # Run this after any AGP bump and update app/build.gradle.kts to match. |
| 121 | android-kotlin-version: |
| 122 | cd android && ANDROID_HOME={{sdk}} ./gradlew :app:buildEnvironment | grep -i kotlin-gradle-plugin |
| 123 | |
| 124 | # --- emulator ---------------------------------------------------------------- |
| 125 | |
| 126 | # The AVDs, oldest to newest. ot29 is minSdk and the one that finds the |
| 127 | # API-level bugs; ot36 is targetSdk and the one that finds the policy bugs. |
| 128 | avd := "ot36" |
| 129 | |
| 130 | # Create the AVD. Needs the matching system image installed first: |
| 131 | # sdkmanager "system-images;android-36;default;x86_64" |
| 132 | # |
| 133 | # Create an AVD if `just emu` says there is none. |
| 134 | emu-create name=avd api="36": |
| 135 | {{sdk}}/cmdline-tools/latest/bin/avdmanager create avd -n {{name}} -k "system-images;android-{{api}};default;x86_64" -d pixel_6 |
| 136 | |
| 137 | # -no-snapshot so every run is a cold boot: a snapshot restores stale |
| 138 | # permission grants and a stale credentials file, which is exactly what you are |
| 139 | # trying to test. |
| 140 | # |
| 141 | # Start the emulator and wait until it has booted. |
| 142 | emu name=avd: |
| 143 | {{sdk}}/emulator/emulator -avd {{name}} -no-boot-anim -no-snapshot & |
| 144 | adb wait-for-device shell 'while [ "$(getprop sys.boot_completed)" != 1 ]; do sleep 1; done' |
| 145 | @echo "booted" |
| 146 | |
| 147 | # Defaults to Marienplatz, Munich. Repeat with new values to simulate walking. |
| 148 | # |
| 149 | # Move the emulated GPS. Longitude first, that is the order `geo fix` wants. |
| 150 | emu-geo lon="11.5754" lat="48.1372": |
| 151 | adb emu geo fix {{lon}} {{lat}} |
| 152 | |
| 153 | # 10.0.2.2 is the emulator's alias for the host's loopback, so the server itself |
| 154 | # still binds 127.0.0.1 and stays off the LAN. |
| 155 | # |
| 156 | # The dev server, handing out the UDP address the emulator can reach. |
| 157 | dev-emu: |
| 158 | OT_SECRET_KEY={{scratch}}/secret.key OT_ADMIN_EMAIL=ops@example.net OT_DB_PATH={{scratch}}/ot.db \ |
| 159 | OT_HTTP_ADDR=127.0.0.1:7372 OT_UDP_ADDR=127.0.0.1:7373 OT_PUBLIC_UDP_HOST=10.0.2.2 \ |
| 160 | cargo run -p otserver -- --dev |
| 161 |