Publish the image the docs have been telling people to pull #244

Closed
opened 2026-09-03 06:22:39 +00:00 by jcoffey-dev · 0 comments
Owner

The bug

README has told people to run ghcr.io/coffey-labs/ihasmail:latest since the Docker instructions were written, and docs-site/docs/configure.md on the docs site repeats it in four more places.

Nothing ever pushed that image.

$ docker pull ghcr.io/coffey-labs/ihasmail:latest
Error response from daemon: error from registry: denied

.github/workflows/ held ci.yml and nothing else, and there is no reference to ghcr.io, docker/build-push or docker push anywhere in the repo. Seven documented commands have never worked. Installing escapes it only because it says docker build -t ihasmail:local .

It also blocks distribution: TrueNAS and Unraid both install by pulling an image, neither builds from source, so neither catalogue is possible without this.

The workflow

Fires on a published release, and by hand for a ref — the same dispatch trigger ci.yml carries, and the only way to build an image for the tags that predate this file.

Two architectures on native runners rather than one build under QEMU. Emulated arm64 has to run npm ci and the Vite build through instruction translation, which takes tens of minutes and occasionally exhausts memory. ubuntu-24.04-arm is free for public repositories and does the same work at native speed. The cost is pushing by digest and joining the two into one manifest in a final job.

latest moves only for a real release — a prerelease that moved it would hand every :latest deployment an unfinished build — and a dispatch run has to ask for it deliberately.

The version is computed once and handed to both builds, so the two architectures cannot disagree about what they are.

README

Documents which tags exist, that the dated tag is the one to pin, and that building it yourself is still fully supported. docker compose up --build is unchanged; the image is a convenience, not a new requirement.

Read this before merging

GHCR creates a new package private, even for a public repository. After the first successful run, an anonymous docker pull will still answer denied until someone flips the package to public by hand in its settings. Nothing in a workflow can do it.

That is the failure that looks like success, so it is written at the top of the workflow file. Verify with a logged-out pull, not from a machine holding credentials.

Testing

actionlint clean, including shellcheck. It caught a real bug on the first pass: the digest was saved with its sha256: prefix intact and then re-prefixed when the manifest was assembled, which would have produced image@sha256:sha256:… and failed the merge. Fixed, and the tag/digest arguments now build as arrays rather than by word-splitting a string.

It cannot be exercised further from a branch — workflow_dispatch only offers workflows on the default branch. Once merged I'll dispatch it against an existing tag, flip the package public, and confirm an anonymous multi-arch pull.

Not in this PR

The four configure.md references live in Coffey-Labs/ihasmail.org, so they cannot be in the same PR. They need no wording change — they were only wrong because the image did not exist, and publishing makes them true.

Merged 2026-09-02 as coffey-labs/ihasmail@e1c85cf82e

Rebuilt from: git history, session transcript.

## The bug README has told people to run `ghcr.io/coffey-labs/ihasmail:latest` since the Docker instructions were written, and `docs-site/docs/configure.md` on the docs site repeats it in four more places. **Nothing ever pushed that image.** ``` $ docker pull ghcr.io/coffey-labs/ihasmail:latest Error response from daemon: error from registry: denied ``` `.github/workflows/` held `ci.yml` and nothing else, and there is no reference to `ghcr.io`, `docker/build-push` or `docker push` anywhere in the repo. Seven documented commands have never worked. `Installing` escapes it only because it says `docker build -t ihasmail:local .` It also blocks distribution: TrueNAS and Unraid both install by **pulling** an image, neither builds from source, so neither catalogue is possible without this. ## The workflow Fires on a published release, and by hand for a `ref` — the same dispatch trigger `ci.yml` carries, and the only way to build an image for the tags that predate this file. **Two architectures on native runners rather than one build under QEMU.** Emulated arm64 has to run `npm ci` and the Vite build through instruction translation, which takes tens of minutes and occasionally exhausts memory. `ubuntu-24.04-arm` is free for public repositories and does the same work at native speed. The cost is pushing by digest and joining the two into one manifest in a final job. `latest` moves only for a real release — a prerelease that moved it would hand every `:latest` deployment an unfinished build — and a dispatch run has to ask for it deliberately. The version is computed once and handed to both builds, so the two architectures cannot disagree about what they are. ## README Documents which tags exist, that the dated tag is the one to pin, and that building it yourself is still fully supported. `docker compose up --build` is unchanged; the image is a convenience, not a new requirement. ## Read this before merging **GHCR creates a new package private, even for a public repository.** After the first successful run, an anonymous `docker pull` will *still* answer `denied` until someone flips the package to public by hand in its settings. Nothing in a workflow can do it. That is the failure that looks like success, so it is written at the top of the workflow file. Verify with a logged-out pull, not from a machine holding credentials. ## Testing `actionlint` clean, including shellcheck. It caught a real bug on the first pass: the digest was saved with its `sha256:` prefix intact and then re-prefixed when the manifest was assembled, which would have produced `image@sha256:sha256:…` and failed the merge. Fixed, and the tag/digest arguments now build as arrays rather than by word-splitting a string. It cannot be exercised further from a branch — `workflow_dispatch` only offers workflows on the default branch. Once merged I'll dispatch it against an existing tag, flip the package public, and confirm an anonymous multi-arch pull. ## Not in this PR The four `configure.md` references live in `Coffey-Labs/ihasmail.org`, so they cannot be in the same PR. They need no wording change — they were only wrong because the image did not exist, and publishing makes them true. **Merged** 2026-09-02 as coffey-labs/ihasmail@e1c85cf82e5e <sub>Rebuilt from: git history, session transcript.</sub>
This repo is archived. You cannot comment on issues.