name: Docker Build & Publish on: push: branches: [ main, develop ] tags: - 'v*.*.*' pull_request: branches: [ main ] workflow_dispatch: env: REGISTRY: ghcr.io IMAGE_NAME: ${{ github.repository }} CUDA_VERSION: '12.0.0' CUDNN_VERSION: '8' UBUNTU_VERSION: '22.04' jobs: # ============================================================================ # Build CPU-Only Image # ============================================================================ build-cpu: name: Build CPU Image runs-on: ubuntu-latest permissions: contents: read packages: write steps: - name: Checkout repository uses: actions/checkout@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Log in to Container Registry if: github.event_name != 'pull_request' uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Extract metadata 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,prefix=cpu- flavor: | suffix=-cpu,onlatest=true - name: Build and push CPU image uses: docker/build-push-action@v5 with: context: . file: ./Dockerfile target: cpu-runtime push: ${{ github.event_name != 'pull_request' }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha cache-to: type=gha,mode=max build-args: | PYTHON_VERSION=3.10 BUILD_DATE=${{ github.event.head_commit.timestamp }} VCS_REF=${{ github.sha }} # ============================================================================ # Build GPU-Enabled Image (Multi-stage with CUDA) # ============================================================================ build-gpu: name: Build GPU Image runs-on: ubuntu-latest permissions: contents: read packages: write strategy: matrix: cuda_version: ['12.0', '11.8'] steps: - name: Checkout repository uses: actions/checkout@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Log in to Container Registry if: github.event_name != 'pull_request' uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Extract metadata 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,prefix=gpu-cuda${{ matrix.cuda_version }}- flavor: | suffix=-gpu-cuda${{ matrix.cuda_version }},onlatest=true - name: Build and push GPU image uses: docker/build-push-action@v5 with: context: . file: ./Dockerfile target: gpu-runtime push: ${{ github.event_name != 'pull_request' }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha cache-to: type=gha,mode=max build-args: | CUDA_VERSION=${{ matrix.cuda_version }} CUDNN_VERSION=${{ env.CUDNN_VERSION }} UBUNTU_VERSION=${{ env.UBUNTU_VERSION }} PYTHON_VERSION=3.10 BUILD_DATE=${{ github.event.head_commit.timestamp }} VCS_REF=${{ github.sha }} # ============================================================================ # Build Development Image (with all tools) # ============================================================================ build-dev: name: Build Development Image runs-on: ubuntu-latest permissions: contents: read packages: write steps: - name: Checkout repository uses: actions/checkout@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Log in to Container Registry if: github.event_name != 'pull_request' uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Extract metadata 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 flavor: | suffix=-dev,onlatest=true - name: Build and push development image uses: docker/build-push-action@v5 with: context: . file: ./Dockerfile target: development push: ${{ github.event_name != 'pull_request' }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha cache-to: type=gha,mode=max build-args: | CUDA_VERSION=${{ env.CUDA_VERSION }} CUDNN_VERSION=${{ env.CUDNN_VERSION }} UBUNTU_VERSION=${{ env.UBUNTU_VERSION }} PYTHON_VERSION=3.10 BUILD_DATE=${{ github.event.head_commit.timestamp }} VCS_REF=${{ github.sha }} # ============================================================================ # Test Docker Images # ============================================================================ test-images: name: Test Docker Images needs: [build-cpu, build-gpu, build-dev] runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v4 - name: Test CPU image run: | docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}-cpu docker run --rm \ ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}-cpu \ python3 -c "import numpy; import cv2; print('CPU image OK')" - name: Test GPU image (mock) run: | echo "GPU image tests would require GPU runner" echo "Image built successfully: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}-gpu-cuda12.0" # ============================================================================ # Scan Images for Vulnerabilities # ============================================================================ scan-images: name: Security Scan Images needs: [build-cpu, build-gpu] runs-on: ubuntu-latest steps: - name: Run Trivy vulnerability scanner (CPU) uses: aquasecurity/trivy-action@master with: image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}-cpu format: 'sarif' output: 'trivy-cpu-results.sarif' - name: Upload Trivy results to GitHub Security tab uses: github/codeql-action/upload-sarif@v2 with: sarif_file: 'trivy-cpu-results.sarif' # ============================================================================ # Publish Release Images # ============================================================================ publish-release: name: Publish Release Images needs: [build-cpu, build-gpu, test-images, scan-images] runs-on: ubuntu-latest if: startsWith(github.ref, 'refs/tags/v') permissions: contents: read packages: write steps: - name: Checkout repository uses: actions/checkout@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Log in to Container Registry uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Log in to Docker Hub uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Extract version from tag id: version run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT - name: Tag and push release images run: | # Pull images docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}-cpu docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}-gpu-cuda12.0 # Tag as latest docker tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}-cpu \ ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest-cpu docker tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}-gpu-cuda12.0 \ ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest-gpu # Tag with version docker tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}-cpu \ ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-cpu docker tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}-gpu-cuda12.0 \ ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-gpu # Push all tags docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest-cpu docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest-gpu docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-cpu docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-gpu - name: Create release notes run: | echo "## Docker Images" > release_notes.md echo "" >> release_notes.md echo "### Pull commands:" >> release_notes.md echo '```bash' >> release_notes.md echo "# CPU version" >> release_notes.md echo "docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-cpu" >> release_notes.md echo "" >> release_notes.md echo "# GPU version (CUDA 12.0)" >> release_notes.md echo "docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-gpu" >> release_notes.md echo '```' >> release_notes.md - name: Upload release notes uses: actions/upload-artifact@v3 with: name: release-notes path: release_notes.md # ============================================================================ # Generate Image Manifest # ============================================================================ generate-manifest: name: Generate Multi-Arch Manifest needs: [build-cpu, build-gpu] runs-on: ubuntu-latest if: github.event_name != 'pull_request' steps: - name: Log in to Container Registry uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Create and push manifest run: | # Create manifest list for latest docker manifest create ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest \ ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}-cpu \ ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}-gpu-cuda12.0 docker manifest push ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest # ============================================================================ # Cleanup Old Images # ============================================================================ cleanup: name: Cleanup Old Images runs-on: ubuntu-latest if: github.event_name == 'push' && github.ref == 'refs/heads/main' needs: [publish-release] steps: - name: Delete old images uses: actions/delete-package-versions@v4 with: package-name: ${{ env.IMAGE_NAME }} package-type: 'container' min-versions-to-keep: 10 delete-only-untagged-versions: 'true'