Skip to content

Build Push Spack Buildcache #43

Build Push Spack Buildcache

Build Push Spack Buildcache #43

name: Build Push Spack Buildcache
on:
workflow_dispatch:
schedule:
- cron: '0 6 * * 1'
push:
branches:
- main
paths:
- Dockerfile
- .github/workflows/build-push-spack-buildcache.yaml
concurrency:
group: ${{ github.workflow }}-${{ github.ref_name || github.event_name }}
cancel-in-progress: true
permissions:
contents: read
packages: write
jobs:
refresh-buildcache:
runs-on: ubuntu-latest
env:
SPACK_BASE_IMAGE: simphony-spack-base:buildcache-${{ github.run_id }}
SPACK_BASE_CACHE_SCOPE: spack-base-defaults
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build Spack base image
uses: docker/build-push-action@v6
with:
context: .
pull: true
load: true
target: spack-base
tags: ${{ env.SPACK_BASE_IMAGE }}
cache-from: type=gha,scope=${{ env.SPACK_BASE_CACHE_SCOPE }}
cache-to: type=gha,mode=max,scope=${{ env.SPACK_BASE_CACHE_SCOPE }}
# Keep the Spack install/publish phase in the workflow instead of a Dockerfile
# RUN step. BuildKit secrets could pass GHCR credentials into `docker build`,
# but registry writes are easier to reason about as explicit workflow side
# effects, and this keeps partial autopush progress plus cache verification
# separate from image layer creation.
- name: Populate dependency buildcache
run: |
docker run --rm \
-e REGISTRY_USER='${{ github.actor }}' \
-e REGISTRY_TOKEN='${{ secrets.GITHUB_TOKEN }}' \
'${{ env.SPACK_BASE_IMAGE }}' \
bash -lc '
set -euo pipefail
# Spack 1.x keeps the builtin package repo in the separate spack-packages git
# repository, so we can keep the Spack tool pinned while still floating package
# metadata to the latest builtin develop branch on each run.
spack repo update -b develop builtin
# Fetch the latest simphony package metadata from the external repo as well.
spack repo add https://github.com/BNLNPPS/spack-packages
# Use a dedicated GHCR package for the Spack OCI mirror so Docker cache cleanup
# jobs cannot prune buildcache artifacts behind the Spack mirror.
spack mirror add --autopush --unsigned --oci-username-variable REGISTRY_USER --oci-password-variable REGISTRY_TOKEN simphony-spack-buildcache "${SPACK_BUILDCACHE_MIRROR}"
spack external find --not-buildable --path /usr/local/cuda cuda
# Print out concretized specs
spack spec --fresh -Il simphony ^geant4@${GEANT4_VERSION} ^optix-dev@${OPTIX_VERSION}
# Solve fresh against current package metadata, but still install exact matches
# from the mirror. Autopush dependencies as soon as source builds complete, so
# partial progress survives a failed long-running buildcache refresh.
spack install --only=dependencies --fresh --use-buildcache auto simphony ^geant4@${GEANT4_VERSION} ^optix-dev@${OPTIX_VERSION}
# Update the index after dependency autopush so partial progress is immediately discoverable.
spack buildcache update-index simphony-spack-buildcache
# Keep the cache focused on dependencies, then build simphony itself from source.
spack mirror set --no-autopush simphony-spack-buildcache
spack install --fresh --use-buildcache package:never,dependencies:auto simphony ^geant4@${GEANT4_VERSION} ^optix-dev@${OPTIX_VERSION}
spack find --format "{name}{@version} {/hash}" simphony
prefix="$(spack location -i simphony)"
echo "Installed prefix: $prefix"
test -d "$prefix"
(test -f "$prefix/.spack/spec.json" || test -f "$prefix/.spack/spec.yaml")
# Ensure the index is current after the full run as well.
spack buildcache update-index simphony-spack-buildcache
'
- name: Verify dependency buildcache
run: |
docker run --rm \
'${{ env.SPACK_BASE_IMAGE }}' \
bash -lc '
set -euo pipefail
# Match the publish step by floating the builtin spack-packages repo while keeping
# the Spack tool itself pinned in spack-base.
spack repo update -b develop builtin
# Fetch the latest simphony package metadata from the external repo as well.
spack repo add https://github.com/BNLNPPS/spack-packages
spack mirror add --unsigned simphony-spack-buildcache "${SPACK_BUILDCACHE_MIRROR}"
spack external find --not-buildable --path /usr/local/cuda cuda
spack install --only=dependencies --fresh --use-buildcache only simphony ^geant4@${GEANT4_VERSION} ^optix-dev@${OPTIX_VERSION}
'
- name: Cleanup local base image
if: ${{ always() }}
run: |
docker image rm -f '${{ env.SPACK_BASE_IMAGE }}' || true
cleanup-buildcache-registry:
if: ${{ always() && needs.refresh-buildcache.result != 'skipped' }}
needs: refresh-buildcache
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Define cleanup targets
run: |
PACKAGE_NAME=$(echo "${{ github.event.repository.name }}-spack-buildcache" | tr '[:upper:]' '[:lower:]')
echo "PACKAGE_NAME=${PACKAGE_NAME}" >> "$GITHUB_ENV"
- name: Cleanup stale untagged GHCR versions
uses: dataaxiom/ghcr-cleanup-action@v1
with:
packages: ${{ env.PACKAGE_NAME }}
# Keep a small buffer so the cleanup pass does not race just-published manifests.
delete-untagged: true
older-than: 1 day