From d7536fdc379d0cd0542086bb764377317beaac88 Mon Sep 17 00:00:00 2001 From: vianm Date: Sun, 15 Feb 2026 15:08:07 +0100 Subject: [PATCH 1/2] feat(docker): add multi-arch build support (amd64 + arm64) - Add GitHub Actions workflow for automated multi-arch builds - Add Dockerfile.all-in-one.arm using stable serversideup/php:8.3-fpm-alpine - Builds trigger on new tags, daily schedule, or manual dispatch - Publishes to Docker Hub with version tags Co-Authored-By: Claude --- .github/workflows/build-multiarch.yml | 111 ++++++++++++++++++++++++++ Dockerfile.all-in-one.arm | 49 ++++++++++++ 2 files changed, 160 insertions(+) create mode 100644 .github/workflows/build-multiarch.yml create mode 100644 Dockerfile.all-in-one.arm diff --git a/.github/workflows/build-multiarch.yml b/.github/workflows/build-multiarch.yml new file mode 100644 index 000000000..a21cc4b4d --- /dev/null +++ b/.github/workflows/build-multiarch.yml @@ -0,0 +1,111 @@ +name: Build Multi-Arch Docker Image + +on: + # Trigger on new tags (when you sync from upstream) + push: + tags: + - 'v*' + # Manual trigger + workflow_dispatch: + inputs: + version: + description: 'Version tag to build (e.g., v1.6.0)' + required: false + default: 'latest' + # Check upstream releases daily + schedule: + - cron: '0 6 * * *' # Every day at 6 AM UTC + +env: + REGISTRY: docker.io + IMAGE_NAME: ${{ secrets.DOCKERHUB_USERNAME }}/hi-events-all-in-one + +jobs: + check-upstream: + runs-on: ubuntu-latest + outputs: + should_build: ${{ steps.check.outputs.should_build }} + version: ${{ steps.check.outputs.version }} + steps: + - name: Check upstream release + id: check + run: | + # Get latest release from upstream + LATEST=$(curl -s https://api.github.com/repos/HiEventsDev/Hi.Events/releases/latest | jq -r '.tag_name') + echo "Latest upstream version: $LATEST" + + # Check if we already have this version + TOKEN="${{ secrets.DOCKERHUB_TOKEN }}" + USERNAME="${{ secrets.DOCKERHUB_USERNAME }}" + + if [ -n "$TOKEN" ] && [ -n "$USERNAME" ]; then + EXISTS=$(curl -s "https://hub.docker.com/v2/repositories/$USERNAME/hi-events-all-in-one/tags/$LATEST" | jq -r '.name // empty') + if [ "$EXISTS" = "$LATEST" ]; then + echo "Version $LATEST already exists, skipping build" + echo "should_build=false" >> $GITHUB_OUTPUT + else + echo "New version $LATEST found, will build" + echo "should_build=true" >> $GITHUB_OUTPUT + echo "version=$LATEST" >> $GITHUB_OUTPUT + fi + else + echo "should_build=true" >> $GITHUB_OUTPUT + echo "version=$LATEST" >> $GITHUB_OUTPUT + fi + + build-and-push: + needs: check-upstream + if: needs.check-upstream.outputs.should_build == 'true' || github.event_name == 'push' || github.event_name == 'workflow_dispatch' + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Sync with upstream (for scheduled builds) + if: github.event_name == 'schedule' + run: | + git remote add upstream https://github.com/HiEventsDev/Hi.Events.git || true + git fetch upstream + VERSION="${{ needs.check-upstream.outputs.version }}" + git checkout $VERSION || git checkout upstream/main + + - name: Set up QEMU (for multi-arch) + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Extract metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=ref,event=tag + type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' || github.event_name == 'schedule' }} + type=raw,value=${{ needs.check-upstream.outputs.version }},enable=${{ github.event_name == 'schedule' }} + type=raw,value=${{ github.event.inputs.version }},enable=${{ github.event_name == 'workflow_dispatch' }} + + - name: Build and push multi-arch image + uses: docker/build-push-action@v5 + with: + context: . + file: ./Dockerfile.all-in-one.arm + platforms: linux/amd64,linux/arm64 + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/Dockerfile.all-in-one.arm b/Dockerfile.all-in-one.arm new file mode 100644 index 000000000..b7c858e8c --- /dev/null +++ b/Dockerfile.all-in-one.arm @@ -0,0 +1,49 @@ +FROM node:22-alpine AS node-frontend + +WORKDIR /app/frontend + +RUN apk add --no-cache yarn + +COPY ./frontend/package.json ./frontend/yarn.lock ./ + +COPY ./frontend . + +RUN yarn install && yarn build + +# Use stable multi-arch serversideup/php image instead of beta +FROM serversideup/php:8.3-fpm-alpine + +ENV PHP_OPCACHE_ENABLE=1 + +RUN install-php-extensions intl + +RUN apk add --no-cache nodejs yarn nginx supervisor dos2unix + +COPY --from=node-frontend /app/frontend /app/frontend + +COPY ./backend /app/backend +RUN mkdir -p /app/backend/bootstrap/cache \ + && mkdir -p /app/backend/storage \ + && chown -R www-data:www-data /app/backend \ + && find /app/backend -type d -exec chmod 755 {} \; \ + && find /app/backend -type f -exec chmod 644 {} \; \ + && chmod -R 755 /app/backend/storage /app/backend/bootstrap/cache \ + && composer install --working-dir=/app/backend \ + --ignore-platform-reqs \ + --no-interaction \ + --no-dev \ + --optimize-autoloader \ + --prefer-dist \ + && chmod -R 755 /app/backend/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Serializer + +COPY ./docker/all-in-one/nginx/nginx.conf /etc/nginx/nginx.conf +COPY ./docker/all-in-one/supervisor/supervisord.conf /etc/supervisord.conf + +COPY ./docker/all-in-one/scripts/startup.sh /startup.sh +RUN dos2unix /startup.sh && chmod +x /startup.sh + +EXPOSE 80 + +WORKDIR /app + +CMD ["/startup.sh"] From 4700d06bbe5b4c74a8f410ca5f69515046660fba Mon Sep 17 00:00:00 2001 From: vianm Date: Sun, 15 Feb 2026 16:19:53 +0100 Subject: [PATCH 2/2] feat(docker): add multi-arch support (amd64 + arm64) for all-in-one image - Update Dockerfile.all-in-one to use stable serversideup/php:8.3-fpm-alpine - Add USER root for extension installation - Increase yarn network timeout for slow ARM builds - Update post-release-push-images.yml with QEMU, Buildx and multi-platform support - Remove redundant Dockerfile.all-in-one.arm and build-multiarch.yml This enables Hi.Events to run on ARM devices like Raspberry Pi. --- .github/workflows/build-multiarch.yml | 111 ------------------ .../workflows/post-release-push-images.yml | 17 ++- Dockerfile.all-in-one | 13 +- Dockerfile.all-in-one.arm | 49 -------- 4 files changed, 23 insertions(+), 167 deletions(-) delete mode 100644 .github/workflows/build-multiarch.yml delete mode 100644 Dockerfile.all-in-one.arm diff --git a/.github/workflows/build-multiarch.yml b/.github/workflows/build-multiarch.yml deleted file mode 100644 index a21cc4b4d..000000000 --- a/.github/workflows/build-multiarch.yml +++ /dev/null @@ -1,111 +0,0 @@ -name: Build Multi-Arch Docker Image - -on: - # Trigger on new tags (when you sync from upstream) - push: - tags: - - 'v*' - # Manual trigger - workflow_dispatch: - inputs: - version: - description: 'Version tag to build (e.g., v1.6.0)' - required: false - default: 'latest' - # Check upstream releases daily - schedule: - - cron: '0 6 * * *' # Every day at 6 AM UTC - -env: - REGISTRY: docker.io - IMAGE_NAME: ${{ secrets.DOCKERHUB_USERNAME }}/hi-events-all-in-one - -jobs: - check-upstream: - runs-on: ubuntu-latest - outputs: - should_build: ${{ steps.check.outputs.should_build }} - version: ${{ steps.check.outputs.version }} - steps: - - name: Check upstream release - id: check - run: | - # Get latest release from upstream - LATEST=$(curl -s https://api.github.com/repos/HiEventsDev/Hi.Events/releases/latest | jq -r '.tag_name') - echo "Latest upstream version: $LATEST" - - # Check if we already have this version - TOKEN="${{ secrets.DOCKERHUB_TOKEN }}" - USERNAME="${{ secrets.DOCKERHUB_USERNAME }}" - - if [ -n "$TOKEN" ] && [ -n "$USERNAME" ]; then - EXISTS=$(curl -s "https://hub.docker.com/v2/repositories/$USERNAME/hi-events-all-in-one/tags/$LATEST" | jq -r '.name // empty') - if [ "$EXISTS" = "$LATEST" ]; then - echo "Version $LATEST already exists, skipping build" - echo "should_build=false" >> $GITHUB_OUTPUT - else - echo "New version $LATEST found, will build" - echo "should_build=true" >> $GITHUB_OUTPUT - echo "version=$LATEST" >> $GITHUB_OUTPUT - fi - else - echo "should_build=true" >> $GITHUB_OUTPUT - echo "version=$LATEST" >> $GITHUB_OUTPUT - fi - - build-and-push: - needs: check-upstream - if: needs.check-upstream.outputs.should_build == 'true' || github.event_name == 'push' || github.event_name == 'workflow_dispatch' - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Sync with upstream (for scheduled builds) - if: github.event_name == 'schedule' - run: | - git remote add upstream https://github.com/HiEventsDev/Hi.Events.git || true - git fetch upstream - VERSION="${{ needs.check-upstream.outputs.version }}" - git checkout $VERSION || git checkout upstream/main - - - name: Set up QEMU (for multi-arch) - uses: docker/setup-qemu-action@v3 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Login to Docker Hub - uses: docker/login-action@v3 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - - name: Extract metadata - id: meta - uses: docker/metadata-action@v5 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - tags: | - type=ref,event=tag - type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' || github.event_name == 'schedule' }} - type=raw,value=${{ needs.check-upstream.outputs.version }},enable=${{ github.event_name == 'schedule' }} - type=raw,value=${{ github.event.inputs.version }},enable=${{ github.event_name == 'workflow_dispatch' }} - - - name: Build and push multi-arch image - uses: docker/build-push-action@v5 - with: - context: . - file: ./Dockerfile.all-in-one.arm - platforms: linux/amd64,linux/arm64 - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - cache-from: type=gha - cache-to: type=gha,mode=max diff --git a/.github/workflows/post-release-push-images.yml b/.github/workflows/post-release-push-images.yml index 145ebfdf2..818b5fff9 100644 --- a/.github/workflows/post-release-push-images.yml +++ b/.github/workflows/post-release-push-images.yml @@ -17,16 +17,22 @@ jobs: token: ${{ secrets.GITHUB_TOKEN }} persist-credentials: true + - name: Set up QEMU (for multi-arch builds) + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Log in to Docker Hub - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - # All-in-one Image Steps + # All-in-one Image Steps (multi-arch: amd64 + arm64) - name: Extract metadata (tags, labels) for All-in-one Docker id: meta_all_in_one - uses: docker/metadata-action@v3 + uses: docker/metadata-action@v5 with: images: daveearley/hi.events-all-in-one tags: | @@ -34,13 +40,16 @@ jobs: type=raw,value=latest,enable=${{ github.event.release.prerelease == false }} - name: Build and push All-in-one Docker image - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v5 with: context: ./ file: ./Dockerfile.all-in-one + platforms: linux/amd64,linux/arm64 push: true tags: ${{ steps.meta_all_in_one.outputs.tags }} labels: ${{ steps.meta_all_in_one.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max # Backend Image Steps - name: Extract metadata (tags, labels) for Backend Docker diff --git a/Dockerfile.all-in-one b/Dockerfile.all-in-one index c5377aa15..8e5ff472a 100644 --- a/Dockerfile.all-in-one +++ b/Dockerfile.all-in-one @@ -4,19 +4,26 @@ WORKDIR /app/frontend RUN apk add --no-cache yarn +# Increase network timeout for slow ARM emulation builds +RUN yarn config set network-timeout 600000 + COPY ./frontend/package.json ./frontend/yarn.lock ./ COPY ./frontend . -RUN yarn install && yarn build +RUN yarn install --network-timeout 600000 --frozen-lockfile && yarn build -FROM serversideup/php:beta-8.3.2-fpm-alpine +# Use stable multi-arch serversideup/php image +FROM serversideup/php:8.3-fpm-alpine ENV PHP_OPCACHE_ENABLE=1 +# Switch to root for installing extensions and packages +USER root + RUN install-php-extensions intl -RUN apk add --no-cache nodejs yarn nginx supervisor +RUN apk add --no-cache nodejs yarn nginx supervisor dos2unix COPY --from=node-frontend /app/frontend /app/frontend diff --git a/Dockerfile.all-in-one.arm b/Dockerfile.all-in-one.arm deleted file mode 100644 index b7c858e8c..000000000 --- a/Dockerfile.all-in-one.arm +++ /dev/null @@ -1,49 +0,0 @@ -FROM node:22-alpine AS node-frontend - -WORKDIR /app/frontend - -RUN apk add --no-cache yarn - -COPY ./frontend/package.json ./frontend/yarn.lock ./ - -COPY ./frontend . - -RUN yarn install && yarn build - -# Use stable multi-arch serversideup/php image instead of beta -FROM serversideup/php:8.3-fpm-alpine - -ENV PHP_OPCACHE_ENABLE=1 - -RUN install-php-extensions intl - -RUN apk add --no-cache nodejs yarn nginx supervisor dos2unix - -COPY --from=node-frontend /app/frontend /app/frontend - -COPY ./backend /app/backend -RUN mkdir -p /app/backend/bootstrap/cache \ - && mkdir -p /app/backend/storage \ - && chown -R www-data:www-data /app/backend \ - && find /app/backend -type d -exec chmod 755 {} \; \ - && find /app/backend -type f -exec chmod 644 {} \; \ - && chmod -R 755 /app/backend/storage /app/backend/bootstrap/cache \ - && composer install --working-dir=/app/backend \ - --ignore-platform-reqs \ - --no-interaction \ - --no-dev \ - --optimize-autoloader \ - --prefer-dist \ - && chmod -R 755 /app/backend/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Serializer - -COPY ./docker/all-in-one/nginx/nginx.conf /etc/nginx/nginx.conf -COPY ./docker/all-in-one/supervisor/supervisord.conf /etc/supervisord.conf - -COPY ./docker/all-in-one/scripts/startup.sh /startup.sh -RUN dos2unix /startup.sh && chmod +x /startup.sh - -EXPOSE 80 - -WORKDIR /app - -CMD ["/startup.sh"]