build-base-image.yml•7.28 kB
name: Build Base Image
on:
# Run weekly on Sundays at 3 AM UTC
schedule:
- cron: '0 3 * * 0'
# Allow manual triggering
workflow_dispatch:
inputs:
force_rebuild:
description: 'Force rebuild even if no changes detected'
required: false
default: false
type: boolean
# Trigger when base image dependencies change
push:
branches: [main]
paths:
- 'Dockerfile.base'
- '.github/workflows/build-base-image.yml'
env:
REGISTRY: ghcr.io
BASE_IMAGE_NAME: ${{ github.repository }}/rmcp-base
jobs:
build-base-image:
name: Build and Push Base R Environment
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
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Check if rebuild is needed
id: check-rebuild
run: |
# Check if base image exists and get its creation date
if docker manifest inspect ${{ env.REGISTRY }}/${{ env.BASE_IMAGE_NAME }}:latest >/dev/null 2>&1; then
echo "Base image exists"
# Get image creation date (simplified check)
CREATION_DATE=$(docker manifest inspect ${{ env.REGISTRY }}/${{ env.BASE_IMAGE_NAME }}:latest | jq -r '.history[0].created // empty')
if [ -n "$CREATION_DATE" ]; then
# Check if image is older than 7 days
CREATION_TIMESTAMP=$(date -d "$CREATION_DATE" +%s 2>/dev/null || echo "0")
CURRENT_TIMESTAMP=$(date +%s)
AGE_DAYS=$(( (CURRENT_TIMESTAMP - CREATION_TIMESTAMP) / 86400 ))
echo "Image age: $AGE_DAYS days"
if [ "$AGE_DAYS" -lt 7 ] && [ "${{ github.event.inputs.force_rebuild }}" != "true" ]; then
echo "Image is recent (< 7 days old) and no force rebuild requested"
echo "rebuild_needed=false" >> $GITHUB_OUTPUT
else
echo "Image is old (>= 7 days) or force rebuild requested"
echo "rebuild_needed=true" >> $GITHUB_OUTPUT
fi
else
echo "Could not determine image age, rebuilding"
echo "rebuild_needed=true" >> $GITHUB_OUTPUT
fi
else
echo "Base image does not exist, building"
echo "rebuild_needed=true" >> $GITHUB_OUTPUT
fi
- name: Extract metadata
id: meta
if: steps.check-rebuild.outputs.rebuild_needed == 'true'
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.BASE_IMAGE_NAME }}
tags: |
type=raw,value=latest
type=raw,value={{date 'YYYY-MM-DD'}}
type=sha,prefix={{branch}}-base-
- name: Build and push base image
id: build
if: steps.check-rebuild.outputs.rebuild_needed == 'true'
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile.base
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: |
type=gha
type=registry,ref=${{ env.REGISTRY }}/${{ env.BASE_IMAGE_NAME }}:latest
cache-to: type=gha,mode=max
build-args: |
BUILDKIT_INLINE_CACHE=1
- name: Test base image
if: steps.check-rebuild.outputs.rebuild_needed == 'true'
run: |
echo "🧪 Testing base image functionality..."
# Test R availability and packages
docker run --rm ${{ env.REGISTRY }}/${{ env.BASE_IMAGE_NAME }}:latest R -e "
cat('✅ R version:', R.version.string, '\n')
# Test key packages
library(jsonlite)
library(dplyr)
library(ggplot2)
library(forecast)
library(randomForest)
cat('✅ Key R packages loaded successfully\n')
# Count total packages
pkg_count <- length(.packages(all.available=TRUE))
cat('📦 Total packages available:', pkg_count, '\n')
if (pkg_count < 100) {
stop('❌ Too few packages installed')
}
"
# Test mkcert availability
docker run --rm ${{ env.REGISTRY }}/${{ env.BASE_IMAGE_NAME }}:latest mkcert -version
# Test Python availability
docker run --rm ${{ env.REGISTRY }}/${{ env.BASE_IMAGE_NAME }}:latest python3 --version
echo "✅ Base image tests passed"
- name: Update image metadata
if: steps.check-rebuild.outputs.rebuild_needed == 'true'
run: |
# Extract and display build information
docker run --rm ${{ env.REGISTRY }}/${{ env.BASE_IMAGE_NAME }}:latest cat /opt/rmcp-base-info.json | jq .
echo "📊 Base image build completed:"
echo " Registry: ${{ env.REGISTRY }}/${{ env.BASE_IMAGE_NAME }}"
echo " Tags: ${{ steps.meta.outputs.tags }}"
echo " Platforms: linux/amd64,linux/arm64"
# Get final image size
docker images --filter "reference=${{ env.REGISTRY }}/${{ env.BASE_IMAGE_NAME }}" --format "table {{.Repository}}:{{.Tag}}\t{{.Size}}\t{{.CreatedAt}}"
- name: Create release note
if: steps.check-rebuild.outputs.rebuild_needed == 'true' && github.event_name == 'schedule'
run: |
echo "📦 **Base Image Updated**" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "The RMCP base R environment has been rebuilt with:" >> $GITHUB_STEP_SUMMARY
echo "- 🔄 Latest system packages" >> $GITHUB_STEP_SUMMARY
echo "- 📊 All R statistical packages" >> $GITHUB_STEP_SUMMARY
echo "- 🔒 mkcert for HTTPS development" >> $GITHUB_STEP_SUMMARY
echo "- 🐍 Python development environment" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Impact:** CI builds will now be significantly faster! ⚡" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Next Steps:** " >> $GITHUB_STEP_SUMMARY
echo "1. Main CI will automatically use the new base image" >> $GITHUB_STEP_SUMMARY
echo "2. Development Docker builds will benefit from faster startup" >> $GITHUB_STEP_SUMMARY
echo "3. No action required from developers" >> $GITHUB_STEP_SUMMARY
- name: Skip notification
if: steps.check-rebuild.outputs.rebuild_needed == 'false'
run: |
echo "ℹ️ Base image rebuild skipped - image is recent and no changes detected" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Current base image:** ${{ env.REGISTRY }}/${{ env.BASE_IMAGE_NAME }}:latest" >> $GITHUB_STEP_SUMMARY
echo "**Reason:** Image is less than 7 days old and no force rebuild requested" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "To force a rebuild, use the manual trigger with 'force_rebuild' option." >> $GITHUB_STEP_SUMMARY