-
Notifications
You must be signed in to change notification settings - Fork 30
Add multi-architecture (amd64 + arm64) Docker builds #128
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Add multi-architecture (amd64 + arm64) Docker builds #128
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.
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 (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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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