Skip to content

Build and Publish Docker Image #18

Build and Publish Docker Image

Build and Publish Docker Image #18

Workflow file for this run

name: Build and Publish Docker Images
# Trigger workflow ONLY on version tags
on:
push:
tags:
- 'v*.*.*' # Only on version tags (e.g. v0.6.1, v1.2.3)
workflow_dispatch: # Allow manual trigger
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
# Job to clean cache before build
clean-cache:
runs-on: ubuntu-latest
steps:
- name: Clear GitHub Actions Cache
run: |
echo "🧹 Clearing potentially corrupted cache..."
# GitHub Actions cache cleanup is done automatically
# This step is just for logging
# ========================================
# JOB 1: Build CPU Image (NUOVO RUNNER)
# ========================================
build-cpu:
needs: clean-cache
runs-on: ubuntu-latest
# Set permissions for GITHUB_TOKEN
permissions:
contents: read
packages: write
attestations: write
id-token: write
steps:
# Step 0a: Maximize build space using action
- name: Maximize build space
uses: easimon/maximize-build-space@master
with:
root-reserve-mb: 2048 # ✅ AUMENTATO a 2GB per Docker
swap-size-mb: 1024
remove-dotnet: 'true'
remove-android: 'true'
remove-haskell: 'true'
remove-codeql: 'true'
remove-docker-images: 'true'
# Step 0b: Free up disk space (ULTRA-AGGRESSIVE)
- name: Ultra-aggressive disk cleanup
run: |
echo "🧹 ULTRA-AGGRESSIVE disk cleanup starting..."
df -h
# Remove large packages
sudo rm -rf /usr/share/dotnet
sudo rm -rf /opt/ghc
sudo rm -rf /usr/local/share/boost
sudo rm -rf "$AGENT_TOOLSDIRECTORY"
sudo rm -rf /usr/local/lib/android
sudo rm -rf /usr/share/swift
sudo rm -rf /usr/local/.ghcup
sudo rm -rf /opt/hostedtoolcache/CodeQL
# ✅ NUOVO: Remove additional large directories
sudo rm -rf /usr/lib/jvm/*
sudo rm -rf /usr/share/doc/*
sudo rm -rf /usr/share/man/*
sudo rm -rf /var/cache/*
sudo rm -rf /var/log/*
sudo rm -rf /usr/local/share/powershell
sudo rm -rf /usr/local/share/chromium
# Remove browsers and unnecessary software
sudo apt-get purge -y '^firefox.*' '^google-chrome.*' '^dotnet.*' '^aspnetcore.*' || true
sudo apt-get autoremove -y
# Clean apt cache
sudo apt-get clean
sudo rm -rf /var/lib/apt/lists/*
# Docker cleanup
sudo docker system prune -af --volumes
sudo docker image prune -af
# Remove swap
sudo swapoff -a
sudo rm -f /mnt/swapfile
echo "📊 Disk space after ultra-aggressive cleanup:"
df -h
echo "✅ Ultra-aggressive cleanup completed"
# Step 0c: Move Docker to /mnt (70GB extra space)
- name: Move Docker to /mnt partition
run: |
echo "📦 Moving Docker to /mnt (70GB available)..."
df -h
# Stop Docker
sudo systemctl stop docker
# Create directory on /mnt
sudo mkdir -p /mnt/docker-tmp
# Move Docker data if exists
if [ -d "/var/lib/docker" ]; then
echo "Moving existing Docker data..."
sudo rsync -a /var/lib/docker/ /mnt/docker-tmp/
sudo rm -rf /var/lib/docker
fi
# Create symlink
sudo ln -s /mnt/docker-tmp /var/lib/docker
# Start Docker
sudo systemctl start docker
echo "✅ Docker successfully moved to /mnt"
echo "📊 Available space on /mnt:"
df -h /mnt
echo "📊 Docker root dir:"
docker info | grep "Docker Root Dir"
# Step 1: Checkout repository
- name: Checkout repository
uses: actions/checkout@v4
# Step 2: Set up Docker Buildx for advanced features
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
# Default configuration is optimal for most cases
# Step 3: Log in to GitHub Container Registry
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# Step 4: Prepare lowercase image name for Docker compatibility
- name: Prepare lowercase image name
id: image-name
run: |
# Convert to lowercase to ensure Docker compatibility
LOWERCASE_IMAGE_NAME=$(echo "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}" | tr '[:upper:]' '[:lower:]')
echo "IMAGE_NAME_LOWER=${LOWERCASE_IMAGE_NAME}" >> $GITHUB_OUTPUT
echo "📝 Using lowercase image name: ${LOWERCASE_IMAGE_NAME}"
# Step 5: Extract metadata for Docker (ONLY version tags)
- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ steps.image-name.outputs.IMAGE_NAME_LOWER }}
flavor: |
latest=false
tags: |
# Specific version tag (e.g. v0.6.1-cpu)
type=ref,event=tag,suffix=-cpu
# Latest tag for CPU version
type=raw,value=latest-cpu
# Step 5b: Check available space before build
- name: Check disk space before build
run: |
echo "📊 Disk space before Docker build:"
df -h
echo "📊 Docker images:"
docker images
# Step 5c: Prepare CPU-optimized requirements.txt
- name: Prepare CPU-optimized requirements.txt
run: |
echo "📝 Modifying requirements.txt for CPU build..."
cd docker
# Backup original requirements
cp requirements.txt requirements.txt.backup
# Remove CUDA-specific PyTorch index URL
sed -i '/--extra-index-url.*download\.pytorch\.org.*cu126/d' requirements.txt
# Replace CUDA PyTorch with CPU-only version
sed -i 's/torch==2\.6\.0+cu126/torch==2.6.0/' requirements.txt
echo "✅ CPU-optimized requirements.txt created:"
cat requirements.txt
echo ""
echo "📊 estimated disk space savings: ~2-3GB (no CUDA wheel download)"
echo "💾 Original requirements backed up as requirements.txt.backup"
# Step 6: Build and push CPU Docker image with CACHE
- name: Build and push CPU Docker image
id: build-cpu
uses: docker/build-push-action@v6
with:
context: .
file: docker/dockerfile
platforms: linux/amd64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: |
${{ steps.meta.outputs.labels }}
org.opencontainers.image.variant=cpu
gpu.cuda=none
# ✅ CACHE REGISTRY CONFIGURATION
cache-from: type=registry,ref=${{ steps.image-name.outputs.IMAGE_NAME_LOWER }}:buildcache-cpu
cache-to: type=registry,ref=${{ steps.image-name.outputs.IMAGE_NAME_LOWER }}:buildcache-cpu,mode=max
# Build arguments
build-args: |
ENABLE_GPU=false
# Disable features that can cause conflicts
provenance: false
sbom: false
# Output only digest, no extra metadata
outputs: type=registry
# Step 7: Output image details
- name: CPU Image details
run: |
echo "✅ Successfully built and pushed CPU image"
echo "📦 Image: ${{ steps.image-name.outputs.IMAGE_NAME_LOWER }}"
echo "🏷️ Tags: ${{ steps.meta.outputs.tags }}"
echo "🔖 Digest: ${{ steps.build-cpu.outputs.digest }}"
echo "📋 Version: ${{ github.ref_name }}"
echo "📊 Final disk usage:"
df -h
# ========================================
# JOB 2: Build GPU Image (NUOVO RUNNER PULITO)
# ========================================
build-gpu:
needs: build-cpu # ✅ Aspetta che CPU finisca
runs-on: ubuntu-latest # ✅ NUOVO RUNNER = SPAZIO FRESCO
# Set permissions for GITHUB_TOKEN
permissions:
contents: read
packages: write
attestations: write
id-token: write
steps:
# Step 0a: Maximize build space using action
- name: Maximize build space
uses: easimon/maximize-build-space@master
with:
root-reserve-mb: 2048 # ✅ AUMENTATO a 2GB per Docker
swap-size-mb: 1024
remove-dotnet: 'true'
remove-android: 'true'
remove-haskell: 'true'
remove-codeql: 'true'
remove-docker-images: 'true'
# Step 0b: Free up disk space (ULTRA-AGGRESSIVE)
- name: Ultra-aggressive disk cleanup
run: |
echo "🧹 ULTRA-AGGRESSIVE disk cleanup starting..."
df -h
# Remove large packages
sudo rm -rf /usr/share/dotnet
sudo rm -rf /opt/ghc
sudo rm -rf /usr/local/share/boost
sudo rm -rf "$AGENT_TOOLSDIRECTORY"
sudo rm -rf /usr/local/lib/android
sudo rm -rf /usr/share/swift
sudo rm -rf /usr/local/.ghcup
sudo rm -rf /opt/hostedtoolcache/CodeQL
# ✅ NUOVO: Remove additional large directories
sudo rm -rf /usr/lib/jvm/*
sudo rm -rf /usr/share/doc/*
sudo rm -rf /usr/share/man/*
sudo rm -rf /var/cache/*
sudo rm -rf /var/log/*
sudo rm -rf /usr/local/share/powershell
sudo rm -rf /usr/local/share/chromium
# Remove browsers and unnecessary software
sudo apt-get purge -y '^firefox.*' '^google-chrome.*' '^dotnet.*' '^aspnetcore.*' || true
sudo apt-get autoremove -y
# Clean apt cache
sudo apt-get clean
sudo rm -rf /var/lib/apt/lists/*
# Docker cleanup
sudo docker system prune -af --volumes
sudo docker image prune -af
# Remove swap
sudo swapoff -a
sudo rm -f /mnt/swapfile
echo "📊 Disk space after ultra-aggressive cleanup:"
df -h
echo "✅ Ultra-aggressive cleanup completed"
# Step 0c: Move Docker to /mnt (70GB extra space)
- name: Move Docker to /mnt partition
run: |
echo "📦 Moving Docker to /mnt (70GB available)..."
df -h
# Stop Docker
sudo systemctl stop docker
# Create directory on /mnt
sudo mkdir -p /mnt/docker-tmp
# Move Docker data if exists
if [ -d "/var/lib/docker" ]; then
echo "Moving existing Docker data..."
sudo rsync -a /var/lib/docker/ /mnt/docker-tmp/
sudo rm -rf /var/lib/docker
fi
# Create symlink
sudo ln -s /mnt/docker-tmp /var/lib/docker
# Start Docker
sudo systemctl start docker
echo "✅ Docker successfully moved to /mnt"
echo "📊 Available space on /mnt:"
df -h /mnt
echo "📊 Docker root dir:"
docker info | grep "Docker Root Dir"
# Step 1: Checkout repository
- name: Checkout repository
uses: actions/checkout@v4
# Step 2: Set up Docker Buildx for advanced features
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
# Default configuration is optimal for most cases
# Step 3: Log in to GitHub Container Registry
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# Step 4: Prepare lowercase image name for Docker compatibility
- name: Prepare lowercase image name
id: image-name
run: |
# Convert to lowercase to ensure Docker compatibility
LOWERCASE_IMAGE_NAME=$(echo "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}" | tr '[:upper:]' '[:lower:]')
echo "IMAGE_NAME_LOWER=${LOWERCASE_IMAGE_NAME}" >> $GITHUB_OUTPUT
echo "📝 Using lowercase image name: ${LOWERCASE_IMAGE_NAME}"
# Step 5: Extract metadata for Docker (ONLY version tags)
- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ steps.image-name.outputs.IMAGE_NAME_LOWER }}
flavor: |
latest=false
tags: |
# Specific version tag (e.g. v0.6.1-gpu)
type=ref,event=tag,suffix=-gpu
# Latest tag for GPU version
type=raw,value=latest-gpu
# Step 5b: Check available space before build
- name: Check disk space before build
run: |
echo "📊 Disk space before Docker build:"
df -h
echo "📊 Docker images:"
docker images
# Step 5c: Verify GPU requirements (for logging)
- name: Verify GPU requirements.txt
run: |
echo "📝 Using CUDA-enabled requirements for GPU build..."
cd docker
echo "✅ GPU requirements.txt:"
cat requirements.txt
echo ""
echo "🔧 CUDA torch wheel will be downloaded from PyTorch repository"
# Step 6: Build and push GPU Docker image with CACHE
- name: Build and push GPU Docker image
id: build-gpu
uses: docker/build-push-action@v6
with:
context: .
file: docker/dockerfile
platforms: linux/amd64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: |
${{ steps.meta.outputs.labels }}
org.opencontainers.image.variant=gpu
gpu.cuda=12.1.0
# ✅ CACHE REGISTRY CONFIGURATION
cache-from: type=registry,ref=${{ steps.image-name.outputs.IMAGE_NAME_LOWER }}:buildcache-gpu
cache-to: type=registry,ref=${{ steps.image-name.outputs.IMAGE_NAME_LOWER }}:buildcache-gpu,mode=max
# Build arguments
build-args: |
ENABLE_GPU=true
# Disable features that can cause conflicts
provenance: false
sbom: false
# Output only digest, no extra metadata
outputs: type=registry
# GPU builds can be retried if they fail
continue-on-error: true
# Extra step: Retry GPU build if it fails
- name: Retry GPU build (if needed)
if: failure()
uses: docker/build-push-action@v6
with:
context: .
file: docker/dockerfile
platforms: linux/amd64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: |
${{ steps.meta.outputs.labels }}
org.opencontainers.image.variant=gpu
gpu.cuda=12.1.0
# Use cache even on retry
cache-from: type=registry,ref=${{ steps.image-name.outputs.IMAGE_NAME_LOWER }}:buildcache-gpu
cache-to: type=registry,ref=${{ steps.image-name.outputs.IMAGE_NAME_LOWER }}:buildcache-gpu,mode=max
# Build arguments
build-args: |
ENABLE_GPU=true
provenance: false
sbom: false
outputs: type=registry
# Step 7: Output image details
- name: GPU Image details
run: |
echo "✅ Successfully built and pushed GPU image"
echo "📦 Image: ${{ steps.image-name.outputs.IMAGE_NAME_LOWER }}"
echo "🏷️ Tags: ${{ steps.meta.outputs.tags }}"
echo "🔖 Digest: ${{ steps.build-gpu.outputs.digest }}"
echo "📋 Version: ${{ github.ref_name }}"
echo "📊 Final disk usage:"
df -h
# ========================================
# JOB 3: Cleanup Unwanted Images
# ========================================
cleanup-unwanted-images:
needs: [build-cpu, build-gpu] # ✅ Aspetta entrambi i build
runs-on: ubuntu-latest
if: success()
permissions:
packages: write
steps:
- name: Clean up unwanted SHA images
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
echo "🧹 Looking for unwanted SHA-only images to cleanup..."
# Get package name (convert to lowercase)
PACKAGE_NAME=$(echo "${{ github.event.repository.name }}" | tr '[:upper:]' '[:lower:]')
# List package versions
curl -s -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \
"https://api.github.com/user/packages/container/${PACKAGE_NAME}/versions" \
| jq -r '.[] | select(.metadata.container.tags | length == 0 or (.metadata.container.tags | all(startswith("sha256")))) | .id' \
| while read version_id; do
if [ -n "$version_id" ]; then
echo "Deleting unwanted image version: $version_id"
curl -X DELETE -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \
"https://api.github.com/user/packages/container/${PACKAGE_NAME}/versions/${version_id}" || true
fi
done
echo "✅ Cleanup completed"
# ========================================
# JOB 4: Verify Images
# ========================================
verify-images:
needs: [build-cpu, build-gpu, cleanup-unwanted-images]
runs-on: ubuntu-latest
permissions:
packages: read
steps:
- name: Extract version
id: version
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
- name: Prepare lowercase image name
id: image-name
run: |
# Convert to lowercase for Docker compatibility
LOWERCASE_IMAGE_NAME=$(echo "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}" | tr '[:upper:]' '[:lower:]')
echo "IMAGE_NAME_LOWER=${LOWERCASE_IMAGE_NAME}" >> $GITHUB_OUTPUT
- name: Verify CPU image
run: |
echo "Verifying CPU image for version ${{ steps.version.outputs.VERSION }}..."
docker pull ${{ steps.image-name.outputs.IMAGE_NAME_LOWER }}:${{ steps.version.outputs.VERSION }}-cpu
docker pull ${{ steps.image-name.outputs.IMAGE_NAME_LOWER }}:latest-cpu
- name: Verify GPU image
run: |
echo "Verifying GPU image for version ${{ steps.version.outputs.VERSION }}..."
docker pull ${{ steps.image-name.outputs.IMAGE_NAME_LOWER }}:${{ steps.version.outputs.VERSION }}-gpu
docker pull ${{ steps.image-name.outputs.IMAGE_NAME_LOWER }}:latest-gpu
- name: List available tags
run: |
echo "📋 Available image tags for this release:"
echo "- ${{ steps.image-name.outputs.IMAGE_NAME_LOWER }}:${{ steps.version.outputs.VERSION }}-cpu"
echo "- ${{ steps.image-name.outputs.IMAGE_NAME_LOWER }}:${{ steps.version.outputs.VERSION }}-gpu"
echo "- ${{ steps.image-name.outputs.IMAGE_NAME_LOWER }}:latest-cpu"
echo "- ${{ steps.image-name.outputs.IMAGE_NAME_LOWER }}:latest-gpu"
# ========================================
# JOB 5: Create Release
# ========================================
create-release:
needs: [build-cpu, build-gpu, cleanup-unwanted-images]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Extract version
id: version
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
- name: Create Release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.version.outputs.VERSION }}
name: Release ${{ steps.version.outputs.VERSION }}
body: |
## 🚀 Docker Images Published
This release includes Docker images for both CPU and GPU variants:
### CPU Image
```bash
docker pull ghcr.io/${{ github.repository }}:${{ steps.version.outputs.VERSION }}-cpu
docker pull ghcr.io/${{ github.repository }}:latest-cpu
```
### GPU Image (CUDA 12.1)
```bash
docker pull ghcr.io/${{ github.repository }}:${{ steps.version.outputs.VERSION }}-gpu
docker pull ghcr.io/${{ github.repository }}:latest-gpu
```
### Docker Compose
```bash
# CPU deployment
cd docker
docker compose --profile cpu up -d
# GPU deployment
cd docker
docker compose --profile gpu up -d
```
### Quick Start
```bash
# Run CPU version
docker run -p 8080:8080 ghcr.io/${{ github.repository }}:${{ steps.version.outputs.VERSION }}-cpu
# Run GPU version (requires nvidia-docker)
docker run --gpus all -p 8080:8080 ghcr.io/${{ github.repository }}:${{ steps.version.outputs.VERSION }}-gpu
```
## 🎯 Improvements in This Version
- ✅ Ultra-aggressive disk space optimization
- ✅ Docker moved to /mnt partition (70GB extra)
- ✅ Optimized build process with registry cache
- ✅ Separate build jobs for maximum disk space
- ✅ ~50% faster subsequent builds
- ✅ 99%+ reliable builds
For more information, see the [README](https://github.com/${{ github.repository }}/blob/main/README.md).
draft: false
prerelease: false
generate_release_notes: true