From c4bfbaf4a9556d60a4d83e48db1ac6558a4d7ef9 Mon Sep 17 00:00:00 2001 From: Matthew Meszaros Date: Mon, 7 Sep 2026 09:02:23 -0700 Subject: [PATCH] feat: address the review on the image publicity gate by passing build-push only the seven services that workflow actually publishes, since web, admin and cli have no :dev tag and would have warned falsely on every push to main, by taking the release tag through the step env instead of interpolating github.ref_name into the shell, and by no longer asserting the tag is fine when a pull is refused, because GHCR denies an unknown namespace exactly as it denies a private one, so a mistyped --registry now gets its own reading in the installer message, the troubleshooting table and the fork note, which also splits the personal and organization paths to the visibility setting --- .github/workflows/build-push.yml | 7 ++++++- .github/workflows/release.yml | 4 +++- .../docs/development/deployment-guide.mdx | 2 +- docs/content/docs/development/install.mdx | 2 +- scripts/check-installer.sh | 7 ++++++- site/public/install.sh | 19 ++++++++++++------- site/public/install.sh.sha256 | 2 +- 7 files changed, 30 insertions(+), 13 deletions(-) diff --git a/.github/workflows/build-push.yml b/.github/workflows/build-push.yml index f73a2167..619d1f64 100644 --- a/.github/workflows/build-push.yml +++ b/.github/workflows/build-push.yml @@ -274,5 +274,10 @@ jobs: contents: read steps: - uses: actions/checkout@v4 + # Only what this workflow publishes. web, admin and cli are built at + # tag time and have no :dev tag, so checking them here would warn about + # something no push to main can fix. - name: Pull-test every image anonymously - run: ./scripts/check-images-public.sh --prefix "$IMAGE_PREFIX" --tag dev --warn + run: | + ./scripts/check-images-public.sh --prefix "$IMAGE_PREFIX" --tag dev --warn \ + backend consumer worker forms updater tracking realtime diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b9bfe976..ff1ab0e1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -287,7 +287,9 @@ jobs: steps: - uses: actions/checkout@v4 - name: Pull-test every image anonymously - run: ./scripts/check-images-public.sh --prefix "$IMAGE_PREFIX" --tag "${{ github.ref_name }}" + env: + TAG: ${{ github.ref_name }} + run: ./scripts/check-images-public.sh --prefix "$IMAGE_PREFIX" --tag "$TAG" create-release: name: Create GitHub Release diff --git a/docs/content/docs/development/deployment-guide.mdx b/docs/content/docs/development/deployment-guide.mdx index dfc037ea..1548a5b3 100644 --- a/docs/content/docs/development/deployment-guide.mdx +++ b/docs/content/docs/development/deployment-guide.mdx @@ -752,7 +752,7 @@ CI publishes multi-arch images to `ghcr.io//warmbly/`, which works on any | Push to `main` | backend, consumer, worker, forms, updater, tracking, realtime | `:`, `:dev` | | Tag `vX.Y.Z` | all of the above plus web and admin | `:vX.Y.Z`, `:vX.Y`, `:vX`, `:prod` | -One thing to know if you are running a fork: GHCR creates each package private and does not inherit your repository's visibility, so images published by a green workflow are still unreadable to anyone else until an owner makes each package public by hand under Packages in the org settings. There is no API for it. `make images-check` tells you where a given tag stands, with no credentials, which is the same check the release runs before it publishes. +One thing to know if you are running a fork: GHCR creates each package private and does not inherit your repository's visibility, so images published by a green workflow stay unreadable to everyone else until you make each package public by hand. On a personal fork that is the package's own settings page, reached from the Packages tab on your profile. On an organization fork it is the same page reached from the org's Packages, and public packages have to be permitted in the org's package settings first, or the control is greyed out. There is no API for either. `make images-check` tells you where a given tag stands, with no credentials, and it is the same check the release runs before it publishes. Every app service in `docker-compose.yml` carries both an `image:` and a `build:` key, so the same file serves both paths. `docker compose pull && docker compose up -d` runs the published images and compiles nothing; `docker compose up --build` still builds this checkout. Pin a release with `WARMBLY_TAG=v1.4.2` in `.env`, or point `WARMBLY_IMAGE_PREFIX` at your own registry. diff --git a/docs/content/docs/development/install.mdx b/docs/content/docs/development/install.mdx index 06a42fcc..481eb9f2 100644 --- a/docs/content/docs/development/install.mdx +++ b/docs/content/docs/development/install.mdx @@ -305,7 +305,7 @@ Stops and removes the containers and leaves every byte of data where it is. Addi | `Docker is installed but not answering` | The daemon is not running, or your user is not in the `docker` group yet. `sudo systemctl start docker`, then log out and back in | | `Something already listens on 3000` | Another self-hosted tool. Change the published port in `docker-compose.yml` before starting | | `Could not pull the release images` | The tag does not exist, or this host cannot reach `ghcr.io`. Check [releases](https://github.com/warmbly/warmbly/releases) | -| `The registry refused to serve the release images` | The images are not readable without an account, which is never how a release is meant to ship. Please [report it](https://github.com/warmbly/warmbly/issues). To get past it now, `docker login ghcr.io` or point `--registry` at a mirror | +| `The registry refused to serve the release images` | The images are not readable without an account, which is never how a release is meant to ship. Please [report it](https://github.com/warmbly/warmbly/issues). If you passed `--registry`, check it for a typo first: a namespace that does not exist is refused the same way. To get past it, point `--registry` at a mirror you can read, or `docker login ghcr.io` as someone who already has access to the packages. Logging in does not by itself grant it | | The API never answers | The first boot applies every migration. `docker compose -p warmbly logs -f backend` | | No claim link printed | The database already has accounts, so there is nothing to claim. See [first run](/development/first-run/) | diff --git a/scripts/check-installer.sh b/scripts/check-installer.sh index 86586007..629ee325 100755 --- a/scripts/check-installer.sh +++ b/scripts/check-installer.sh @@ -198,16 +198,21 @@ STUB printf 'Error response from daemon: Head "https://ghcr.io/v2/warmbly/warmbly/forms/manifests/v0.4.0": unauthorized\n' >"$work/log.unauth" printf 'Error response from daemon: manifest unknown\n' >"$work/log.missing" +printf 'Error response from daemon: denied\n' >"$work/log.denied" diag "$work/log.unauth" | grep -q 'refused to serve' || fail "an unauthorized pull is not diagnosed as a registry refusal" -diag "$work/log.unauth" | grep -q 'is not the problem' || +diag "$work/log.unauth" | grep -q 'probably fine' || fail "an unauthorized pull still blames the tag" +diag "$work/log.unauth" | grep -q 'check it for a typo' || + fail "an unauthorized pull does not mention a mistyped --registry, which" diag "$work/log.missing" | grep -q 'may not exist' || fail "an ordinary pull failure lost its generic message" if diag "$work/log.missing" | grep -q 'refused to serve'; then fail "an ordinary pull failure is misreported as a registry refusal" fi +diag "$work/log.denied" | grep -q 'check it for a typo' || + fail "a bare denied does not offer the mistyped-registry reading" pass "diagnoses an unauthorized pull separately from a missing tag" # The checksum is the whole answer to "why would I pipe this into a shell", so diff --git a/site/public/install.sh b/site/public/install.sh index d328cf7a..33d6cf52 100644 --- a/site/public/install.sh +++ b/site/public/install.sh @@ -2284,15 +2284,20 @@ pull_failed() { grep -Eqi 'unauthorized|denied|authentication required' "$LOGFILE"; then _host=${REGISTRY%%/*} fail_with "The registry refused to serve the release images." \ - "It answered unauthorized, so they are not readable without" \ - "an account. The tag ${RESOLVED_TAG} is not the problem." \ + "It answered unauthorized rather than 'no such tag', so" \ + "${RESOLVED_TAG} is probably fine and the images are simply" \ + "not readable without an account." \ "" \ - "Every Warmbly image is meant to be public. If this is the" \ - "published registry, that is our bug, not yours: please tell" \ - "us at https://github.com/$REPO/issues" \ + "Every Warmbly image is meant to be public. If you did not" \ + "pass --registry, that makes this our bug, not yours:" \ + "please tell us at https://github.com/$REPO/issues" \ "" \ - "To get past it now: docker login $_host, or point --registry" \ - "at a mirror this host can read." + "If you did pass it, check it for a typo. A namespace that" \ + "does not exist is refused in exactly the same way as a" \ + "private one." \ + "" \ + "To get past it now: docker login $_host as someone who can" \ + "read these images, or point --registry at a mirror you can." fi fail_with "Could not pull the release images." \ "The tag ${RESOLVED_TAG} may not exist, or this host cannot" \ diff --git a/site/public/install.sh.sha256 b/site/public/install.sh.sha256 index f0fc821c..2c17e6b7 100644 --- a/site/public/install.sh.sha256 +++ b/site/public/install.sh.sha256 @@ -1 +1 @@ -861af096a6e3c520ad0172ca039f376a916aea6c820bff34da66c21ea87d689c install.sh +19ce4203d82b086fd65e30bbd63758364370f154979b664f453842327c67ecb6 install.sh