diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index a549d57..dca4cae 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -12,9 +12,11 @@ env: IMAGE_NAME: ${{ github.repository }} jobs: - # Job for AMD64 architecture - runs on all events - build-amd64: + build: runs-on: ubuntu-latest + strategy: + matrix: + platform: [linux/amd64, linux/arm64] permissions: contents: read packages: write @@ -22,11 +24,15 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 id: buildx - name: Log in to the Container registry + # Only login if we're pushing (not a PR) if: github.event_name != 'pull_request' uses: docker/login-action@v3 with: @@ -34,71 +40,12 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Extract metadata (tags, labels) for Docker - id: meta - uses: docker/metadata-action@v5 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - tags: | - type=ref,event=branch - type=ref,event=pr - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - type=sha,format=long - - - name: Build and push AMD64 Docker image - id: build - uses: docker/build-push-action@v5 - with: - context: . - push: ${{ github.event_name != 'pull_request' }} - tags: ${{ steps.meta.outputs.tags }}-amd64 - labels: ${{ steps.meta.outputs.labels }} - platforms: linux/amd64 - cache-from: type=gha - cache-to: type=gha,mode=max - builder: ${{ steps.buildx.outputs.name }} - outputs: type=image,name=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=${{ github.event_name != 'pull_request' }} - - - name: Export AMD64 digest - if: github.event_name != 'pull_request' + - name: Extract platform short name + id: platform run: | - mkdir -p /tmp/digests - digest="${{ steps.build.outputs.digest }}" - touch "/tmp/digests/${digest#sha256:}" - echo "AMD64_DIGEST=${digest}" >> $GITHUB_ENV - - - name: Upload AMD64 digest - if: github.event_name != 'pull_request' - uses: actions/upload-artifact@v4 - with: - name: amd64-digest - path: /tmp/digests/* - if-no-files-found: error - retention-days: 1 - - # Job for ARM64 architecture - only runs on main branch - build-arm64: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - # Only run this job for pushes to main, not for PRs - if: github.event_name == 'push' && github.ref == 'refs/heads/main' - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - id: buildx - - - name: Log in to the Container registry - uses: docker/login-action@v3 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} + # Extract the architecture from the platform (e.g., amd64 from linux/amd64) + ARCH=$(echo "${{ matrix.platform }}" | cut -d/ -f2) + echo "arch=${ARCH}" >> $GITHUB_OUTPUT - name: Extract metadata (tags, labels) for Docker id: meta @@ -107,64 +54,32 @@ jobs: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} tags: | type=ref,event=branch + type=ref,event=pr type=semver,pattern={{version}} type=semver,pattern={{major}}.{{minor}} type=sha,format=long - - name: Build and push ARM64 Docker image - id: build + - name: Build and push Docker image uses: docker/build-push-action@v5 with: context: . - push: true - tags: ${{ steps.meta.outputs.tags }}-arm64 + # Only push if not a PR + push: ${{ github.event_name != 'pull_request' }} + platforms: ${{ matrix.platform }} + tags: ${{ steps.meta.outputs.tags }}-${{ steps.platform.outputs.arch }} labels: ${{ steps.meta.outputs.labels }} - platforms: linux/arm64 cache-from: type=gha cache-to: type=gha,mode=max - builder: ${{ steps.buildx.outputs.name }} - outputs: type=image,name=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true - - - name: Export ARM64 digest - run: | - mkdir -p /tmp/digests - digest="${{ steps.build.outputs.digest }}" - touch "/tmp/digests/${digest#sha256:}" - echo "ARM64_DIGEST=${digest}" >> $GITHUB_ENV - - - name: Upload ARM64 digest - uses: actions/upload-artifact@v4 - with: - name: arm64-digest - path: /tmp/digests/* - if-no-files-found: error - retention-days: 1 - # Job to create multi-architecture manifest create-manifest: + # Skip this job for pull requests + if: github.event_name != 'pull_request' runs-on: ubuntu-latest - needs: [build-amd64, build-arm64] - # This job only runs if at least build-amd64 completed successfully - # build-arm64 might be skipped for PRs, so we don't require it - if: github.event_name != 'pull_request' && always() && needs.build-amd64.result == 'success' + needs: build permissions: contents: read packages: write steps: - - name: Download AMD64 digest - uses: actions/download-artifact@v4 - with: - name: amd64-digest - path: /tmp/digests/amd64 - - - name: Download ARM64 digest - # Only try to download ARM64 digest if the job ran - if: needs.build-arm64.result == 'success' - uses: actions/download-artifact@v4 - with: - name: arm64-digest - path: /tmp/digests/arm64 - - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 @@ -182,31 +97,29 @@ jobs: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} tags: | type=ref,event=branch + type=ref,event=pr type=semver,pattern={{version}} type=semver,pattern={{major}}.{{minor}} type=sha,format=long - - name: Create manifest list and push + - name: Create manifest lists and push run: | - # Get the first tag from meta outputs - FIRST_TAG=$(echo "${{ steps.meta.outputs.tags }}" | cut -d, -f1) - - # If ARM64 build was skipped, only use AMD64 digest - if [ "${{ needs.build-arm64.result }}" != "success" ]; then - AMD64_DIGEST=$(cat /tmp/digests/amd64/*) - docker buildx imagetools create \ - --tag ${FIRST_TAG} \ - ${AMD64_DIGEST} - else - # Otherwise use both digests - AMD64_DIGEST=$(cat /tmp/digests/amd64/*) - ARM64_DIGEST=$(cat /tmp/digests/arm64/*) - docker buildx imagetools create \ - --tag ${FIRST_TAG} \ - ${AMD64_DIGEST} ${ARM64_DIGEST} - fi - - - name: Inspect image + # Process each tag and create a manifest for it + echo "${{ steps.meta.outputs.tags }}" | while read -r TAG; do + if [ -n "$TAG" ]; then + echo "Creating manifest for $TAG" + docker buildx imagetools create \ + --tag $TAG \ + $TAG-amd64 \ + $TAG-arm64 + fi + done + + - name: Inspect images run: | - FIRST_TAG=$(echo "${{ steps.meta.outputs.tags }}" | cut -d, -f1) - docker buildx imagetools inspect ${FIRST_TAG} \ No newline at end of file + echo "${{ steps.meta.outputs.tags }}" | while read -r TAG; do + if [ -n "$TAG" ]; then + echo "Inspecting manifest for $TAG" + docker buildx imagetools inspect $TAG + fi + done \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 5979be9..3077837 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # Frontend build stage -FROM node:20-slim as frontend-builder +FROM node:20-slim AS frontend-builder WORKDIR /app/frontend # Copy package files first to leverage layer caching @@ -42,4 +42,4 @@ ENV PYTHONUNBUFFERED=1 EXPOSE 8000 # Run the application -CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"] \ No newline at end of file +CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]