Files
minstrel/client
bvandeusenandClaude Opus 5.5 c96231106f docs: setup files and the client, shared and CI docs describe what is (#6277)
.dockerignore, .gitignore, deploy/initdb, ci-requirements.md,
client/README.md, shared/README.md and two web/scripts test names: dates,
issue/rule/spike ids and the stories behind each exclusion removed.

Corrected:
- client/README.md: the dev lane bundles its own APK; :main no longer
  exists; main bundles the recorded sidecar instead of re-deriving the
  version name. Its smoke-test step wrote a bare "v0.2.0", which the
  server rejects as a sidecar that is not JSON; it now writes the JSON
  shape, which the README documents.
- ci-requirements.md: one workflow, not four; the android lane builds no
  debug APK; main reads the recorded sidecar.
- .gitignore: CI fills client/ on every image build, not tag releases only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 13:04:02 -04:00
..

Bundled client assets

Holds the Android APK (minstrel.apk) and its version sidecar (minstrel.apk.version) that the server serves via /api/client/version and /api/client/apk for the in-app update flow.

Production

CI fills this directory in release.yml before every image build:

  • Dev push (:dev) and tag release (v*): the android-release job builds and signs the APK and uploads it as a workflow artifact; image-release downloads it here as minstrel.apk and writes the sidecar from that build's version outputs. A tag release also attaches both files to the Gitea release.
  • Main push (:latest + :<sha>): no APK is built, but the image still moves :latest, so image-release downloads the most recent release's APK and the sidecar recorded beside it. :latest keeps its update channel between releases.

Either way, docker buildx build bakes both files into the image via COPY client/ /app/client/.

The sidecar

minstrel.apk.version is JSON:

{"name": "2026.10.23.1200", "code": 3549060, "channel": "dev"}
  • name is the version name the app shows.
  • code is the ordering key Android installs by. null means unknown, and clients then compare names.
  • channel says which lane built it.

A sidecar that is not valid JSON, or has no name, is an error rather than a blank version.

Development

An empty directory works fine: the endpoints return 404 and the Android update banner stays hidden.

To smoke-test the update flow locally, drop a real APK and a sidecar into this directory:

cp /path/to/app-release.apk client/minstrel.apk
echo '{"name":"2099.01.01.0000","code":null,"channel":"dev"}' > client/minstrel.apk.version

Why a directory and not embed.FS?

The APK is 30-60 MB. Embedding bloats the Go binary and slows go build for everyone, even when the APK isn't being changed. File-on-disk also lets an admin override it at runtime with a volume mount on /app/client/ to ship their own build.