Build claude-code manifest from registry tags directly#4243
Conversation
The previous flow ran docker pull on each per-arch tag and then docker inspect to extract digests. On a runner without QEMU/multi-platform support, pulling a single-arch image of a different architecture leaves no local tag, so docker inspect returns "No such object" and the manifest creation fails. Use the registry tag references directly with docker buildx imagetools create so the host platform no longer matters, and switch the idempotency check to docker buildx imagetools inspect. Tolerate hub-tool tag rm failures so a token-scope error does not fail the job after the manifest is already published.
There was a problem hiding this comment.
Local verification
I verified locally that the core change in this manifest:push rewrite — docker buildx imagetools create assembling a multi-arch manifest from registry tag references — works without any local pull. Nothing was pushed (used --dry-run).
1. Per-arch tags exist on Docker Hub
The previous CI run left chatwork/claude-code:2.1.116-aarch64 and :2.1.116-x86_64 on the registry, which I used as inputs.
$ docker buildx imagetools inspect chatwork/claude-code:2.1.116-aarch64
Name: docker.io/chatwork/claude-code:2.1.116-aarch64
MediaType: application/vnd.oci.image.index.v1+json
Manifests:
Platform: linux/arm64
$ docker buildx imagetools inspect chatwork/claude-code:2.1.116-x86_64
Name: docker.io/chatwork/claude-code:2.1.116-x86_64
MediaType: application/vnd.oci.image.index.v1+json
Manifests:
Platform: linux/amd64
2. Multi-arch manifest assembly (--dry-run)
$ docker buildx imagetools create --dry-run \
-t chatwork/claude-code:2.1.116 \
chatwork/claude-code:2.1.116-aarch64 \
chatwork/claude-code:2.1.116-x86_64
The resulting OCI image index includes both platform manifests:
linux/arm64← digest of2.1.116-aarch64(sha256:bf943f...)linux/amd64← digest of2.1.116-x86_64(sha256:d8f14a...)- Plus 2 build attestation manifests
3. Why this matters vs. the old implementation
The old recipe ran docker pull followed by docker inspect to extract the digest of each per-arch image. On the manifest job's amd64 runner (no QEMU), pulling an arm64 single-arch image does not produce a usable local tag, so docker inspect returned "No such object" and manifest creation failed.
The new recipe passes registry references straight to docker buildx imagetools create, so it does not depend on the host's platform support. The CI workflow's missing setup-qemu-action in the manifest job is sidestepped without touching the shared workflow.
Out of scope
hub-tool tag rmmay still fail under the current Docker Hub token scope. The|| truekeeps the job green, but the per-arch suffix tags will linger until the token scope is fixed in a follow-up.
Summary
docker pull+docker inspectdigest flow inclaude-code/Makefilemanifest:pushwithdocker buildx imagetools createagainst the per-arch registry tags directly.docker buildx imagetools inspectso the host's docker daemon state is no longer queried.hub-tool tag rmfailures so a token-scope error does not fail the job after the multi-arch tag is already published.Why
On the previous PR (#4206), the
manifestjob failed withError: No such object: chatwork/claude-code:<version>-aarch64. Root cause: the workflow'smanifestjob runs onubuntu-latest(amd64) and does not rundocker/setup-qemu-action(onlysetup-buildx-action).docker pullof a single-arch arm64 image on an amd64 host without QEMU does not create a usable local tag, so the subsequentdocker inspectreturns "No such object" and the manifest creation fails.docker buildx imagetools createoperates on registry references directly, so it does not depend on the runner's platform support. This sidesteps the missing QEMU setup without changing the shared workflow.Test plan
manifestjob for this PR completes successfully and publisheschatwork/claude-code:<version>andchatwork/claude-code:latestas multi-arch manifests.hub-tool tag rmmay still fail under the current Docker Hub token scope, but|| truekeeps the job green; the per-arch suffix tags will linger until the token scope is fixed.🤖 Generated with Claude Code