mirror of
https://github.com/harvard-edge/cs249r_book.git
synced 2026-04-27 09:59:49 -05:00
- Add 'branches: [dev]' to container build push triggers - Prevents unnecessary builds when publishing to main - Container builds should only happen during development - Keeps scheduled weekly rebuilds and manual triggers intact
313 lines
11 KiB
YAML
313 lines
11 KiB
YAML
name: '🐳 Build Linux Container'
|
|
|
|
# This workflow builds the Quarto build container and pushes it to GitHub Container Registry
|
|
# The container pre-installs all dependencies to eliminate 30-45 minute setup time
|
|
# Includes comprehensive testing to ensure all components work properly
|
|
|
|
# Prevent multiple builds running simultaneously
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
inputs:
|
|
force_rebuild:
|
|
description: 'Force rebuild even if no changes'
|
|
required: false
|
|
default: false
|
|
type: boolean
|
|
no_cache:
|
|
description: 'Disable Docker build cache (fresh build)'
|
|
required: false
|
|
default: false
|
|
type: boolean
|
|
container_registry:
|
|
description: 'Container registry URL'
|
|
required: false
|
|
default: 'ghcr.io'
|
|
type: string
|
|
container_name:
|
|
description: 'Container image name'
|
|
required: false
|
|
default: 'quarto-linux'
|
|
type: string
|
|
container_tag:
|
|
description: 'Container tag'
|
|
required: false
|
|
default: 'latest'
|
|
type: string
|
|
workflow_call:
|
|
inputs:
|
|
force_rebuild:
|
|
required: false
|
|
default: false
|
|
type: boolean
|
|
no_cache:
|
|
required: false
|
|
default: false
|
|
type: boolean
|
|
container_registry:
|
|
required: false
|
|
default: 'ghcr.io'
|
|
type: string
|
|
container_name:
|
|
required: false
|
|
default: 'quarto-linux'
|
|
type: string
|
|
container_tag:
|
|
required: false
|
|
default: 'latest'
|
|
type: string
|
|
|
|
outputs:
|
|
build-status:
|
|
description: "Container build status (success/failure/skipped)"
|
|
value: ${{ jobs.build.outputs.build-status }}
|
|
image-name:
|
|
description: "Full container image name with registry"
|
|
value: ${{ jobs.build.outputs.image-name }}
|
|
image-digest:
|
|
description: "Container image digest (SHA256)"
|
|
value: ${{ jobs.build.outputs.image-digest }}
|
|
cache-hit:
|
|
description: "Whether build used cache (true/false)"
|
|
value: ${{ jobs.build.outputs.cache-hit }}
|
|
|
|
# Re-enable automatic triggers
|
|
schedule:
|
|
- cron: '0 0 * * 0' # Weekly rebuild (Sunday at midnight)
|
|
push:
|
|
branches: [dev] # Only trigger on dev branch, not main
|
|
paths:
|
|
- 'tools/dependencies/**'
|
|
- 'docker/linux/**'
|
|
- '.github/workflows/build-linux-container.yml'
|
|
|
|
env:
|
|
# Container Registry Configuration (configurable via inputs)
|
|
REGISTRY: ${{ (github.event_name == 'workflow_dispatch' && inputs.container_registry) || 'ghcr.io' }}
|
|
IMAGE_NAME: ${{ github.repository }}/${{ (github.event_name == 'workflow_dispatch' && inputs.container_name) || 'quarto-linux' }}
|
|
CONTAINER_TAG: ${{ (github.event_name == 'workflow_dispatch' && inputs.container_tag) || 'latest' }}
|
|
|
|
# Container Build Configuration
|
|
PLATFORM: linux/amd64
|
|
DOCKERFILE_PATH: ./docker/linux/Dockerfile
|
|
CONTEXT_PATH: .
|
|
|
|
jobs:
|
|
build:
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 90 # 1.5 hour timeout for Linux builds
|
|
permissions:
|
|
contents: read
|
|
packages: write
|
|
outputs:
|
|
build-status: ${{ steps.build-summary.outputs.build-status }}
|
|
image-name: ${{ steps.build-summary.outputs.image-name }}
|
|
image-digest: ${{ steps.build-summary.outputs.image-digest }}
|
|
cache-hit: ${{ steps.build-summary.outputs.cache-hit }}
|
|
|
|
steps:
|
|
- name: 🔍 Check workflow environment
|
|
run: |
|
|
set -euo pipefail # Exit immediately on any error
|
|
echo "🔍 Checking workflow environment..."
|
|
echo "📊 Repository: ${{ github.repository }}"
|
|
echo "📊 Actor: ${{ github.actor }}"
|
|
echo "📊 Event: ${{ github.event_name }}"
|
|
echo "📊 Ref: ${{ github.ref }}"
|
|
echo "📊 SHA: ${{ github.sha }}"
|
|
echo "📊 Workflow: ${{ github.workflow }}"
|
|
echo "📊 Run ID: ${{ github.run_id }}"
|
|
echo "📊 Run Number: ${{ github.run_number }}"
|
|
|
|
# Check if we have the required permissions
|
|
echo "🔍 Checking permissions..."
|
|
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
|
echo "✅ Manual workflow dispatch - should have full permissions"
|
|
elif [ "${{ github.event_name }}" = "push" ]; then
|
|
echo "✅ Push event - should have full permissions"
|
|
elif [ "${{ github.event_name }}" = "schedule" ]; then
|
|
echo "✅ Scheduled event - should have full permissions"
|
|
else
|
|
echo "⚠️ Unknown event type: ${{ github.event_name }}"
|
|
fi
|
|
|
|
# Check if secrets are available
|
|
echo "🔍 Checking secrets availability..."
|
|
if [ -n "${{ secrets.GITHUB_TOKEN }}" ]; then
|
|
echo "✅ GITHUB_TOKEN is available"
|
|
else
|
|
echo "❌ GITHUB_TOKEN is not available"
|
|
exit 1
|
|
fi
|
|
|
|
echo "✅ Environment check completed"
|
|
|
|
- name: 📥 Checkout repository
|
|
uses: actions/checkout@v4
|
|
|
|
- name: 🧹 Free up disk space
|
|
run: |
|
|
echo "🧹 Freeing up disk space for large container build..."
|
|
echo "📊 Disk space before cleanup:"
|
|
df -h /
|
|
|
|
# Remove unnecessary packages and files
|
|
sudo rm -rf /usr/share/dotnet
|
|
sudo rm -rf /usr/local/lib/android
|
|
sudo rm -rf /opt/ghc
|
|
sudo rm -rf /opt/hostedtoolcache/CodeQL
|
|
sudo docker system prune -af
|
|
|
|
echo "📊 Disk space after cleanup:"
|
|
df -h /
|
|
echo "✅ Disk cleanup complete"
|
|
|
|
- name: 🐳 Set up Docker Buildx
|
|
uses: docker/setup-buildx-action@v3
|
|
with:
|
|
driver: docker-container
|
|
driver-opts: |
|
|
network=host
|
|
buildkitd-flags: |
|
|
--allow-insecure-entitlement security.insecure
|
|
--allow-insecure-entitlement network.host
|
|
buildkitd-config-inline: |
|
|
[worker.oci]
|
|
max-parallelism = 1
|
|
[registry."docker.io"]
|
|
mirrors = ["mirror.gcr.io"]
|
|
|
|
- name: 🔍 Verify Buildx Builder
|
|
run: |
|
|
echo "🔍 Checking buildx builder status..."
|
|
docker buildx ls
|
|
echo "🔍 Inspecting builder..."
|
|
docker buildx inspect --bootstrap
|
|
echo "✅ Buildx builder ready"
|
|
|
|
- name: 🔐 Log in to Container Registry
|
|
id: login
|
|
uses: docker/login-action@v3
|
|
with:
|
|
registry: ${{ env.REGISTRY }}
|
|
username: ${{ github.actor }}
|
|
password: ${{ secrets.GITHUB_TOKEN }}
|
|
|
|
- name: 🔍 Check registry access
|
|
run: |
|
|
set -euo pipefail # Exit immediately on any error
|
|
echo "🔍 Checking container registry access..."
|
|
echo "📊 Registry: ${{ env.REGISTRY }}"
|
|
echo "📊 Repository: ${{ github.repository }}"
|
|
echo "📊 Actor: ${{ github.actor }}"
|
|
echo "📊 Event: ${{ github.event_name }}"
|
|
|
|
# Test if we can access the registry
|
|
echo "🔍 Testing Docker daemon access..."
|
|
if docker info >/dev/null 2>&1; then
|
|
echo "✅ Docker daemon is accessible"
|
|
else
|
|
echo "❌ Docker daemon not accessible"
|
|
exit 1
|
|
fi
|
|
|
|
# Test registry login with detailed error checking
|
|
echo "🔍 Testing container registry login..."
|
|
if echo "${{ secrets.GITHUB_TOKEN }}" | docker login ${{ env.REGISTRY }} -u ${{ github.actor }} --password-stdin; then
|
|
echo "✅ Successfully logged into container registry"
|
|
else
|
|
echo "❌ Failed to log into container registry"
|
|
echo "🔍 This could be due to:"
|
|
echo " - Missing GITHUB_TOKEN secret"
|
|
echo " - Insufficient permissions"
|
|
echo " - Registry access issues"
|
|
echo "🔍 Checking GITHUB_TOKEN availability..."
|
|
if [ -n "${{ secrets.GITHUB_TOKEN }}" ]; then
|
|
echo "✅ GITHUB_TOKEN is available"
|
|
else
|
|
echo "❌ GITHUB_TOKEN is empty or not available"
|
|
fi
|
|
exit 1
|
|
fi
|
|
|
|
# Verify we can actually push to the registry
|
|
echo "🔍 Testing registry write permissions..."
|
|
TEST_IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/test-push:latest"
|
|
if docker pull hello-world:latest >/dev/null 2>&1; then
|
|
if docker tag hello-world:latest $TEST_IMAGE >/dev/null 2>&1; then
|
|
if docker push $TEST_IMAGE >/dev/null 2>&1; then
|
|
echo "✅ Registry write permissions confirmed"
|
|
# Clean up test image
|
|
docker rmi $TEST_IMAGE >/dev/null 2>&1 || true
|
|
else
|
|
echo "❌ Registry write permissions failed"
|
|
exit 1
|
|
fi
|
|
else
|
|
echo "❌ Failed to tag test image"
|
|
exit 1
|
|
fi
|
|
else
|
|
echo "❌ Failed to pull hello-world image for testing"
|
|
exit 1
|
|
fi
|
|
|
|
- name: 🏷️ Extract metadata
|
|
id: meta
|
|
uses: docker/metadata-action@v5
|
|
with:
|
|
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
|
tags: |
|
|
type=raw,value=${{ env.CONTAINER_TAG }}
|
|
|
|
- name: 🐳 Build Linux container
|
|
id: build
|
|
uses: docker/build-push-action@v5
|
|
with:
|
|
context: ${{ env.CONTEXT_PATH }}
|
|
file: ${{ env.DOCKERFILE_PATH }}
|
|
load: true # Keep local copy for testing
|
|
push: true # Also push to registry
|
|
tags: ${{ steps.meta.outputs.tags }}
|
|
labels: ${{ steps.meta.outputs.labels }}
|
|
no-cache: ${{ github.event_name == 'workflow_dispatch' && inputs.no_cache || false }} # Use cache unless explicitly disabled
|
|
platforms: ${{ env.PLATFORM }}
|
|
provenance: false # Disable provenance for better compatibility
|
|
sbom: false # Disable SBOM for better compatibility
|
|
cache-from: ${{ (github.event_name != 'workflow_dispatch' || !inputs.no_cache) && 'type=gha' || '' }}
|
|
cache-to: ${{ (github.event_name != 'workflow_dispatch' || !inputs.no_cache) && 'type=gha,mode=max' || '' }}
|
|
outputs: type=docker
|
|
|
|
- name: Build Complete
|
|
run: |
|
|
echo "✅ Linux container build completed successfully!"
|
|
echo "📊 Container: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.CONTAINER_TAG }}"
|
|
|
|
- name: 📊 Build Summary
|
|
id: build-summary
|
|
if: always()
|
|
run: |
|
|
# Determine build status
|
|
if [ "${{ steps.build.outcome }}" = "success" ]; then
|
|
BUILD_STATUS="success"
|
|
else
|
|
BUILD_STATUS="failure"
|
|
fi
|
|
|
|
# Extract build information
|
|
IMAGE_NAME="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.CONTAINER_TAG }}"
|
|
IMAGE_DIGEST="${{ steps.build.outputs.digest }}"
|
|
CACHE_HIT="${{ steps.build.outputs.cache-hit }}"
|
|
|
|
echo "build-status=$BUILD_STATUS" >> $GITHUB_OUTPUT
|
|
echo "image-name=$IMAGE_NAME" >> $GITHUB_OUTPUT
|
|
echo "image-digest=$IMAGE_DIGEST" >> $GITHUB_OUTPUT
|
|
echo "cache-hit=$CACHE_HIT" >> $GITHUB_OUTPUT
|
|
|
|
echo "📊 Build Status: $BUILD_STATUS"
|
|
echo "🐳 Image: $IMAGE_NAME"
|
|
echo "🔍 Digest: $IMAGE_DIGEST"
|
|
echo "💾 Cache Hit: $CACHE_HIT" |