Skip to content

fix(jpeg): unbreak the arm64 build; add a publish-nothing Docker dry run - #613

Merged
swackhamer merged 2 commits into
mainfrom
claude/docker-cache-bench
Aug 8, 2026
Merged

fix(jpeg): unbreak the arm64 build; add a publish-nothing Docker dry run#613
swackhamer merged 2 commits into
mainfrom
claude/docker-cache-bench

Conversation

@swackhamer

Copy link
Copy Markdown
Collaborator

The bug

perl_sprintf_g declared its snprintf buffer as [0_i8; 32]. c_char is
signed on x86_64 but unsigned on aarch64-linux, so this never compiled for
aarch64-unknown-linux-musl:

error[E0308]: mismatched types
  --> src/core/jpeg_helpers.rs:1725:33
   | expected `*mut u8`, found `*mut i8`

Every arm64 Docker image build failed on it, and release.yml builds the
same target. It survived because arm64 is built only by those two workflows,
both of which run solely on a version tag — so it would first surface during a
release
.

It also can't be caught on an arm64 Mac: signedness follows the platform ABI,
not the CPU. aarch64-apple-darwin is signed (verified locally),
aarch64-unknown-linux-musl is unsigned. Same chip, opposite sign.

Fixed by using libc::c_char instead of hardcoding either side.

The dry run that found it

docker.yml triggered only on v* tags, so the layer cache and the image build
were exercised only by a real release — a bad place to discover a broken
build or a misconfigured cache backend. The WarpBuild proxy settings this repo
now uses had never executed at all.

workflow_dispatch now builds both architectures exactly as a release does and
publishes nothing: push=false, and the Docker Hub login, smoke test, digest
export/upload and the entire manifest-list job are skipped. The ancestry gate is
short-circuited for dispatch deliberately — it exists to keep unreviewed commits
off Docker Hub, and a run that cannot push has nothing to gate.

Measured

The WarpBuild layer cache works (importing cache manifest from gha:…):

cold unchanged commit
Build amd64 232s 23s
Build arm64 309s 19s

Where the build time actually goes, measured, since it rules out the obvious
"cache dependencies in their own layer" tweak:

amd64 arm64
RUN cargo build 163s 235s
├ third-party deps ~12s ~10s
oxidex-tags-* ~23s ~29s
oxidex itself ~128s ~195s

A dependency-layer split would save ~12s — the build is dominated by compiling
oxidex under [profile.release]'s lto = true + codegen-units = 1. That is
the shipped binary, so it is not changed here. .dockerignore already excludes
docs/, tests/ and .github/ so doc-only commits don't bust the layer.

🤖 Generated with Claude Code

swackhamer and others added 2 commits August 8, 2026 15:29
The Docker layer cache and the image build were only ever exercised by a
real release, which is the worst place to find out the cache backend is
misconfigured -- and the WarpBuild proxy settings this repo now uses
(127.0.0.1:49160 plus the network=host buildx driver-opt) had never
executed at all.

workflow_dispatch now builds both architectures exactly as a release
does, and publishes nothing: push=false on the build output, and the
Docker Hub login, smoke test, digest export/upload and the whole
manifest-list job are skipped. The ancestry gate is short-circuited for
dispatch on purpose rather than by accident -- it exists to keep an
unreviewed commit off Docker Hub, and a run that cannot push has nothing
to gate.

Everything above the outputs line is identical between a dry run and a
release, so the dry run measures the real build and populates the real
cache.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
perl_sprintf_g declared its snprintf buffer as [0_i8; 32]. c_char is
signed on x86_64 but UNSIGNED on aarch64, so passing *mut i8 where the
target expects *mut u8 is a hard type error: this never compiled for
aarch64-unknown-linux-musl.

    error[E0308]: mismatched types
      --> src/core/jpeg_helpers.rs:1725:33
       | expected `*mut u8`, found `*mut i8`

Every arm64 Docker image build failed on it, and release.yml builds the
same target. It went unnoticed because arm64 is built only by those two
workflows, both of which run solely on a version tag -- so the failure
would first appear during a release.

Found by the Docker dry run added in the previous commit, which is the
only way to exercise an arm64 build without cutting a release.

The `as u8` on the read side is left as is: it is a no-op where c_char
is unsigned and a reinterpret where it is signed, and both are correct
for the ASCII that %g emits.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@swackhamer
swackhamer merged commit 56975b6 into main Aug 8, 2026
19 checks passed
@swackhamer
swackhamer deleted the claude/docker-cache-bench branch August 8, 2026 22:50
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