From 278f7f18d91ccae8afda48275e8e62b9c2b86d4f Mon Sep 17 00:00:00 2001 From: Anso Date: Tue, 24 Mar 2026 07:50:33 -0400 Subject: [PATCH] fix(ci): add linux/arm64 Docker build support with QEMU optimization (#76) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add docker/setup-qemu-action@v3 to docker-publish.yml (was missing, causing multi-platform builds to hang indefinitely) - Add platforms: linux/amd64,linux/arm64 to build-push-action step - Optimize Dockerfile with --platform=$BUILDPLATFORM on builder stages so TypeScript compilation runs at native amd64 speed; only the lean npm ci --omit=dev step runs under QEMU in the final stage, compiling the three native modules (bcrypt, better-sqlite3, node-pty) for the correct target architecture — reduces arm64 build time from 6+ hours to ~15-30 minutes --- .github/workflows/docker-publish.yml | 6 ++++-- CHANGELOG.md | 7 +++++++ Dockerfile | 29 ++++++++++++++++++++++------ 3 files changed, 34 insertions(+), 8 deletions(-) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index f29da8c1..4a2165d3 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -16,8 +16,10 @@ jobs: - name: Check out the repo uses: actions/checkout@v4 - - name: Set up QEMU (for ARM64 support) + - name: Set up QEMU uses: docker/setup-qemu-action@v3 + with: + platforms: arm64 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 @@ -44,8 +46,8 @@ jobs: uses: docker/build-push-action@v5 with: context: . - platforms: linux/amd64,linux/arm64 push: true + platforms: linux/amd64,linux/arm64 tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} cache-from: type=registry,ref=saelix/sencho:buildcache diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b88286d..c243f3cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added +- feat(ci): add `linux/arm64` platform to Docker Hub publish workflow for ARM server support (e.g. Raspberry Pi 4/5, Oracle ARM VMs) + +### Fixed +- fix(ci): add missing `docker/setup-qemu-action@v3` step to `docker-publish.yml` — without it multi-platform builds hang indefinitely and consume all available runner minutes +- fix(docker): optimize Dockerfile for multi-platform builds using `--platform=$BUILDPLATFORM` on builder stages so TypeScript compilation runs at native amd64 speed; native modules (bcrypt, better-sqlite3, node-pty) are compiled for the target architecture only in the lean production stage via `npm ci --omit=dev`, reducing arm64 QEMU-emulated work from 6+ hours to ~15–30 minutes + ### Added - feat(ui): theme-aware sidebar logo — dark and light variants auto-switch based on active theme (dark/light/auto) diff --git a/Dockerfile b/Dockerfile index d169cd01..e19f679f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,7 @@ # Stage 1: Build Frontend -FROM node:20-alpine AS frontend-builder +# Run on the BUILD platform (amd64 on GitHub Actions) — frontend has no native +# modules so the compiled output is platform-agnostic JS/CSS/HTML. +FROM --platform=$BUILDPLATFORM node:20-alpine AS frontend-builder WORKDIR /app/frontend @@ -17,8 +19,12 @@ COPY frontend/ ./ # Build frontend RUN npm run build -# Stage 2: Build Backend -FROM node:20-alpine AS backend-builder +# Stage 2: Build Backend (TypeScript compilation only) +# Run on the BUILD platform (amd64) so tsc runs at native speed. +# Native modules (bcrypt, better-sqlite3, node-pty) are intentionally NOT +# copied to the final image — they are re-installed on the TARGET platform +# in Stage 3 so the correct architecture binaries are produced. +FROM --platform=$BUILDPLATFORM node:20-alpine AS backend-builder WORKDIR /app/backend @@ -40,6 +46,11 @@ COPY backend/ ./ RUN npm run build # Stage 3: Production +# Runs on the TARGET platform (e.g. linux/arm64 via QEMU on amd64 runners). +# We minimise QEMU-emulated work to a single lean `npm ci --omit=dev` which +# compiles only the three native production modules (bcrypt, better-sqlite3, +# node-pty) for the correct architecture. Build tooling is removed afterwards +# to keep the image small. FROM node:20-alpine # Install Docker CLI, Docker Compose CLI, and Bash for Host Console @@ -47,10 +58,16 @@ RUN apk add --no-cache docker-cli docker-cli-compose bash su-exec WORKDIR /app -# Copy built backend and node_modules from backend-builder +# Install production dependencies on the TARGET platform so native modules +# (bcrypt, better-sqlite3, node-pty) are compiled for the right architecture. +# Build tools are added, used, then removed in a single RUN layer. +COPY backend/package*.json ./ +RUN apk add --no-cache python3 make g++ && \ + npm ci --omit=dev && \ + apk del python3 make g++ + +# Copy compiled TypeScript output from the backend builder (platform-agnostic JS) COPY --from=backend-builder /app/backend/dist ./dist -COPY --from=backend-builder /app/backend/node_modules ./node_modules -COPY --from=backend-builder /app/backend/package.json ./ # Copy built frontend from frontend-builder to public folder COPY --from=frontend-builder /app/frontend/dist ./public