mirror of
https://github.com/Screenly/Anthias.git
synced 2026-08-02 10:46:24 -04:00
Make `ghcr.io/screenly/anthias-*` the canonical source for Anthias
container images and demote Docker Hub's `screenly/anthias-*` to a
parallel mirror during the migration window. The legacy
`screenly/srly-ose-*` namespace is dropped entirely (matrix push +
latest-* mirror). The compose templates are flipped to ghcr in the
same change so `bin/upgrade_containers.sh` regenerates with ghcr
on the next run.
Why
---
Two motivations stack:
1. Docker Hub's anonymous-pull rate limit (100 pulls / 6h per IP) bites
end-users when a fleet of devices behind one NAT all run
`bin/upgrade_containers.sh` at once, not just CI. GHCR has no such
limit for public packages, and storage is free unlimited. Authed
pushes from CI also get a much higher quota under the GitHub Actions
token than under our shared Docker Hub bot.
2. d568602's publish-latest hit Docker Hub's 429 rate limit on retag
#52 (`srly-ose-redis:latest-pi3`) — the legacy namespace doubled the
manifest GETs in the loop and bought no real back-compat in
exchange. `docker-compose.yml.tmpl` has pointed installs at
`screenly/anthias-*` since 2023-02 (b9998438), and
`bin/upgrade_containers.sh` regenerates compose from the template
on every upgrade, so any device that has run an upgrade in the past
three years is on `screenly/anthias-*` already.
What ships
----------
* `tools/image_builder/__main__.py` — `namespaces` becomes
`['ghcr.io/screenly/anthias', 'screenly/anthias']`. GHCR is listed
first so it's the primary push target; Docker Hub is the parallel
mirror. The buildx matrix now pushes both `<short-hash>-<board>`
tags to both registries on every build.
* `.github/workflows/docker-build.yaml` — adds job-scoped
`permissions: { contents: read, packages: write }` on `buildx` and
`publish-latest` (not at workflow level, so `run-tests` doesn't
inherit), plus a `Login to GitHub Container Registry` step using
`${{ github.actor }}` + `${{ secrets.GITHUB_TOKEN }}` in both jobs.
The publish-latest mirror loop iterates over both namespaces (GHCR
first) inside the same retry-wrapped retag block, so `latest-<board>`
advances atomically across both registries or not at all.
* `docker/labels.j2` — new shared partial that emits the OCI image
labels (`source`, `url`, `licenses`, `title`, `description`).
`image.source` is the load-bearing one for GHCR: it links the
package to its source repo, which makes the package inherit the
repo's visibility and grants repo collaborators push/delete access.
* `docker/Dockerfile.{base,redis,viewer}.j2` — include the new
partial. `Dockerfile.base.j2` covers server / celery /
wifi-connect / test (which all `{% include 'Dockerfile.base.j2' %}`);
`redis.j2` and `viewer.j2` have their own production-stage `FROM`
so include `labels.j2` directly.
* `docker-compose.yml.tmpl`, `docker-compose.balena.yml.tmpl`,
`docker-compose.balena.dev.yml.tmpl` — flip 15 `image:` lines from
`screenly/anthias-*` to `ghcr.io/screenly/anthias-*`. Devices pick
this up on next `bin/upgrade_containers.sh` (the script regenerates
`docker-compose.yml` from the template).
Retry-with-backoff seatbelt around `imagetools` calls (originally
added in 8099a14a) is preserved.
Deployment notes
----------------
After this lands, the docker-build workflow will run on master and
publish to GHCR for the first time. Before merging, set
`Screenly`'s default-new-package visibility to "Public" at
https://github.com/organizations/Screenly/settings/packages so the
five new packages don't land private. (`org.opencontainers.image.source`
auto-links each package to this repo but does not set visibility.)
Migration-window risk: between merge and `publish-latest` completion
(~80 min), `ghcr.io/screenly/anthias-*:latest-<board>` tags don't
exist yet. Devices that run `bin/upgrade_containers.sh` in that
window will fail to pull and stay on their existing containers (no
auto-fallback to Docker Hub). They'll pull successfully on the next
upgrade attempt. To minimise impact, merge during a low-fleet-upgrade
window.
Phase 3 (months later, separate PR): stop publishing `latest-*` to
Docker Hub once enough fleet has rotated through an upgrade.
`<short-hash>-<board>` tags on Docker Hub stay around indefinitely
for explicit pins.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>