ci(server): bundle Android APK into image on tag releases (#397 phase 3)
Final phase of the in-app update flow. release.yml on tag pushes fetches the APK that flutter.yml is attaching to the same release, drops it into client/ in the build context, and the Dockerfile's COPY client/ /app/client/ bakes it into the image. ### How it sequences flutter.yml and release.yml both trigger on tag pushes and run in parallel on different runners (flutter-ci vs go-ci). flutter.yml typically finishes APK build + release attachment in 2-5 min. release.yml polls the release page for up to 15 min for the APK to appear, then proceeds. If the APK never lands (flutter.yml failure, network hiccup), release.yml emits a warning and ships the image without the bundled APK — server returns 404 from /api/client/version, banner stays hidden, manual download from the release page still works. Graceful degradation, not a blocker. ### Repo shape - client/.gitkeep + client/README.md so the directory exists in git and the COPY in the Dockerfile always finds something to copy - .gitignore excludes client/minstrel.apk + .version so accidental commits don't bloat the repo - Dockerfile: COPY --chown=minstrel:minstrel client/ /app/client/ ### Why polling vs cross-workflow trigger Forgejo Actions' workflow_run support varies by runner version; the polling approach is universally compatible. If/when we standardize on a runner that handles workflow_run cleanly, the polling step can become a `needs:` dependency. Closes #397. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
# Bundled client assets
|
||||
|
||||
Holds the Android APK (`minstrel.apk` + `minstrel.apk.version`) that the
|
||||
server serves via `/api/client/version` and `/api/client/apk` for the
|
||||
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
|
||||
Forgejo release as `minstrel-<TAG>.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/`.
|
||||
|
||||
## Development
|
||||
|
||||
Empty directory works fine — the endpoints return 404 and the Flutter
|
||||
update banner stays hidden ("no update channel" graceful degradation).
|
||||
|
||||
To smoke-test the update flow locally, drop a real APK + a version
|
||||
file into this directory:
|
||||
|
||||
```sh
|
||||
cp /path/to/app-release.apk client/minstrel.apk
|
||||
echo "v0.2.0" > 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 operators override at runtime via volume
|
||||
mount on `/app/client/` if they want to ship their own build.
|
||||
Reference in New Issue
Block a user