Skip to content

ci(release): publish to Docker Hub + bump all actions to Node-24#469

Merged
xe-nvdk merged 4 commits into
mainfrom
ci/dockerhub-release-push
May 27, 2026
Merged

ci(release): publish to Docker Hub + bump all actions to Node-24#469
xe-nvdk merged 4 commits into
mainfrom
ci/dockerhub-release-push

Conversation

@xe-nvdk
Copy link
Copy Markdown
Member

@xe-nvdk xe-nvdk commented May 27, 2026

Summary

Two release-pipeline changes, both verified with live test_mode dispatches (no real latest clobbered):

1. Publish the release image to Docker Hub alongside GHCR

  • New docker-build-dockerhub job does a single multi-platform docker buildx build --push straight to docker.io/basekicklabs/arc (version, short_version, latest).
  • Why a separate job, not the digest-and-merge pattern: GHCR keeps the fast per-platform digest-merge (docker-build + docker-merge) because GHCR's package UI indexes registry-API pushes. Docker Hub's Tags page does NOT index imagetools create pushes — the image was pullable but hub.docker.com/r/basekicklabs/arc/tags showed empty. A direct buildx --push goes through the path Hub's UI indexes, so published tags actually appear. Verified: test run populated the Tags page correctly.
  • Layers come from the same gha cache the GHCR build populates → mostly cache-hit + push.
  • create-draft-release now needs the Docker Hub job, so a Hub push failure blocks the release.

2. test_mode workflow_dispatch input

  • When true, suppresses the latest tag end-to-end on both registries so a CI verification run can't overwrite the real latest. Branch-push releases are never test mode. Used to validate this whole change against 00.00.1 / 00.00.2 without touching production tags.

3. Bump every JS action to its Node-24 major

GitHub forces Node-24 for JS actions 2026-06-02 and removes Node-20 from runners 2026-09-16. Each version verified node24 from release notes (not guessed):
checkout v4→v5, setup-go v5→v6, upload-artifact v4→v6, download-artifact v4→v7, setup-helm v4→v5, setup-kubectl v4→v5, docker/setup-qemu v3→v4, docker/setup-buildx v3→v4, docker/login v3→v4, docker/metadata v5→v6, docker/build-push v5/v6→v7, action-gh-release v1→v3. Left helm/kind-action@v1 (already node24) and claude-code-action@v1 (composite, no JS).

Test plan

  • test_mode dispatch (00.00.1): confirmed dual-registry push works; multi-arch image index live on both GHCR + Docker Hub.
  • test_mode dispatch (00.00.2): confirmed Docker Hub Tags page populates with the direct buildx --push approach.
  • All workflow jobs green on both test runs.
  • YAML validated.

Required repo secrets (already configured)

DOCKERHUB_USERNAME + DOCKERHUB_TOKEN (Docker Hub access token, Read & Write).

Post-merge cleanup

Test tags 00.00.1 / 00.00.2 / 00.00 should be deleted from both GHCR and Docker Hub (harmless 00.00.x test versions; my CLI token lacked delete:packages).

🤖 Generated with Claude Code

Ignacio Van Droogenbroeck and others added 4 commits May 27, 2026 14:59
Every release now publishes the multi-arch manifest (version,
short_version, latest) to both ghcr.io/basekick-labs/arc and
docker.io/basekicklabs/arc.

- docker-build: logs into Docker Hub + pushes the per-platform image
  by digest to both registries (second build-push is a buildkit cache
  hit). Asserts the manifest digest matches across registries.
- docker-merge: creates the multi-arch manifest in both registries
  from the shared digests.

Adds a `test_mode` workflow_dispatch input (default false). When true,
the `latest` tag is suppressed end-to-end so a CI verification run
can't clobber the real latest on either registry. Branch-push
releases are never test mode.

Requires repo secrets DOCKERHUB_USERNAME + DOCKERHUB_TOKEN (token is a
Docker Hub access token with Read & Write, not the account password).
The first test run failed: my digest-equality assertion was wrong.
push-by-digest serialises the manifest per registry, so GHCR and
Docker Hub legitimately return DIFFERENT manifest digests for the
same image content — not a non-reproducible build.

Fix: drop the equality assertion. Write each registry's digest to its
own subdir (/tmp/digests/{ghcr,dockerhub}/) and have each merge step
reference that registry's digest set when building its manifest.
GitHub forces Node-24 for JS actions starting 2026-06-02 and removes
Node-20 from runners 2026-09-16. Bumping every JS action to its
lowest/current Node-24 major so the release pipeline doesn't break
mid-release.

Verified each action's Node runtime from its release notes / action.yml
rather than guessing:

  actions/checkout            v4 → v5   (v5 = first node24)
  actions/setup-go            v5 → v6   (v5 was node20; v6 = node24)
  actions/upload-artifact     v4 → v6   (v6 = first true node24 default)
  actions/download-artifact   v4 → v7   (v7 = first true node24; pairs w/ upload v6)
  azure/setup-helm            v4 → v5   (named in the deprecation warning)
  azure/setup-kubectl         v4 → v5   (node24)
  docker/setup-qemu-action    v3 → v4   (node24)
  docker/setup-buildx-action  v3 → v4   (node24)
  docker/login-action         v3 → v4   (node24)
  docker/metadata-action      v5 → v6   (node24)
  docker/build-push-action    v5,v6 → v7 (v7 = node24 default; unifies the
                                          two new dual-registry pushes with
                                          the existing helm-smoke build)
  softprops/action-gh-release v1 → v3   (v3 = node24)

Left as-is (verified not affected):
  helm/kind-action@v1          — action.yml already declares using: node24
  anthropics/claude-code-action@v1 — composite action, no JS runtime

Chose the artifact pair upload@v6 + download@v7 (the first true node24
defaults) over v7/v8 to clear the deprecation without inheriting v7/v8's
ESM + direct-upload + digest-error behaviour changes mid-release-stabilisation.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ags page)

The digest-and-merge pattern (imagetools create) pushes via the
registry manifest API, which Docker Hub's Tags page does NOT index —
the image was pullable (verified anonymously) but hub.docker.com/r/
basekicklabs/arc/tags showed count:0. GHCR indexes registry-API
pushes fine; Docker Hub only indexes the standard buildx --push path.

Fix: split Docker Hub into its own job (docker-build-dockerhub) that
does ONE multi-platform `docker buildx build --push` straight to
docker.io/basekicklabs/arc with all tags. Layers come from the same
gha cache the GHCR build populates, so it's mostly cache-hit + push.
This goes through the path Hub's UI indexes, so published tags show
up on the Tags page.

GHCR stays on the fast per-platform digest-and-merge (docker-build +
docker-merge) — its package UI indexes those correctly.

test_mode latest-suppression preserved on both registries. Draft
release now also `needs` docker-build-dockerhub so a Hub push failure
blocks the release.
@xe-nvdk
Copy link
Copy Markdown
Member Author

xe-nvdk commented May 27, 2026

@gemini-code-assist review please

@gemini-code-assist
Copy link
Copy Markdown
Contributor

Note

Gemini is unable to generate a review for this pull request due to the file types involved not being currently supported.

1 similar comment
@gemini-code-assist
Copy link
Copy Markdown
Contributor

Note

Gemini is unable to generate a review for this pull request due to the file types involved not being currently supported.

@xe-nvdk xe-nvdk merged commit 64f426e into main May 27, 2026
21 of 22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant