Skip to content

feat(docker): migrate from Spack to Pixi for container dependency management - #5025

Merged
jgfouca merged 20 commits into
masterfrom
jasonb5/docker/pixi-migration
Aug 24, 2026
Merged

feat(docker): migrate from Spack to Pixi for container dependency management#5025
jgfouca merged 20 commits into
masterfrom
jasonb5/docker/pixi-migration

Conversation

@jasonb5

@jasonb5 jasonb5 commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Summary

Replace Spack with Pixi for CIME Docker dependency management, with separate E3SM and CESM environments. Update the container runtime configuration and CI workflow to build and test PR images locally, while publishing images from master.

Notable changes

  • Switch from Open MPI to MPICH.
  • Require CIME_MODEL=e3sm or CIME_MODEL=cesm.
  • Add CPU-aware PE configuration.
  • Remove the legacy Spack configuration.
  • Update Docker and contributor documentation.

Testing

  • Unit tests across supported Python versions.
  • E3SM and CESM system-test jobs.
  • Pre-commit and configuration validation checks.

@github-actions

Copy link
Copy Markdown
Contributor

jasonb5 added 16 commits August 13, 2026 12:18
- Add strict error handling with 'set -Eeuo pipefail'
- Validate CIME_MODEL must be 'e3sm' or 'cesm' (lowercase)
- Verify Pixi environment directory exists before activation
- Make /etc writes conditional for non-root/read-only container support
- Prepend to PKG_CONFIG_PATH and LD_LIBRARY_PATH instead of overwriting
- Initialize 'cores' variable to fix unbound variable error with set -u

These changes ensure the entrypoint fails fast with clear error messages
and supports running as non-root or in read-only mode.
- Pin Ubuntu 24.04 base image by digest (sha256:561618e2c...)
- Pin uv to version 0.12.3 with SHA256 checksum verification
- Fix tar extraction using --strip-components=1 to handle directory structure
- Replace unpinned astral.sh installer with direct GitHub release download

This ensures reproducible builds and validates dependencies against known
checksums before installation.
- Accurately document that all components concurrently execute on same ranks
- Update comment to reflect overlapping layout vs misleading 'one per component'
- Remove trailing whitespace from XML and documentation

The concurrent PE layout is valid for CIME and allows all components to
share the available MPI tasks, preventing oversubscription in containers.
Docker README updates:
- Clarify CIME_MODEL must be 'e3sm' or 'cesm' (lowercase)
- Add 'Required flags' section explaining --hostname, --shm-size, and -e
- Update all examples to include required --shm-size=1g flag
- Add 'Running without a shell' section with pytest and create_test examples

Contributing guide updates:
- Fix OpenMPI → MPICH reference and add pixi/conda-forge mention
- Add BuildKit requirement note
- Update all examples with --shm-size=1g and concrete 'e3sm' values
- Add --pesfile to create_test example
- Add troubleshooting section for common issues:
  * CIME_MODEL validation errors
  * Out of memory errors
  * Container core count mismatches
- Add reference to docker/README.md for complete documentation

All examples are now complete, accurate, and copy-pasteable.
Query configuration examples:
- Update mpilibs from 'openmpi' to 'mpich'
- Update compilers from 'gnu,gnuX' to 'gnu'
- Update environment variables to show Pixi paths
- Remove obsolete OpenMPI-specific variables
- Update core counts to realistic values (4 instead of 8)

Machine configuration examples:
- Update example XML to use mpich mpilib
- Change mpirun arguments from --oversubscribe to -bind-to none
- Update paths from /storage to /root/storage
- Use $ENV{DOCKER_MAX_TASKS} for dynamic task limits
- Update environment variables to use $ENV{PIXI_ENV_ROOT} and $ENV{CIME_MODEL}
- Remove OpenMPI-specific environment variables
- Update support contact

These examples now accurately reflect the current Pixi/MPICH-based
container implementation.
The entrypoint was sourcing /root/.local/bin/env which no longer exists
after the uv migration. The Dockerfile now installs uv directly to
/usr/local/bin and creates a venv at ~/.venv, so only the venv
activation is needed.

This fixes a critical bug that breaks normal container startup.
The examples at lines 124 and 133 were missing required flags:
- --hostname docker (required by NODENAME_REGEX validation)
- -e CIME_MODEL=e3sm (required by entrypoint environment setup)
- --shm-size=1g (required for MPI shared memory)

