Skip to content

feat(docker): multi-arch image (amd64 + arm64) — native on Apple Silicon Macs - #947

Merged
fernandotonon merged 5 commits into
masterfrom
feat/docker-multiarch-macos
Aug 13, 2026
Merged

feat(docker): multi-arch image (amd64 + arm64) — native on Apple Silicon Macs#947
fernandotonon merged 5 commits into
masterfrom
feat/docker-multiarch-macos

Conversation

@fernandotonon

@fernandotonon fernandotonon commented Aug 12, 2026

Copy link
Copy Markdown
Owner

Problem

The Docker image (ghcr.io/fernandotonon/qtmesh, fernandotr1/qtmesh) was single-arch linux/amd64, built from the amd64-only release .deb. On Apple Silicon Macs it only ran under QEMU emulation — slow, and fragile with software GL + Xvfb + Ogre. There was no native linux/arm64 variant.

Fix — ship a native multi-arch image

1. Build an arm64 .deb

build-linux and its build-n-cache-assimp-linux / build-n-cache-ogre-linux dependencies are now a strategy.matrix over both arches:

arch runner Qt arch triplet
amd64 ubuntu-latest linux_gcc_64 x86_64-linux-gnu
arm64 ubuntu-24.04-arm (native, no QEMU) linux_gcc_arm64 aarch64-linux-gnu

Produces qtmesheditor_amd64.deb + qtmesheditor_arm64.deb (control Architecture: field set per arch). Packaging logic is shared verbatim — only Qt path + lib triplet are matrix vars.

  • Cache keys embed matrix.archrunner.os is Linux for both runners, so without it the two arches would clobber each other's assimp/ogre caches.
  • Per-arch release artifacts linux-binaries-{amd64,arm64} (upload-artifact@v4 rejects duplicate names).
  • amd64-pinned consumers updated: unit-tests-linux restores the -amd64- caches; snap-publish pulls linux-binaries-amd64 (Snap stays amd64-only).

2. Multi-arch image

  • Dockerfile: ARG TARGETARCHCOPY qtmesheditor_${TARGETARCH}.deb, so each platform in the buildx manifest installs its own .deb.
  • Release docker-publish (deploy.yml): downloads both artifacts (pattern: linux-binaries-*, merge-multiple), buildx --platform linux/amd64,linux/arm64, pushes one manifest, and asserts both arches are present.
  • Manual docker-publish.yml: downloads both release .debs; falls back to amd64-only for old releases without an arm64 .deb.

Verification

  • Locally validated the per-arch COPY end-to-end with docker buildx --platform linux/amd64,linux/arm64: the amd64 stage picks qtmesheditor_amd64.deb, the arm64 stage picks qtmesheditor_arm64.deb (confirmed via TARGETARCH + payload echo).
  • Confirmed Qt 6.9.3 for linux arm64 IS available via aqt (arch: linux_gcc_arm64).
  • Both workflow YAMLs lint clean.

Result on Apple Silicon:

docker run --rm ghcr.io/fernandotonon/qtmesh info model.fbx --json
# pulls linux/arm64, runs NATIVELY (no QEMU, no --platform flag)

Notes / risks to watch on first release run

  • arm64 native runner (ubuntu-24.04-arm) is GA on GitHub-hosted.
  • ONNX Runtime downloads a per-platform archive (cmake/OnnxRuntime.cmake) — the arm64 build must resolve the aarch64 ORT; will confirm on the first arm64 CI run.
  • install-qt-action@v3 on the arm64 host — will confirm it resolves linux_gcc_arm64 on the native runner.

The heavy CI (arm64 assimp/ogre/draco builds) only runs on release: published, so it validates on the next release; happy to iterate on that run like we did for the Draco PR.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added native Docker image support for Linux amd64 and arm64, including Apple Silicon Macs.
    • Added architecture-specific Linux packages and multi-architecture image publishing.
    • Added platform verification and smoke testing for published images.
  • Documentation

    • Updated Docker usage and release instructions for architecture-specific packages, runners, and builds.

… macOS

