Build & test¶
Prerequisites¶
- JDK 17 (Gradle's embedded Kotlin DSL crashes on newer JVMs — see README).
- Android SDK with
platforms;android-35andbuild-tools;34.0.0. - The Gradle wrapper (never use a system Gradle).
- Node.js only when editing the injected JS (
web/src/*.ts) — the TypeScript sources are compiled toapp/src/main/assets/*.json the host; the Docker container has no Node.
A concrete JDK export that works:
If the SDK isn't wired through local.properties, export it for the session:
Commands¶
All commands run via the wrapper (./gradlew):
| Task | Command |
|---|---|
| Debug APK | ./gradlew assembleDebug |
| Release APK | ./gradlew assembleRelease |
| Play-Store bundle | ./gradlew bundleRelease |
| Lint (primary check) | ./gradlew lint |
| Kotlin style | ./gradlew ktlintCheck |
| Static analysis | ./gradlew detekt |
| Unit tests | ./gradlew testDebugUnitTest |
| Single test | ./gradlew testDebugUnitTest --tests "com.spooty.app.SomeTest" |
| Auto-format | ./gradlew ktlintFormat |
Desktop companion (JVM)¶
The desktop/ module is a small Compose Desktop companion. jpackage distribution is
configured via compose.desktop.application.nativeDistributions (EXE/MSI/ZIP targets):
| Task | Command |
|---|---|
| Run locally | ./gradlew :desktop:run |
| Compile | ./gradlew :desktop:build |
| Windows installer | ./gradlew :desktop:packageExe (Windows host + WiX) |
packageExe/packageMsi must run on a Windows host with the WiX Toolset installed
(the on-demand build passes the tag version via -PspootyDesktopVersion=X.Y.Z). On
Linux/macOS these tasks are unavailable. CI packages the installer on a windows-latest
runner via the Desktop (Windows EXE) workflow — on v* tags it attaches the installer to
the GitHub Release, and it can be triggered on demand with workflow_dispatch.
The local verification sweep¶
Before opening a PR, run the full sweep and wait for it to pass:
If you changed the injected JS (web/src/*.ts), regenerate the committed assets on the
host first — CI recompiles them and fails if they diverge:
The TypeScript build is idempotent: it only rewrites app/src/main/assets/*.js when the
source actually changed.
Get the APK¶
You can sideload the app straight from CI — no local Android Studio needed:
- Open the Actions tab on GitHub.
- Run the CI workflow (or reuse the latest green run from
main). - Download the
spooty-debug-apkartifact from thebuildjob. - Install on your phone with
adb install --no-streaming app-debug.apkor by tapping the file once your device allows unknown sources.
On-demand builds
The CI workflow supports workflow_dispatch, so you can
Run workflow → ⚙️ → Branch: main anytime to build a fresh APK without committing.
Release & signing¶
- Signing is optional and driven by
keystore.properties(gitignored). CI injects it from repository secrets when present; otherwise the release build is unsigned (debug-key) — fine for sideloading a personal app. - Tagging
vX.Y.Ztriggers the release job:assembleRelease+bundleReleaseand a GitHub Release with both artifacts attached.
CI overview¶
- CI (
.github/workflows/ci.yml) — on pushes tomainandtesting, on PRs, and on demand: - compiles the TypeScript assets (
web/src/→app/src/main/assets/), - fails if the committed assets are stale (
git diff --exit-code), - runs lint, ktlint, detekt, unit tests,
assembleDebug, - uploads the debug APK —
spooty-debug.apkonmain/PRs,spooty-testing.apkwhen the push came from thetestingbranch. - Cloudflare Pages (
.github/workflows/cloudflare-pages.yml) — on pushes touchingdocs/ormkdocs.yml: builds the MkDocs site and direct-uploads it with Wrangler tohttps://spooty.pages.dev. - The release job runs on
v*tags, recompiles the web assets first, and publishes APK - AAB from a GitHub Release.