4872ef71f8
QEMU emulation of ARM64 on x86 GitHub runners is too slow and unreliable for npm operations, causing builds to hang or crash with "Illegal instruction" errors. Changed platform detection logic to: - Tagged releases (v*.*.*): Build both amd64 and arm64 - All other builds (branches, PRs): Build amd64 only This ensures fast CI feedback during development while still providing multi-arch images for production releases.
270 lines
9.8 KiB
YAML
270 lines
9.8 KiB
YAML
name: Build and Push Docker Images
|
|
|
|
# This workflow is triggered by:
|
|
# - Push to main/develop branches (builds 'latest' or branch-tagged images)
|
|
# - Version tags from Release Please (e.g., v1.2.0 -> builds versioned images)
|
|
# - GitHub Releases (created by Release Please)
|
|
# - Manual workflow dispatch
|
|
|
|
on:
|
|
push:
|
|
branches: [ main, develop ]
|
|
tags: [ 'v*.*.*' ] # Triggered by Release Please tags
|
|
pull_request:
|
|
branches: [ main ]
|
|
release:
|
|
types: [ published ] # Triggered when Release Please creates a release
|
|
workflow_dispatch:
|
|
inputs:
|
|
push:
|
|
description: 'Push images to registry'
|
|
required: false
|
|
default: 'false'
|
|
type: choice
|
|
options:
|
|
- 'true'
|
|
- 'false'
|
|
|
|
env:
|
|
REGISTRY: ghcr.io
|
|
BACKEND_IMAGE_NAME: ${{ github.repository }}/backend
|
|
FRONTEND_IMAGE_NAME: ${{ github.repository }}/frontend
|
|
|
|
jobs:
|
|
build-backend:
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: read
|
|
packages: write
|
|
security-events: write
|
|
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Determine build platforms
|
|
id: platforms
|
|
run: |
|
|
# Only build ARM64 for tagged releases (v*.*.*)
|
|
# QEMU emulation is too slow/unreliable for npm operations on regular builds
|
|
if [[ "${{ github.ref }}" == refs/tags/v* ]]; then
|
|
echo "platforms=linux/amd64,linux/arm64" >> $GITHUB_OUTPUT
|
|
echo "skip_qemu=false" >> $GITHUB_OUTPUT
|
|
else
|
|
echo "platforms=linux/amd64" >> $GITHUB_OUTPUT
|
|
echo "skip_qemu=true" >> $GITHUB_OUTPUT
|
|
fi
|
|
|
|
- name: Set up QEMU
|
|
if: steps.platforms.outputs.skip_qemu != 'true'
|
|
uses: docker/setup-qemu-action@v3
|
|
with:
|
|
platforms: arm64
|
|
|
|
- name: Set up Docker Buildx
|
|
uses: docker/setup-buildx-action@v3
|
|
|
|
- name: Log in to Container Registry
|
|
if: github.event_name != 'pull_request' || github.event.inputs.push == 'true'
|
|
id: login-ghcr
|
|
continue-on-error: true
|
|
uses: docker/login-action@v3
|
|
with:
|
|
registry: ${{ env.REGISTRY }}
|
|
username: ${{ github.actor }}
|
|
password: ${{ secrets.GITHUB_TOKEN }}
|
|
|
|
- name: Extract metadata for Backend
|
|
id: meta-backend
|
|
uses: docker/metadata-action@v5
|
|
with:
|
|
images: ${{ env.REGISTRY }}/${{ env.BACKEND_IMAGE_NAME }}
|
|
labels: |
|
|
org.opencontainers.image.title=PicPeak Backend
|
|
org.opencontainers.image.description=PicPeak photo sharing platform backend service
|
|
org.opencontainers.image.vendor=PicPeak
|
|
maintainer=${{ github.repository_owner }}
|
|
tags: |
|
|
type=ref,event=branch
|
|
type=ref,event=pr
|
|
type=semver,pattern={{version}}
|
|
type=semver,pattern={{major}}.{{minor}}
|
|
type=semver,pattern={{major}}
|
|
type=sha,format=short
|
|
type=raw,value=latest,enable={{is_default_branch}}
|
|
|
|
- name: Build and push Backend Docker image
|
|
uses: docker/build-push-action@v5
|
|
with:
|
|
context: ./backend
|
|
file: ./backend/Dockerfile
|
|
# Always build; only push when registry login succeeded
|
|
push: ${{ (github.event_name != 'pull_request' || github.event.inputs.push == 'true') && steps.login-ghcr.outcome == 'success' }}
|
|
tags: ${{ steps.meta-backend.outputs.tags }}
|
|
labels: ${{ steps.meta-backend.outputs.labels }}
|
|
platforms: ${{ steps.platforms.outputs.platforms }}
|
|
cache-from: type=gha,scope=backend
|
|
cache-to: type=gha,mode=max,scope=backend
|
|
build-args: |
|
|
CACHEBUST=${{ github.run_number }}
|
|
BUILD_DATE=${{ github.event.head_commit.timestamp }}
|
|
VCS_REF=${{ github.sha }}
|
|
VERSION=${{ steps.meta-backend.outputs.version }}
|
|
|
|
- name: Run Trivy vulnerability scanner
|
|
if: github.event_name != 'pull_request' && steps.login-ghcr.outcome == 'success'
|
|
uses: aquasecurity/trivy-action@master
|
|
with:
|
|
image-ref: ${{ env.REGISTRY }}/${{ env.BACKEND_IMAGE_NAME }}:${{ steps.meta-backend.outputs.version }}
|
|
format: 'sarif'
|
|
output: 'trivy-backend.sarif'
|
|
severity: 'CRITICAL,HIGH'
|
|
timeout: '10m'
|
|
|
|
- name: Upload Trivy scan results to GitHub Security tab
|
|
if: github.event_name != 'pull_request' && steps.login-ghcr.outcome == 'success'
|
|
uses: github/codeql-action/upload-sarif@v3
|
|
with:
|
|
sarif_file: 'trivy-backend.sarif'
|
|
category: 'backend-vulnerabilities'
|
|
|
|
build-frontend:
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: read
|
|
packages: write
|
|
security-events: write
|
|
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Determine build platforms
|
|
id: platforms
|
|
run: |
|
|
# Only build ARM64 for tagged releases (v*.*.*)
|
|
# QEMU emulation is too slow/unreliable for npm operations on regular builds
|
|
if [[ "${{ github.ref }}" == refs/tags/v* ]]; then
|
|
echo "platforms=linux/amd64,linux/arm64" >> $GITHUB_OUTPUT
|
|
echo "skip_qemu=false" >> $GITHUB_OUTPUT
|
|
else
|
|
echo "platforms=linux/amd64" >> $GITHUB_OUTPUT
|
|
echo "skip_qemu=true" >> $GITHUB_OUTPUT
|
|
fi
|
|
|
|
- name: Set up QEMU
|
|
if: steps.platforms.outputs.skip_qemu != 'true'
|
|
uses: docker/setup-qemu-action@v3
|
|
with:
|
|
platforms: arm64
|
|
|
|
- name: Set up Docker Buildx
|
|
uses: docker/setup-buildx-action@v3
|
|
|
|
- name: Log in to Container Registry
|
|
if: github.event_name != 'pull_request' || github.event.inputs.push == 'true'
|
|
id: login-ghcr
|
|
continue-on-error: true
|
|
uses: docker/login-action@v3
|
|
with:
|
|
registry: ${{ env.REGISTRY }}
|
|
username: ${{ github.actor }}
|
|
password: ${{ secrets.GITHUB_TOKEN }}
|
|
|
|
- name: Extract metadata for Frontend
|
|
id: meta-frontend
|
|
uses: docker/metadata-action@v5
|
|
with:
|
|
images: ${{ env.REGISTRY }}/${{ env.FRONTEND_IMAGE_NAME }}
|
|
labels: |
|
|
org.opencontainers.image.title=PicPeak Frontend
|
|
org.opencontainers.image.description=PicPeak photo sharing platform frontend application
|
|
org.opencontainers.image.vendor=PicPeak
|
|
maintainer=${{ github.repository_owner }}
|
|
tags: |
|
|
type=ref,event=branch
|
|
type=ref,event=pr
|
|
type=semver,pattern={{version}}
|
|
type=semver,pattern={{major}}.{{minor}}
|
|
type=semver,pattern={{major}}
|
|
type=sha,format=short
|
|
type=raw,value=latest,enable={{is_default_branch}}
|
|
|
|
- name: Build and push Frontend Docker image
|
|
uses: docker/build-push-action@v5
|
|
with:
|
|
context: ./frontend
|
|
file: ./frontend/Dockerfile
|
|
# Always build; only push when registry login succeeded
|
|
push: ${{ (github.event_name != 'pull_request' || github.event.inputs.push == 'true') && steps.login-ghcr.outcome == 'success' }}
|
|
tags: ${{ steps.meta-frontend.outputs.tags }}
|
|
labels: ${{ steps.meta-frontend.outputs.labels }}
|
|
platforms: ${{ steps.platforms.outputs.platforms }}
|
|
cache-from: type=gha,scope=frontend
|
|
cache-to: type=gha,mode=max,scope=frontend
|
|
build-args: |
|
|
CACHEBUST=${{ github.run_number }}
|
|
BUILD_DATE=${{ github.event.head_commit.timestamp }}
|
|
VCS_REF=${{ github.sha }}
|
|
VERSION=${{ steps.meta-frontend.outputs.version }}
|
|
|
|
- name: Run Trivy vulnerability scanner
|
|
if: github.event_name != 'pull_request' && steps.login-ghcr.outcome == 'success'
|
|
uses: aquasecurity/trivy-action@master
|
|
with:
|
|
image-ref: ${{ env.REGISTRY }}/${{ env.FRONTEND_IMAGE_NAME }}:${{ steps.meta-frontend.outputs.version }}
|
|
format: 'sarif'
|
|
output: 'trivy-frontend.sarif'
|
|
severity: 'CRITICAL,HIGH'
|
|
timeout: '10m'
|
|
|
|
- name: Upload Trivy scan results to GitHub Security tab
|
|
if: github.event_name != 'pull_request' && steps.login-ghcr.outcome == 'success'
|
|
uses: github/codeql-action/upload-sarif@v3
|
|
with:
|
|
sarif_file: 'trivy-frontend.sarif'
|
|
category: 'frontend-vulnerabilities'
|
|
|
|
# Note: The publish-manifest job is not needed since docker/build-push-action@v5
|
|
# automatically creates multi-arch manifests when building for multiple platforms.
|
|
# The images are already properly tagged and include all architectures.
|
|
|
|
summary:
|
|
needs: [build-backend, build-frontend]
|
|
if: always()
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: read
|
|
|
|
steps:
|
|
- name: Build Summary
|
|
run: |
|
|
echo "## 🐳 Docker Build Summary" >> $GITHUB_STEP_SUMMARY
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
|
|
if [[ "${{ needs.build-backend.result }}" == "success" ]]; then
|
|
echo "✅ **Backend**: Successfully built" >> $GITHUB_STEP_SUMMARY
|
|
else
|
|
echo "❌ **Backend**: Build failed" >> $GITHUB_STEP_SUMMARY
|
|
fi
|
|
|
|
if [[ "${{ needs.build-frontend.result }}" == "success" ]]; then
|
|
echo "✅ **Frontend**: Successfully built" >> $GITHUB_STEP_SUMMARY
|
|
else
|
|
echo "❌ **Frontend**: Build failed" >> $GITHUB_STEP_SUMMARY
|
|
fi
|
|
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
echo "### 📦 Images" >> $GITHUB_STEP_SUMMARY
|
|
echo "- Backend: \`${{ env.REGISTRY }}/${{ env.BACKEND_IMAGE_NAME }}\`" >> $GITHUB_STEP_SUMMARY
|
|
echo "- Frontend: \`${{ env.REGISTRY }}/${{ env.FRONTEND_IMAGE_NAME }}\`" >> $GITHUB_STEP_SUMMARY
|
|
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
echo "### 🏷️ Tags" >> $GITHUB_STEP_SUMMARY
|
|
echo "Images are tagged based on:" >> $GITHUB_STEP_SUMMARY
|
|
echo "- Branch name (for branch pushes)" >> $GITHUB_STEP_SUMMARY
|
|
echo "- PR number (for pull requests)" >> $GITHUB_STEP_SUMMARY
|
|
echo "- Version tags (for releases)" >> $GITHUB_STEP_SUMMARY
|
|
echo "- Short SHA with branch prefix" >> $GITHUB_STEP_SUMMARY
|
|
echo "- \`latest\` (for main branch)" >> $GITHUB_STEP_SUMMARY
|