The Docker image was single-arch linux/amd64 (built from the amd64-only
release .deb), so on Apple Silicon Macs it only ran under slow QEMU
emulation. Ship a native linux/arm64 variant so `docker run` works natively
on both Intel and Apple Silicon Macs.

Build the arm64 .deb:
- build-linux + its assimp/ogre cache-builder jobs are now a strategy.matrix
  over {amd64: ubuntu-latest, arm64: ubuntu-24.04-arm} — arm64 builds on
  GitHub's NATIVE arm64 runner (no QEMU). Qt arm64 = arch linux_gcc_arm64
  (path gcc_arm64); lib triplet aarch64-linux-gnu; the .deb control
  Architecture field is sed'd to match. Produces qtmesheditor_{amd64,arm64}.deb.
- Cache keys embed matrix.arch (runner.os is "Linux" for both runners, so
  without it the two arches would clobber each other's assimp/ogre caches).
- Per-arch release artifacts linux-binaries-{amd64,arm64} (upload-artifact@v4
  rejects duplicate names). amd64-pinned consumers updated: unit-tests-linux
  restores the -amd64- caches; snap-publish pulls linux-binaries-amd64.

Multi-arch image:
- Dockerfile uses ARG TARGETARCH → COPY qtmesheditor_${TARGETARCH}.deb, so
  each platform installs its own .deb. (Verified locally with buildx that
  linux/amd64 and linux/arm64 each select the right file.)
- Release docker-publish (deploy.yml) downloads both artifacts + buildx
  --platform linux/amd64,linux/arm64, pushes one manifest, and asserts both
  arches are in it. Manual docker-publish.yml downloads both release .debs
  and falls back to amd64-only for old releases lacking an arm64 .deb.

Docs (README/CLAUDE.md) note the image now runs natively on Apple Silicon.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@fernandotonon, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 38 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 5806ae53-8cdc-4514-932d-6dcb3f486471

📥 Commits

Reviewing files that changed from the base of the PR and between 4561cc0 and 0a4ffb7.

📒 Files selected for processing (1)
  • .github/workflows/deploy.yml
📝 Walkthrough

Walkthrough

The release pipeline now builds amd64 and arm64 Linux packages, publishes multi-architecture Docker images, verifies both image platforms, and selects architecture-specific packages during image builds. Documentation describes the new package, runner, cache, and native ARM behavior.

Changes

Multi-architecture release pipeline

Layer / File(s) Summary
Architecture-specific Linux packages
.github/workflows/deploy.yml
Linux Assimp, Ogre, Qt, packaging, cache, test, and Snap steps now use architecture-specific runners, paths, metadata, caches, and artifacts.
Multi-architecture Docker publishing
.github/workflows/deploy.yml, .github/workflows/docker-publish.yml, Dockerfile, CLAUDE.md, README.md
Docker publishing downloads available Debian packages, builds both platforms when possible, verifies the manifest, and selects the package matching TARGETARCH. Documentation describes amd64 and arm64 support.

Estimated code review effort: 4 (Complex) | ~60 minutes

Mergeability Score: 🟠 High · up to 4561c

The release workflow can publish a broken arm64 image and an unverifiable arm64 package because smoke-test failures are suppressed and the new package is not included in signing. Merge should wait until both release checks are corrected or explicitly accepted by the owner.

Sequence Diagram(s)

sequenceDiagram
  participant LinuxPackageBuild
  participant DockerPublishing
  participant DockerBuildx
  participant DockerRegistry
  participant DockerVerification
  LinuxPackageBuild->>DockerPublishing: provide amd64 and arm64 Debian packages
  DockerPublishing->>DockerBuildx: select platforms and enable arm64 emulation
  DockerBuildx->>DockerRegistry: publish linux/amd64 and linux/arm64 manifest
  DockerVerification->>DockerRegistry: inspect manifest platforms
  DockerVerification->>DockerRegistry: run amd64 and arm64 smoke tests
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the primary change: native amd64 and arm64 Docker image support for Apple Silicon Macs.
Description check ✅ Passed The description provides a detailed summary, technical changes, verification steps, and known risks; the PS1 runtime section is not applicable.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/docker-multiarch-macos

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

- scan-assets-qtmesh builds the Dockerfile from a single downloaded .deb; the
  new Dockerfile COPYs qtmesheditor_${TARGETARCH}.deb, so download to that
  exact name (amd64) and pin --platform linux/amd64. (Broke on this PR:
  "/qtmesheditor_amd64.deb: not found" during docker build.)
- Add qtmesheditor_arm64.deb to the release required-asset verification lists
  now that build-linux produces it for both arches.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4561cc0022

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Dockerfile
Comment on lines +30 to +31
ARG TARGETARCH
COPY qtmesheditor_${TARGETARCH}.deb /tmp/qtmesheditor.deb

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep the asset-scan package name compatible

In the scan-assets-qtmesh job's “Build qtmesh Docker image for asset scan” step, the package is still downloaded as qtmesheditor.deb before this Dockerfile is built. This new COPY instead requests qtmesheditor_amd64.deb on the amd64 runner, so the Docker build cannot find its source file and the prerequisite scan job blocks every downstream build on pushes, pull requests, and releases. Save the scan package under the architecture-qualified name or retain a compatible single-architecture input path.

Useful? React with 👍 / 👎.


dpkg-deb --build --root-owner-group pack-deb
mv pack-deb.deb qtmesheditor_amd64.deb
mv pack-deb.deb qtmesheditor_${{ matrix.arch }}.deb

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Sign the new arm64 release package

When the release matrix creates and publishes qtmesheditor_arm64.deb, the sign-release-artifacts job's wait, download, and signing REQUIRED arrays still contain only qtmesheditor_amd64.deb. The arm64 package is therefore omitted from both its expected .minisig sidecar and the signed SHA256SUMS manifest, leaving the newly advertised ARM download without the integrity metadata supplied for the other release artifacts.

Useful? React with 👍 / 👎.

The arm64 Assimp build failed on Common/Assimp.cpp with 'all warnings being
treated as errors' — Assimp defaults ASSIMP_WARNINGS_AS_ERRORS=ON and arm64
GCC emits a warning amd64 GCC doesn't. Pass -DASSIMP_WARNINGS_AS_ERRORS=OFF
(the Windows Assimp build already does this).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
.github/workflows/deploy.yml (1)

625-646: 🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

Replace actions/cache@v3 with actions/cache@v4.

actions/cache@v3 declares the retired Node 16 runtime. Update all 17 references in .github/workflows/deploy.yml at lines 176, 229, 239, 330, 341, 630, 712, 728, 824, 843, 1119, 1139, 1802, 1892, 1911, 2010, and 2027.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/deploy.yml around lines 625 - 646, Replace every
actions/cache@v3 reference with actions/cache@v4 in
.github/workflows/deploy.yml, including the sites at lines 176, 229, 239, 330,
341, 630, 712, 728, 824, 843, 1119, 1139, 1802, 1892, 1911, 2010, and 2027; no
other workflow behavior needs to change.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/deploy.yml:
- Around line 2995-3001: Update the arm64 variant smoke test in the deployment
workflow to propagate failure from docker run instead of suppressing it with the
warning-and-true fallback. The release must stop when the linux/arm64 --version
check fails; alternatively, execute the equivalent mandatory check on a native
arm64 runner before publishing the image manifest.
- Around line 1041-1049: Update the sign-release-artifacts job to include
qtmesheditor_arm64.deb in each of its three REQUIRED arrays, covering artifact
readiness, download/signing, and hash generation alongside
qtmesheditor_amd64.deb.

---

Outside diff comments:
In @.github/workflows/deploy.yml:
- Around line 625-646: Replace every actions/cache@v3 reference with
actions/cache@v4 in .github/workflows/deploy.yml, including the sites at lines
176, 229, 239, 330, 341, 630, 712, 728, 824, 843, 1119, 1139, 1802, 1892, 1911,
2010, and 2027; no other workflow behavior needs to change.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 0f2e9cfe-494b-4271-9ecd-80d2185c83b2

📥 Commits

Reviewing files that changed from the base of the PR and between c50ce17 and 4561cc0.

📒 Files selected for processing (5)
  • .github/workflows/deploy.yml
  • .github/workflows/docker-publish.yml
  • CLAUDE.md
  • Dockerfile
  • README.md

Comment thread .github/workflows/deploy.yml
Comment on lines +2995 to +3001
# Smoke-test the arm64 variant under QEMU (the layer only installs a
# .deb; this confirms the arm64 binary loads + CLI dispatches).
echo "=== arm64 variant smoke test (QEMU) ==="
docker run --rm --platform linux/arm64 "$IMAGE" --version || {
echo "::warning::arm64 --version under QEMU failed (emulation-only check)"
true
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Fail the release when the arm64 smoke test fails.

Lines 2998-3001 suppress every arm64 --version failure. A missing library or invalid arm64 package can therefore publish a broken linux/arm64 image manifest.

Require this smoke test to pass. If QEMU is not reliable enough to gate releases, run the equivalent check on a native arm64 runner before publishing the manifest.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/deploy.yml around lines 2995 - 3001, Update the arm64
variant smoke test in the deployment workflow to propagate failure from docker
run instead of suppressing it with the warning-and-true fallback. The release
must stop when the linux/arm64 --version check fails; alternatively, execute the
equivalent mandatory check on a native arm64 runner before publishing the image
manifest.

fernandotonon and others added 2 commits August 12, 2026 19:37
build-linux (arm64) failed at Install Qt: aqt errored 'qt_base/qtmultimedia
not found' because host was hardcoded to 'linux', which has no arm64
packages. Qt 6.9.3 arm64 lives under aqt host 'linux_arm64' (arch
linux_gcc_arm64). Make host a matrix var (linux for amd64, linux_arm64 for
arm64).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
install-qt-action@v3 rejects host 'linux_arm64' with 'not one of
windows|mac|linux'. v4 adds linux_arm64 (and windows_arm64) hosts and is
backward-compatible with the amd64 host: linux inputs. Required for the
build-linux arm64 matrix leg.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@fernandotonon

Copy link
Copy Markdown
Owner Author

The native arm64 build lane is green on real CI — validated end-to-end on this PR (the matrix jobs run on pull_request, not just release):

arm64 job result
build-n-cache-assimp-linux (arm64, ubuntu-24.04-arm) ✅ pass
build-n-cache-ogre-linux (arm64, ubuntu-24.04-arm) ✅ pass
build-linux (arm64, …) — app build + qtmesheditor_arm64.deb ✅ pass (14m43s)

Three arm64-specific issues surfaced and were fixed by iterating on CI:

  1. Assimp -Werror fatal on arm64 GCC → -DASSIMP_WARNINGS_AS_ERRORS=OFF (Windows already did this).
  2. Qt installhost: linux has no arm64 packages (aqt: qt_base not found) → made host a matrix var (linux_arm64 for arm64).
  3. install-qt-action@v3 rejects host: linux_arm64 (not one of windows|mac|linux) → bumped to @v4 (adds arm64 hosts; backward-compatible for amd64).

The arm64 app build also confirmed the remaining unknowns resolve on arm64: Qt 6.9.3 arm64 installs, and ONNX Runtime's aarch64 archive downloads/links.

The multi-arch docker-publish job only runs on release: published, so it doesn't execute on this PR — but its per-arch COPY qtmesheditor_${TARGETARCH}.deb was validated locally with docker buildx --platform linux/amd64,linux/arm64 (each platform selects the right .deb), and both .debs are now produced. The first release after merge will publish the multi-arch manifest; happy to babysit that run.

Remaining CI (build-linux amd64 / build-macos / build-windows / unit-tests-linux) are unchanged code paths, still running.

@sonarqubecloud

Copy link
Copy Markdown

@fernandotonon
fernandotonon merged commit e89d3a2 into master Aug 13, 2026
24 checks passed
@fernandotonon
fernandotonon deleted the feat/docker-multiarch-macos branch August 13, 2026 02:07
fernandotonon added a commit that referenced this pull request Aug 13, 2026
Cut a release so the multi-arch Docker work (#947) actually ships: the
arm64 .deb build + the linux/amd64+arm64 docker-publish manifest only run on
'release: published'. 3.28.0 predates that merge (amd64-only image), so a new
release is required to publish the native Apple Silicon image.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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