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:
2026-05-10 19:44:08 -04:00
parent 02d9f39845
commit e57a53a92e
5 changed files with 73 additions and 8 deletions
+5 -8
View File
@@ -36,14 +36,11 @@ COPY config.example.yaml /etc/smartmusic/config.yaml
RUN mkdir -p /app/data /app/client && chown -R minstrel:minstrel /app
WORKDIR /app
# In-app update channel (#397): bundled Android APK + sidecar version
# file. CI populates these on tag releases via a build context COPY;
# leaving the COPY commented during scaffold means the endpoints return
# 404 until the CI sequencing lands. Operators can also drop these in
# at runtime via a volume mount on /app/client.
#
# COPY minstrel.apk /app/client/minstrel.apk
# COPY minstrel.apk.version /app/client/minstrel.apk.version
# In-app update channel (#397). client/ in the build context holds
# minstrel.apk + minstrel.apk.version (populated by release.yml on tag
# pushes; .gitkeep + README otherwise). Endpoints return 404 when the
# APK files aren't present, so non-tag images degrade gracefully.
COPY --chown=minstrel:minstrel client/ /app/client/
USER minstrel
EXPOSE 4533