server: bake the newest Android client into every image (operator call)
Reverses the placement decision made an hour ago. That one put the APK only on the data volume, reasoning that ~55 MiB should not be charged to installs that never touch Android. The operator's call is that ending the manual copy is worth the megabytes, and it is their deployment. CI now fetches the newest published client into the build context immediately before the image build, so `:dev`, `:latest` and `:<version>` all ship one and a `docker compose pull` delivers a new server and a new client together. **Always the rolling `dev` release — the newest build there is.** A versioned image therefore carries the newest client rather than one pinned to that version. Deliberate: the two negotiate a sync protocol version before linking, so a mismatch is caught by the handshake, and pinning would buy nothing the handshake does not already provide. **Fetched by the JOB, never by the Dockerfile.** The release is private, and a token used inside a build ends up in the context or a layer. **It cannot fail the image build.** No release yet, a network blip, a first-ever build — all of them log a warning and produce an image with no client, which is a state the server already supports. Half a pair is cleaned up rather than shipped: a sidecar without its APK is worse than neither, because the server would be describing something it cannot serve. **The volume still wins.** `DATA_DIR/client/` is checked first and the baked copy second, so an operator who deliberately drops a build in gets that build — and a BROKEN drop-in falls through to the image's copy rather than taking the feature offline, which is what makes the copy-order advice survivable instead of load-bearing. Three tests cover the precedence, including that last case. The baked copy lives inside the package, not under DATA_DIR: that path is a volume mount, and anything the image wrote there would disappear behind it the moment one is attached. `client/.keep` is tracked so `COPY client/` cannot fail on a tree where the CI step never ran; the artifacts themselves are gitignored, since a 55 MiB binary does not belong in git history and is re-fetched on every build anyway.
This commit is contained in:
@@ -11,28 +11,42 @@ It also keeps the two in step: client and server negotiate a sync protocol versi
|
||||
before linking, so a server that serves the client cannot hand out a phone it
|
||||
cannot talk to.
|
||||
|
||||
## Where it goes
|
||||
## Where it comes from
|
||||
|
||||
Two files, both required, in `/var/thoughtsync/client/`:
|
||||
**Normally: nowhere. It is already in the image.**
|
||||
|
||||
CI fetches the newest published Android build into every server image it builds,
|
||||
so `:dev`, `:latest` and `:<version>` all ship a client. `docker compose pull &&
|
||||
docker compose up -d` delivers a new server and a new client together, and there
|
||||
is nothing to copy.
|
||||
|
||||
A versioned image therefore carries the *newest* client rather than one pinned to
|
||||
that version. That is deliberate: the two negotiate a sync protocol version
|
||||
before they link, so a mismatch is caught by the handshake rather than by
|
||||
pinning.
|
||||
|
||||
## Overriding it
|
||||
|
||||
If you want a specific build — testing something, or holding back — drop it in
|
||||
`/var/thoughtsync/client/` and it wins over the image's copy.
|
||||
|
||||
Two files, both required:
|
||||
|
||||
| File | What it is |
|
||||
| --- | --- |
|
||||
| `thoughtsync.apk` | the client |
|
||||
| `thoughtsync-android.json` | `{version_name, version_code, size, sha256}` |
|
||||
|
||||
The sidecar exists because an APK keeps its version in a binary manifest that needs
|
||||
the Android build tools to read. CI writes it beside the APK, where the real values
|
||||
are already known.
|
||||
The sidecar exists because an APK keeps its version in a binary manifest that
|
||||
needs the Android build tools to read. CI writes it beside the APK, where the
|
||||
real values are already known.
|
||||
|
||||
`/var/thoughtsync` is the same volume that holds attachments (`Config.DATA_DIR`),
|
||||
so a build dropped there survives container recreation. Nothing is baked into the
|
||||
image: the APK is ~55 MiB and an install that never touches Android should not
|
||||
carry it.
|
||||
|
||||
## Putting a build there
|
||||
so a build dropped there survives container recreation — and survives an image
|
||||
upgrade, which is the point of an override.
|
||||
|
||||
Both files are published to the rolling `dev` release on every green Android
|
||||
build. From the machine running the server:
|
||||
build:
|
||||
|
||||
```sh
|
||||
REPO=https://git.fabledsword.com/bvandeusen/thoughtsync
|
||||
@@ -43,14 +57,14 @@ for f in thoughtsync.apk thoughtsync-android.json; do
|
||||
-o "/tmp/$f" "$REPO/releases/download/dev/$f"
|
||||
done
|
||||
|
||||
# Into the app container's volume. Copy the sidecar LAST: the server treats a
|
||||
# sidecar that does not match the APK beside it as "no client at all", so a
|
||||
# half-finished copy advertises nothing rather than advertising a lie.
|
||||
# Copy the sidecar LAST. A sidecar that does not match the APK beside it is not a
|
||||
# client, so a half-finished copy falls back to the image's build rather than
|
||||
# advertising a lie.
|
||||
docker compose cp /tmp/thoughtsync.apk app:/var/thoughtsync/client/
|
||||
docker compose cp /tmp/thoughtsync-android.json app:/var/thoughtsync/client/
|
||||
```
|
||||
|
||||
`docker compose cp` creates `/var/thoughtsync/client/` if it does not exist.
|
||||
To go back to whatever the image ships, delete both files.
|
||||
|
||||
## Checking it took
|
||||
|
||||
@@ -64,14 +78,18 @@ offering a button that fails.
|
||||
|
||||
## What happens if you get it wrong
|
||||
|
||||
- **Only the APK, no sidecar** — the server reports no client. It cannot state a
|
||||
version it has no way to read.
|
||||
- **Mismatched pair** (new APK, old sidecar) — the server reports no client,
|
||||
because the recorded size does not match the file. It will not serve one build
|
||||
while describing another.
|
||||
- **Neither** — the server reports no client, the UI hides the card, and
|
||||
`/api/client/android` returns 404. This is the ordinary state of a server whose
|
||||
owner does not use Android, and nothing about it is an error.
|
||||
All of these describe an override in `/var/thoughtsync/client/`. A broken
|
||||
override does not take the feature away — it falls through to the build the image
|
||||
shipped with, which is the whole reason precedence runs in that direction.
|
||||
|
||||
- **Only the APK, no sidecar** — not a client. The server cannot state a version
|
||||
it has no way to read.
|
||||
- **Mismatched pair** (new APK, old sidecar) — not a client. It will not serve
|
||||
one build while describing another.
|
||||
- **Neither, and no baked copy either** — the server reports no client, the UI
|
||||
hides the card, and `/api/client/android` returns 404. That is the state of any
|
||||
image built before the first Android build ever published, and nothing about it
|
||||
is an error.
|
||||
|
||||
## Signing, and why replacing the APK is safe
|
||||
|
||||
|
||||
Reference in New Issue
Block a user