Without these flags, the entrypoint validation fails and containers
don't start properly.
Changed :ref: to backtick link syntax for the docker/README.md reference.
The :ref: directive is for internal Sphinx cross-references, not external
GitHub URLs. This fixes a Sphinx build error.
Excludes .git directory, untracked data/ files (~442 MB), build artifacts,
and development files from the Docker build context. This significantly
reduces build context size and speeds up image builds.
Pin pytest==8.4.2, pytest-cov==7.1.0, and evv4esm==0.6.2 to ensure
reproducible test environments across the CI Python matrix (3.9-3.14).

pytest 9.x requires Python 3.10+, so 8.4.2 is the latest version
compatible with Python 3.9.
Remove the gnuX compiler option from Docker machine XML files. Only
the gnu compiler is supported and tested in the container environment.

Also prepend Pixi library paths to LD_LIBRARY_PATH and PKG_CONFIG_PATH
instead of replacing them. This preserves any library paths set by
the caller or entrypoint while ensuring Pixi dependencies take
precedence.
Add explicit documentation that the container is Linux amd64 only and
runs as root with storage under /root/storage. Update all build
examples to include --platform linux/amd64 flag for clarity and
consistency.
Major changes:
- Build local Docker images for PRs, saved as compressed artifacts
- Convert test jobs from GitHub container jobs to regular runners using docker run
- Only publish to ghcr.io/esmci/cime:latest on pushes to master
- Remove QEMU setup (amd64-only builds)
- Expand path filters to include workflow config, dependencies, and .dockerignore

This ensures fork PRs receive full container test coverage without requiring
GHCR push permissions, and eliminates the stale-latest fallback behavior
that could mask test failures.
Add data/ to .gitignore to prevent untracked test data from being
committed to the repository. The .dockerignore already excludes this
directory from Docker build contexts.
- Fix entrypoint.sh CIME_MODEL check to handle unset var with set -u
- Make test jobs depend on successful build-pr-image result
- Add fork-safe condition to Codecov uploads (same-repo only)
- Fix tmate debug condition from !always() to failure() && !cancelled()
- Make tar archive robust with --ignore-failed-read for missing dirs
@jasonb5
jasonb5 force-pushed the jasonb5/docker/pixi-migration branch from 0926e9f to 333412c Compare August 13, 2026 20:28
GitHub Actions runners cannot reach portal.nersc.gov, causing Docker
image builds to fail during the RUN step that downloads input data.

Changes:
- Add DOWNLOAD_SKIP_ON_ERROR flag to make download failures non-fatal
  during Docker build (when NERSC is unreachable from CI)
- Add wget retry logic (--tries=5, --timeout=30, --waitretry=10)
- Skip downloading files that already exist (idempotent)
- Attempt to download missing files in background at container startup
- Add .download_complete marker to track successful downloads
- Clean up partial downloads on failure

The image will now build successfully in CI even when NERSC is
unreachable. Missing files will be retried at runtime when the
container starts (e.g., when run locally where NERSC is accessible).

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR migrates the project’s CI/development container from a Spack-based stack to Pixi/conda-forge, splitting dependencies into E3SM and CESM environments while updating container runtime behavior and GitHub Actions to build/test PR images locally and publish images from master.

Changes:

  • Replaces Spack with Pixi (with separate e3sm and cesm environments) and updates container entrypoint/runtime configuration (CPU-aware PE sizing, MPICH, UCX settings).
  • Refactors Docker image build to use pinned pixi and uv binaries with checksum verification; updates container docs and Sphinx docs accordingly.
  • Updates CI workflow to build a PR image, pass it to downstream jobs, and only publish latest on pushes.

Reviewed changes

Copilot reviewed 15 out of 17 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
test-requirements.txt Pins Python test tooling versions for reproducible CI/containers.
docker/spack.yaml Removes legacy Spack environment definition.
docker/README.md Updates container documentation for Pixi-based environments and new runtime expectations.
docker/pixi.toml Defines shared conda-forge dependency set plus E3SM/CESM feature envs.
docker/pixi.lock Locks full conda-forge resolution for reproducible container builds.
docker/entrypoint.sh Selects Pixi env by CIME_MODEL, computes cores for PE sizing, and manages runtime behaviors (inputdata, paths).
docker/Dockerfile Builds Pixi envs in-image, builds cprnc, installs uv + Python test env, and stages input data.
docker/.cime/docker/config_machines.xml Switches Docker machine config to MPICH and dynamic task sizing via DOCKER_MAX_TASKS.
docker/.cime/docker.cmake Points include/lib flags at the model-selected Pixi environment.
docker/.cime/config_pes.xml Adds a generic “cap-to-available-cores” PE layout for container runs.
docker/.cime/config_machines.v2.xml Updates v2 machine config to MPICH and dynamic core/task sizing.
doc/source/contributing-guide.rst Reworks contributing doc and updates container usage instructions to match new image behavior.
doc/source/ccs/query-configuration.rst Updates documentation examples for the Docker machine config changes.
doc/source/ccs/model-configuration/variables/machine.rst Updates example machine XML snippet to match MPICH + Pixi environment approach.
.gitignore Ignores local data and Pixi state directories.
.github/workflows/testing.yml Builds PR image as an artifact, runs tests by docker run, and publishes latest only on push.
.dockerignore Excludes VCS, artifacts, and local dev state from Docker build context.
Suppressed comments (3)

.github/workflows/testing.yml:221

  • Same as above: this docker load reads a gzipped stream from stdin without decompression, which can break the job. Decompress before loading.
      - name: Load Docker image
        run: |
          docker load < /tmp/cime-image.tar.gz

.github/workflows/testing.yml:93

  • These Docker actions are still on older major versions. To avoid breakage when GitHub Actions updates Node runtimes, bump setup-buildx-action and login-action to their current majors.
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
      - name: Login to GHCR
        uses: docker/login-action@v3

.github/workflows/testing.yml:100

  • docker/build-push-action is still on v6 here. Consider bumping to the current major (v7) for Node runtime compatibility with GitHub Actions.
      - name: Build and push
        uses: docker/build-push-action@v6
        with:

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread docker/README.md Outdated
Comment thread docker/entrypoint.sh Outdated
Comment thread docker/entrypoint.sh Outdated
Comment thread doc/source/contributing-guide.rst
Comment thread doc/source/contributing-guide.rst
Comment thread .github/workflows/testing.yml Outdated
Comment thread .github/workflows/testing.yml
- Remove obsolete reference to legacy/spack.yaml in docker/README.md
- Export PIXI_ENV_ROOT in entrypoint.sh for child process visibility
- Fix silent background download to redirect both stdout and stderr
- Remove trailing whitespace in contributing-guide.rst
- Restore contributing-guide-running-tests reference target for Sphinx
- Fix docker load commands to decompress gzipped tar before loading
- Docker action versions already at latest (v3/v6), no update needed
@jasonb5
jasonb5 requested review from billsacks and jgfouca August 20, 2026 23:21
@billsacks
billsacks requested review from samsrabin and removed request for billsacks August 20, 2026 23:27
@samsrabin

Copy link
Copy Markdown
Contributor

It seems like this PR does a few very different things. To help me in reviewing, could you please break it up into discrete PRs?

@jgfouca jgfouca left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM but can you explain the reasoning behind this change a bit more in the description?

@jgfouca

jgfouca commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Oh, this actually gets E3SM working again! Definitely would be nice to merge this ASAP.

@jgfouca jgfouca self-assigned this Aug 24, 2026
@jgfouca
jgfouca merged commit 38eab1d into master Aug 24, 2026
12 checks passed
@jgfouca
jgfouca deleted the jasonb5/docker/pixi-migration branch August 24, 2026 17:04
@jasonb5

jasonb5 commented Aug 25, 2026

Copy link
Copy Markdown
Collaborator Author

@samsrabin That’s fair. The changes touch several areas, but they’re all part of the same Spack-to-Pixi migration and are fairly dependent on each other. Splitting them up would leave some of the intermediate PRs incomplete or non-functional.

I can make that relationship clearer in the PR description and clean up the commits to make it easier to review.

@samsrabin

Copy link
Copy Markdown
Contributor

Ah okay, sorry, I didn't understand that—makes sense now!

Do you still need a review now that it's merged?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants