Add multi-architecture (amd64 + arm64) Docker builds#128
Add multi-architecture (amd64 + arm64) Docker builds#128dkd-dobberkau wants to merge 1 commit intodecidim:masterfrom
Conversation
Replace the single-arch CI workflow with a multi-arch build pipeline using Docker Buildx push-by-digest pattern and native ARM runners. This enables Decidim Docker images to run natively on Apple Silicon and ARM servers (e.g. AWS Graviton) without slow QEMU emulation. - Dockerfile-test: Make Chrome, ChromeDriver, and dockerize arch-aware via TARGETARCH (Google Chrome on amd64, Chromium on arm64; bump dockerize to v0.8.0 for arm64 support) - Workflow: Split into prepare/build/merge jobs with matrix strategy, native ubuntu-24.04-arm runners, GHA buildx cache, dual registry push (Docker Hub + GHCR) - docker-compose.yml: Revert to upstream state Closes decidim#99 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
📝 WalkthroughWalkthroughThe CI/CD workflow and Dockerfiles have been redesigned to support multi-architecture image builds (linux/amd64 and linux/arm64). The workflow now uses per-architecture builders with manifest merging stages, and Dockerfile-test implements architecture-aware tooling setup. Minor formatting updates were applied to the compose file. Changes
Sequence Diagram(s)sequenceDiagram
participant GH as GitHub Actions
participant Prep as Prepare Stage
participant BA as Build-Arch (Gen/App/Test/Dev)
participant Reg as Registries<br/>(Hub/GHCR)
participant Merge as Manifest<br/>Merge Stage
GH->>Prep: Trigger workflow
Prep->>Prep: Extract decidim, ruby,<br/>node versions
Prep-->>BA: Output versions
BA->>BA: Build image for<br/>linux/amd64
BA->>Reg: Push amd64 image
Reg-->>BA: Return digest
BA->>BA: Build image for<br/>linux/arm64
BA->>Reg: Push arm64 image
Reg-->>BA: Return digest
BA-->>GH: Upload digests<br/>as artifacts
GH->>Merge: Download digests
Merge->>Reg: Login to registries
Merge->>Reg: Create manifest from<br/>amd64 + arm64 digests
Reg-->>Merge: Multi-arch manifest
Merge->>Reg: Push manifest
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~35 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
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 (2)
.github/workflows/dockerhub.yml (2)
37-51:⚠️ Potential issue | 🟠 Major
curlcalls lack failure detection — empty versions will silently propagate.If the curl to fetch
.ruby-versionor.node-versionfails (e.g., tag doesn't exist, network issue), the version output will be empty, and downstream builds will receive empty build-args. Usecurl -sforcurl --failso the step fails fast instead of silently producing broken images.🛡️ Proposed fix
- run: echo "version=$(curl -s $RUBY_VERSION_URL)" >> $GITHUB_OUTPUT + run: echo "version=$(curl -sf "$RUBY_VERSION_URL")" >> $GITHUB_OUTPUT- run: echo "version=$(curl -s $NODE_VERSION_URL | cut -d. -f1)" >> $GITHUB_OUTPUT + run: echo "version=$(curl -sf "$NODE_VERSION_URL" | cut -d. -f1)" >> $GITHUB_OUTPUT
30-35:⚠️ Potential issue | 🟡 MinorPin third-party action to a full commit SHA.
oprypin/find-latest-tag@v1uses a mutable tag that can be retargeted without notice. Pin to a specific commit SHA to prevent supply-chain attacks. The current v1.1.2 release points todd2729fe78b0bb55523ae2b2a310c6773a652bd1:uses: oprypin/find-latest-tag@dd2729fe78b0bb55523ae2b2a310c6773a652bd1 # v1.1.2
🤖 Fix all issues with AI agents
In @.github/workflows/dockerhub.yml:
- Around line 43-45: The current GitHub Actions step "Set Decidim Version" (id:
decidim-version) is injecting `${{ steps.decidim-tag.outputs.tag }}` directly
into the shell `run:` which risks shell interpolation; change the step to pass
the tag via an environment variable and reference that env var in the `run:`
instead: set an env entry (e.g., DECIDIM_TAG: ${{ steps.decidim-tag.outputs.tag
}}) and update the shell command to use the safe env variable (e.g., echo
"version=$(echo $DECIDIM_TAG | cut -c2-)" >> $GITHUB_OUTPUT) so the value is not
expanded by the Action runner into the shell before execution. Ensure the step
keeps the id decidim-version and the output writing to $GITHUB_OUTPUT.
- Around line 63-64: Remove the dead job output declaration that exports
image_name using ${{ env.GENERATOR_IMAGE_NAME }} because the env context isn't
available at the job outputs level and the output is unused; delete the outputs:
image_name line (and its value reference to GENERATOR_IMAGE_NAME) from the job
definition so there is no misleading/empty build-generator.outputs.image_name
export.
🧹 Nitpick comments (2)
Dockerfile-test (1)
9-27: Arch-aware browser installation looks correct overall; a few minor observations.
apt-key add -(Line 15) is deprecated in modern Debian/Ubuntu. Consider using a signed-by keyring approach instead. This is non-urgent since it still works, butapt-keymay be removed in future base images.Neither branch removes
/var/lib/apt/lists/*afterapt-get clean, which inflates the layer size. Consider appending&& rm -rf /var/lib/apt/lists/*to both branches.♻️ Suggested improvement for apt cleanup
RUN if [ "$TARGETARCH" = "arm64" ]; then \ apt-get update \ && apt-get install -y chromium chromium-driver \ - && apt-get clean; \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/*; \ else \ wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \ && echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list \ && apt-get update \ && apt-get install -y google-chrome-stable \ - && apt-get clean \ && apt-get install -y unzip curl \ && CHROME_VERSION=$(google-chrome --version | grep -oP '\d+\.\d+\.\d+\.\d+') \ && CHROMEDRIVER_URL="https://storage.googleapis.com/chrome-for-testing-public/${CHROME_VERSION}/linux64/chromedriver-linux64.zip" \ && curl -sL -o /tmp/chromedriver.zip "$CHROMEDRIVER_URL" \ && unzip -j /tmp/chromedriver.zip '*/chromedriver' -d /usr/local/bin \ && rm /tmp/chromedriver.zip \ - && apt-get clean; \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/*; \ fi.github/workflows/dockerhub.yml (1)
53-627: Consider extracting reusable workflows to reduce ~600 lines of duplication.The four build jobs and four merge jobs are structurally identical, differing only in image name, Dockerfile, build-args, and
needs. A pair of reusable workflows (build-image.ymlandmerge-manifest.ymlwithworkflow_call) parameterized by these values would cut the workflow to ~100 lines and make maintenance far easier.This isn't blocking, but the current duplication means every future change (e.g., adding a registry, changing cache config, adding a build step) must be replicated in four places.
| - name: Set Decidim Version | ||
| id: decidim-version | ||
| run: echo ::set-output name=version::$(echo ${{ steps.decidim-tag.outputs.tag }} | cut -c2-) | ||
| run: echo "version=$(echo ${{ steps.decidim-tag.outputs.tag }} | cut -c2-)" >> $GITHUB_OUTPUT |
There was a problem hiding this comment.
Potential script injection via ${{ }} interpolation in run:.
${{ steps.decidim-tag.outputs.tag }} is interpolated directly into the shell command. If the tag ever contains shell metacharacters (e.g., $(cmd), backticks, ;), they would be executed. While release tags for decidim/decidim are under your control, the safer pattern is to pass the value through an environment variable:
🛡️ Proposed fix
- name: Set Decidim Version
id: decidim-version
- run: echo "version=$(echo ${{ steps.decidim-tag.outputs.tag }} | cut -c2-)" >> $GITHUB_OUTPUT
+ env:
+ TAG: ${{ steps.decidim-tag.outputs.tag }}
+ run: echo "version=$(echo "$TAG" | cut -c2-)" >> $GITHUB_OUTPUT🤖 Prompt for AI Agents
In @.github/workflows/dockerhub.yml around lines 43 - 45, The current GitHub
Actions step "Set Decidim Version" (id: decidim-version) is injecting `${{
steps.decidim-tag.outputs.tag }}` directly into the shell `run:` which risks
shell interpolation; change the step to pass the tag via an environment variable
and reference that env var in the `run:` instead: set an env entry (e.g.,
DECIDIM_TAG: ${{ steps.decidim-tag.outputs.tag }}) and update the shell command
to use the safe env variable (e.g., echo "version=$(echo $DECIDIM_TAG | cut
-c2-)" >> $GITHUB_OUTPUT) so the value is not expanded by the Action runner into
the shell before execution. Ensure the step keeps the id decidim-version and the
output writing to $GITHUB_OUTPUT.
| outputs: | ||
| image_name: ${{ env.GENERATOR_IMAGE_NAME }} |
There was a problem hiding this comment.
Dead output — env context is unavailable at job outputs level.
${{ env.GENERATOR_IMAGE_NAME }} in a job-level outputs expression evaluates to an empty string because env is only available inside steps. Moreover, no downstream job references build-generator.outputs.image_name. Remove these lines to avoid confusion.
🧹 Proposed fix
runs-on: ${{ contains(matrix.platform, 'arm64') && 'ubuntu-24.04-arm' || 'ubuntu-latest' }}
- outputs:
- image_name: ${{ env.GENERATOR_IMAGE_NAME }}
steps:📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| outputs: | |
| image_name: ${{ env.GENERATOR_IMAGE_NAME }} | |
| runs-on: ${{ contains(matrix.platform, 'arm64') && 'ubuntu-24.04-arm' || 'ubuntu-latest' }} | |
| steps: |
🤖 Prompt for AI Agents
In @.github/workflows/dockerhub.yml around lines 63 - 64, Remove the dead job
output declaration that exports image_name using ${{ env.GENERATOR_IMAGE_NAME }}
because the env context isn't available at the job outputs level and the output
is unused; delete the outputs: image_name line (and its value reference to
GENERATOR_IMAGE_NAME) from the job definition so there is no misleading/empty
build-generator.outputs.image_name export.
Summary
linux/arm64support for all 4 Docker images (generator, app, test, dev), enabling native execution on Apple Silicon and ARM servers (AWS Graviton) without QEMU emulationubuntu-24.04-arm)Dockerfile-testarch-aware: Google Chrome + ChromeDriver on amd64, Chromium + chromium-driver on arm64; bump dockerize to v0.8.0 for arm64 supportCloses #99
Changes
Dockerfile-testTARGETARCHbuild arg (auto-set by buildx) for conditional installs:chromium+chromium-driverfrom Debian repos (Google Chrome has no arm64 Linux build).github/workflows/dockerhub.ymlpreparefetches Decidim/Ruby/Node versionsbuild-{image}matrix [linux/amd64, linux/arm64] with native runnersmerge-{image}combines per-arch digests into multi-arch manifestsdocker-compose.ymlMotivation
Issue #99 has been open since Dec 2022. On Apple Silicon Macs and ARM servers, the current amd64-only images run via slow QEMU emulation. Comparable projects (Discourse, Mastodon, GitLab, Chatwoot) all ship multi-arch images. This PR brings Decidim in line.
Test plan
Generated with Claude Code
Summary by CodeRabbit
New Features
Chores