From ad37937949880fd4cb7bf7c476d9878a21f336bf Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Sat, 20 Jun 2026 10:57:28 -0400 Subject: [PATCH] docs: correct README setup/OOBE + fix data-volume mount Bring the README and client/README in line with the current product and release pipeline ahead of making the repo public-facing: - Highlights: replace the retired "Flutter client in flight" bullet with the native-Android-APK-bundled-in-the-image story. - Quickstart: document the purpose of each compose volume mount, and fix the minstrel-data mount (/data -> /app/data) so it matches the image's MINSTREL_STORAGE_DATA_DIR and generated artefacts actually persist. - Configuration: tie MINSTREL_STORAGE_DATA_DIR to the /app/data mount. - Updating: replace the bogus :v1.0.x scheme with the real tag model (:latest, immutable :vYYYY.MM.DD, :main) and note the bundled APK. - client/README: rewrite the Production section to match release.yml (android-release + needs: ordering, non-tag :latest bundle path). Co-Authored-By: Claude Opus 4.8 (1M context) --- README.md | 25 ++++++++++++++++++++----- client/README.md | 26 ++++++++++++++++++-------- 2 files changed, 38 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 1a923024..aa6beaf6 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ A self-hosted music server that thinks for you. Smart shuffle, contextual likes, - **ListenBrainz radio.** Session-aware "more like this" pulls from ListenBrainz similarity data, not a static genre tag. - **Lidarr integration.** Triggered scans, request-driven album imports, and a quarantine flow when something doesn't fit. - **Built-in web SPA.** Full-feature library, search, queue, playlists, and admin — no separate frontend container to deploy. -- **Flutter mobile client in flight.** Tracking issue [#356](https://git.fabledsword.com/bvandeusen/minstrel/issues/356). +- **Native Android client, shipped with the server.** The signed APK is bundled into every image and attached to each [release](https://git.fabledsword.com/bvandeusen/minstrel/releases) — sideload it once, then the app self-updates straight from your own server (no app store, no separate download to track). ## Quickstart @@ -24,10 +24,18 @@ services: image: git.fabledsword.com/bvandeusen/minstrel:latest ports: ['4533:4533'] volumes: + # Your music library. Point ./music at wherever your audio files + # live. Mounted read-only — Minstrel never writes to your library. - ./music:/music:ro - - minstrel-data:/data + # Generated data: playlist cover collages, artist art, caches. + # The path must match MINSTREL_STORAGE_DATA_DIR, which the image + # sets to /app/data — keep this mount on /app/data or your cache + # won't survive a container recreate. + - minstrel-data:/app/data environment: MINSTREL_DATABASE_URL: postgres://minstrel:minstrel@db:5432/minstrel?sslmode=disable + # Colon-separated library roots to scan; must match the container + # path of the read-only music mount above (/music here). MINSTREL_LIBRARY_SCAN_PATHS: /music depends_on: [db] @@ -37,6 +45,8 @@ services: POSTGRES_USER: minstrel POSTGRES_PASSWORD: minstrel POSTGRES_DB: minstrel + # Postgres data dir — users, likes, play history, sessions, settings. + # The one volume you must never lose; back it up with pg_dump. volumes: [pgdata:/var/lib/postgresql/data] volumes: @@ -57,7 +67,7 @@ For the full configuration surface, see [`config.example.yaml`](./config.example Most operators only need the env vars in the quickstart above. A few extras worth knowing: - `MINSTREL_BRANDING_APP_NAME` — rename the instance ("Family Jukebox", "Office Music"). Surfaces in the header, browser tab, and OG share previews. -- `MINSTREL_STORAGE_DATA_DIR` — defaults to `./data`. Holds playlist cover collages and other generated artefacts. +- `MINSTREL_STORAGE_DATA_DIR` — where generated artefacts (playlist cover collages, artist art, caches) are written. The container image sets this to `/app/data`, which is why the quickstart mounts the `minstrel-data` volume there. - `MINSTREL_LIBRARY_SCAN_PATHS` — colon-separated list of music library roots to scan. Supports multiple roots (`/music:/podcasts`). ListenBrainz integration (per-user scrobble + similarity tokens) and Lidarr integration (URL + API key) are configured through the admin Settings UI rather than env vars or yaml — per Minstrel's "config in UI" rule, integration settings live where operators can edit them without restarting. @@ -66,8 +76,13 @@ Most operational keys have a `MINSTREL_
_` env override. Recommen ## Updating -- `:main` — rolling, follows the dev branch's tested tip. Recommended only for the operator who's running an upstream-watching deployment. -- `:v1.0.x` — pinned releases. Recommended default. Database migrations run automatically at startup; rollbacks require restoring a Postgres dump. +Image tags (`git.fabledsword.com/bvandeusen/minstrel:`): + +- `:latest` — the newest blessed image. Moves on every `main` push **and** every release. Recommended for most operators. +- `:vYYYY.MM.DD` — immutable per-day release tags. Pin one of these for a deployment you don't want moving under you. (Per-day CalVer — no trailing patch digit; a same-day re-cut moves the tag forward.) +- `:main` — the rolling post-merge tip. Same image as `:latest` at push time; choose it if you want to track `main` explicitly rather than the release line. + +Every `:latest` and every `:vYYYY.MM.DD` bundles the current signed Android APK, so the in-app update channel is always live. Database migrations run automatically at startup; rollbacks require restoring a Postgres dump. ## Specs diff --git a/client/README.md b/client/README.md index b9ffaef4..66fe8e9b 100644 --- a/client/README.md +++ b/client/README.md @@ -6,17 +6,27 @@ in-app update flow (#397). ## Production -CI populates this directory on tag releases: -1. `flutter.yml` builds `app-release.apk` and attaches it to the - Gitea release as `minstrel-.apk`. -2. `release.yml` waits for that asset to appear, downloads it into - this directory as `minstrel.apk`, writes the tag string to - `minstrel.apk.version`, and `docker buildx build` includes both - via `COPY client/ /app/client/`. +CI populates this directory in `release.yml` (no Flutter — the client is +native Android, built by the `android-release` job): + +- **Tag release (`v*`):** the `android-release` job builds + signs the + APK, uploads it as a workflow artifact, and attaches it to the Gitea + release as `minstrel-.apk`. The `image-release` job + (`needs: android-release`) downloads that artifact into this directory + as `minstrel.apk` and writes the computed versionName to + `minstrel.apk.version`. +- **Main push (non-tag `:latest` / `:main`):** no APK is built, but the + image still moves `:latest`, so `image-release` pulls the most-recent + release's signed APK from the Gitea API and reconstructs its exact + versionName — keeping the in-app update channel alive on `:latest` + instead of stripping it until the next tag. + +Either way, `docker buildx build` bakes both files into the image via +`COPY client/ /app/client/`. ## Development -Empty directory works fine — the endpoints return 404 and the Flutter +Empty directory works fine — the endpoints return 404 and the Android update banner stays hidden ("no update channel" graceful degradation). To smoke-test the update flow locally, drop a real APK + a version