Build Base Image #26
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |