diff --git a/.github/workflows/packages.yml b/.github/workflows/packages.yml index 0e46877..02a87f0 100644 --- a/.github/workflows/packages.yml +++ b/.github/workflows/packages.yml @@ -13,8 +13,6 @@ on: required: true PACKAGES_RSA_KEY: required: true - DOCKERHUB_USERNAME: - required: true DOCKERHUB_TOKEN: required: true workflow_dispatch: @@ -79,10 +77,6 @@ jobs: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - uses: docker/login-action@v4 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - uses: goreleaser/goreleaser-action@v7 with: version: v2.18.3 @@ -91,14 +85,40 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} TAP_GITHUB_TOKEN: ${{ secrets.TAP_GITHUB_TOKEN }} - - name: The built binary and the published images print the tag + - name: The built binary and the published image print the tag env: TAG: ${{ inputs.tag }} run: | test "$(dist/fejkdata_linux_amd64_v1/fejkdata --version)" = "fejkdata $TAG" - for image in docker.io/larvit/fejkdata ghcr.io/larvit/fejkdata; do - test "$(docker run --rm "$image:${TAG#v}" --version)" = "fejkdata $TAG" - done + test "$(docker run --rm "ghcr.io/larvit/fejkdata:${TAG#v}" --version)" = "fejkdata $TAG" + + dockerhub: + name: Docker Hub image for ${{ inputs.tag }} + needs: packages + runs-on: ubuntu-26.04 + permissions: + packages: read + steps: + - uses: docker/login-action@v4 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - uses: docker/login-action@v4 + with: + username: ${{ vars.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Copy the GHCR image to Docker Hub, and check it prints the tag + env: + LATEST: ${{ needs.packages.outputs.latest }} + TAG: ${{ inputs.tag }} + run: | + tags=(-t "docker.io/larvit/fejkdata:${TAG#v}") + if [ "$LATEST" = true ]; then + tags+=(-t docker.io/larvit/fejkdata:latest) + fi + docker buildx imagetools create "${tags[@]}" "ghcr.io/larvit/fejkdata:${TAG#v}" + test "$(docker run --rm "docker.io/larvit/fejkdata:${TAG#v}" --version)" = "fejkdata $TAG" repos: name: apt, dnf and apk repositories for ${{ inputs.tag }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index be6d1c1..e3ece3f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -99,5 +99,4 @@ jobs: TAP_GITHUB_TOKEN: ${{ secrets.TAP_GITHUB_TOKEN }} PACKAGES_GPG_KEY: ${{ secrets.PACKAGES_GPG_KEY }} PACKAGES_RSA_KEY: ${{ secrets.PACKAGES_RSA_KEY }} - DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} diff --git a/AGENTS.md b/AGENTS.md index 6cdad16..b32d2bd 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -123,6 +123,7 @@ In [docs/decisions.md](docs/decisions.md): - The locales are Ethnologue 2026's ten most-spoken languages, each in the country with the most speakers of it, the Nordic countries and Germany - A country with no open address register covering it whole ships its places and postal codes without streets - The apt, dnf and apk repositories are static files on GitHub Pages, holding the latest release only +- The image is pushed to GHCR and copied to Docker Hub, whose name the README shows # Scoring run diff --git a/CHANGELOG.md b/CHANGELOG.md index ef7ef62..c1568d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ replacement, and each removed path, column or flag. ### Added - Prebuilt binaries for Linux, macOS and Windows on amd64 and arm64, packages for apt, - dnf, apk, Homebrew and Scoop, and the image `larvit/fejkdata` on Docker Hub and GHCR, so + dnf, apk, Homebrew and Scoop, and the image `larvit/fejkdata` on Docker Hub, also at `ghcr.io/larvit/fejkdata`, so the CLI installs without Go. 0.1.0 ships them too; the README's [Install](https://github.com/larvit/fejkdata#install) lists each command. diff --git a/README.md b/README.md index 269b671..5b88408 100644 --- a/README.md +++ b/README.md @@ -42,8 +42,8 @@ docker run --rm -i larvit/fejkdata < template.json # any OS with go install github.com/larvit/fejkdata/cmd/fejkdata@latest # any OS with Go ``` -The image is also `ghcr.io/larvit/fejkdata`, which CI on shared runners may prefer: -Docker Hub limits anonymous pulls per IP address. +On shared CI runners, pull `ghcr.io/larvit/fejkdata` instead: Docker Hub limits anonymous +pulls per IP address. Debian, Ubuntu, Fedora, RHEL and Alpine install from fejkdata's signed repositories, which hold the latest release: @@ -1280,21 +1280,22 @@ To release, head `CHANGELOG.md` with the version's section in place of `Unreleas and merge: once `main` passes the gate, CI tags that commit `vX.Y.Z` and publishes the GitHub release with the section as its body. A top heading of `[Unreleased]` publishes nothing. CI then runs the Packages workflow on the new tag, which attaches the -binaries and packages, pushes the image to Docker Hub and GHCR, updates the Homebrew tap and the Scoop bucket, -and deploys the apt, dnf and apk repositories to GitHub Pages. The workflow reads -these repository secrets: +binaries and packages, pushes the image to GHCR and copies it to Docker Hub, updates the +Homebrew tap and the Scoop bucket, and deploys the apt, dnf and apk repositories to GitHub +Pages. The workflow reads these repository secrets: -- `DOCKERHUB_USERNAME`, `DOCKERHUB_TOKEN`: a Docker Hub account and access token that can - push to `larvit/fejkdata`. +- `DOCKERHUB_TOKEN`: a Docker Hub access token with Read & Write and no Delete, or an + organization token that can push only to `larvit/fejkdata`. The repository variable + `DOCKERHUB_USERNAME` names its account. - `PACKAGES_GPG_KEY`, `PACKAGES_RSA_KEY`: the private keys that sign the repositories. - `TAP_GITHUB_TOKEN`: a token that can push to `larvit/homebrew-tap` and `larvit/scoop-bucket`. -The image's Docker Hub repository and GHCR package must be public, and Pages must deploy from GitHub Actions. To -package a release again, -run the workflow by hand with its tag; it replaces what it uploaded before. For a tag older -than the latest release it leaves the tap, the bucket, the images' `latest` and -the repositories alone, since each holds one version. +The image's Docker Hub repository and GHCR package must be public, and Pages must deploy +from GitHub Actions. To package a release again, run the workflow by hand with its tag; it +replaces what it uploaded before. For a tag older than the latest release it leaves the +tap, the bucket, the image's `latest` tags and the repositories alone, since each holds one +version. ## License diff --git a/docs/decisions.md b/docs/decisions.md index c8f0d44..b2a56ef 100644 --- a/docs/decisions.md +++ b/docs/decisions.md @@ -903,7 +903,7 @@ By the maintainer's decision of 2026-10-09, every 0.1.0 chunk ships under the fl Shipped under that decision: item 89, the last of 0.1.0, at 52e0304. Its paired ruling against 90ffcff was better. The release's scoring run read Navigation 7.00, Locality 5.75, Shape 6.38, Self-sufficiency 6.50 and overall 6.25; its seats named places items 170 to 172 and 178 hold, and `isPath`, filed as item 251. -Shipped under the floor by the maintainer's decision of 2026-10-13: item 58, prebuilt binaries and packages, which the plan had under 0.2.0, ships as 0.1.0's distribution, and so does the image on Docker Hub, decided 2026-10-14. Neither changes Go code. +Shipped under the floor as 0.1.0's distribution, each by the maintainer's decision: item 58, prebuilt binaries and packages, which the plan had under 0.2.0, on 2026-10-13; and the image on Docker Hub, on 2026-10-14. Neither changes Go code. Shipped by the maintainer's decision of 2026-10-11 with its ratchet waived: item 182, the shipped data as Go packages, at a632a92. Its last paired ruling against bf1becb was same overall, Mid A better and Junior A worse, with Shape fallen, three seats against the namespace mount in `internal/datafiles/mount.go` and the `at` field of `datafiles.Source`; the scoring run that would rule it worse under the floor was not run. `todo.md` item 197 replaces that mount. @@ -970,13 +970,20 @@ vendor-neutral option (`~/.claude/principles/technical-principles.md`). The repo are built here, as static files any host can serve: `release-tooling/package-repos.Dockerfile` signs them with a GPG key (apt, dnf) and an RSA key (apk). A hosted package service would only save that tooling. GitHub already hosts the code (see "GitHub is canonical, and the -module path names it"), so Pages adds no new vendor. The image sits on GHCR for the same -reason; being OCI, it moves to another registry by changing its path. Docker Hub carries -the same image too, by the maintainer's decision of 2026-10-14: it is Docker's default -registry, so `docker run larvit/fejkdata` works with no host in the name. Docker Hub limits -anonymous pulls by IP, so CI on shared runners may pull from `ghcr.io` instead. A Pages site holds at +module path names it"), so Pages adds no new vendor. A Pages site holds at most 1 GB and a release's packages take about 35 MB, so the repositories hold the latest release only; each GitHub release still carries its own packages, and the image keeps a tag per version. Leaving Pages costs each Linux user an edit: their apt list, `.repo` file or apk line names `larvit.github.io`. Valid while GitHub hosts the code. + +## The image is pushed to GHCR and copied to Docker Hub, whose name the README shows + +2026-10-14, decided by the maintainer. Serves goal 1.1; technical principle: prefer the +vendor-neutral option (`~/.claude/principles/technical-principles.md`). Docker Hub is +Docker's default registry, so `docker run larvit/fejkdata` works with no host in the name; +that is a capability only Docker Hub has. GHCR stays the image's source: GoReleaser pushes +there, and a job of its own copies the image to Docker Hub, so a Docker Hub failure holds up +no other channel. GHCR also serves CI on shared runners, which Docker Hub's per-IP limit on +anonymous pulls blocks. Leaving Docker Hub costs each user an edit of every `docker run` line +and pinned script that names the bare `larvit/fejkdata`. diff --git a/release-tooling/goreleaser.yaml b/release-tooling/goreleaser.yaml index 531fca8..b68967b 100644 --- a/release-tooling/goreleaser.yaml +++ b/release-tooling/goreleaser.yaml @@ -54,7 +54,6 @@ nfpms: dockers_v2: - images: - - docker.io/larvit/fejkdata - ghcr.io/larvit/fejkdata tags: - "{{ .Version }}"