diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index ce15387d08c..8114d401444 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -62,8 +62,8 @@ jobs: - name: Build and push uses: docker/build-push-action@v6 with: - target: base - context: docker/ + file: docker/Dockerfile + context: . push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} @@ -123,6 +123,8 @@ jobs: with: path: cesm/cime submodules: "true" + - name: Install uv + uses: astral-sh/setup-uv@v7 - name: Run tests shell: bash working-directory: cesm/cime @@ -132,18 +134,15 @@ jobs: CIME_TEST_PLATFORM: "ubuntu-latest" SKIP_ENTRYPOINT: "true" run: | - source /opt/conda/etc/profile.d/conda.sh - - conda activate cesm - - mamba install -y 'python=${{ matrix.python-version }}' - - pip install -r test-requirements.txt + # manually run the entrypoint + source /entrypoint.sh - # GitHub runner home is different than container - cp -rf /home/cime/.cime /github/home/ + uv python install "${{ matrix.python-version }}" + uv venv + source .venv/bin/activate + uv pip install -r test-requirements.txt - pytest -vvv --cov=CIME --cov-branch --cov-report=xml --machine docker CIME/tests/test_unit* + uv run pytest -vvv --cov=CIME --cov-branch --cov-report=xml --machine docker CIME/tests/test_unit* - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v4 with: @@ -214,6 +213,10 @@ jobs: with: path: /home/cime/inputdata key: inputdata-2 + - name: Install uv + uses: astral-sh/setup-uv@v7 + with: + python-version: "3.10" - name: Run tests shell: bash working-directory: ${{ matrix.model.name }}/cime @@ -223,31 +226,18 @@ jobs: CIME_TEST_PLATFORM: ubuntu-latest SKIP_ENTRYPOINT: "true" run: | - source /opt/conda/etc/profile.d/conda.sh - - conda activate ${{ matrix.model.name }} - - pip install -r test-requirements.txt - + # manually run the entrypoint source /entrypoint.sh - if [[ "${{ matrix.model.name }}" == "e3sm" ]]; then - fix_mct_makefiles ../externals/mct - fi - - # GitHub runner home is different than container - cp -rf /home/cime/.cime /github/home/ - - if [[ "${CIME_MODEL}" == "e3sm" ]]; then - ln -sf /github/home/.cime/config_machines.v2.xml /github/home/.cime/config_machines.xml - else - ln -sf /github/home/.cime/config_machines.v3.xml /github/home/.cime/config_machines.xml - fi + uv python install 3.10 + uv venv + source .venv/bin/activate + uv pip install -r test-requirements.txt git config --global user.name "${{ github.actor }}" git config --global user.email "${{ github.actor }}@users.noreply.github.com" - pytest -vvv --cov=CIME --cov-branch --cov-report=xml --machine docker --no-fortran-run --no-teardown CIME/tests/test_sys* + uv run pytest -vvv --cov=CIME --cov-branch --cov-report=xml --machine docker --no-fortran-run --no-teardown CIME/tests/test_sys* - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v4 with: @@ -259,7 +249,7 @@ jobs: - name: Create testing log archive if: ${{ failure() }} shell: bash - run: tar -czvf /testing-logs-${GITHUB_RUN_NUMBER}-${{ matrix.model.name }}-${{ matrix.driver }}.tar.gz /home/cime/cases /home/cime/baselines /home/cime/archive + run: tar -czvf /testing-logs-${GITHUB_RUN_NUMBER}-${{ matrix.model.name }}-${{ matrix.driver }}.tar.gz /root/storage/cases /root/storage/baselines /root/storage/archive # How to download artifacts: # https://docs.github.com/en/actions/managing-workflow-runs/downloading-workflow-artifacts - name: Upload testing logs diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a70f247e9d4..88207281515 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -20,11 +20,13 @@ For more information on contributing to open source projects, is a great starting point. Also, checkout the [Zen of Scientific Software Maintenance](https://jrleeman.github.io/ScientificSoftwareMaintenance/) for some guiding principles on how to create high quality scientific software contributions. +The canonical, detailed contributing guide for this repository is included in the source tree at `doc/source/contributing-guide.rst`; please consult that file as the single source of truth for developer workflows, testing, and container usage. + ## Getting Started Interested in helping extend CIME? Have code from your research that you believe others will -find useful? Have a few minutes to tackle an issue? In this guide we will get you setup and -integrated into contributing to CIME! +find useful? Have a few minutes to tackle an issue? This guide will get you set up to contribute +to CIME. ## What Can I Do? * Tackle any unassigned [issues](https://github.com/ESMCI/CIME/issues) you wish! @@ -44,7 +46,7 @@ The goal is to maintain a diverse community that's pleasant for everyone. include multiple bug fixes in a single pull request, but they should be related. For unrelated changes, please submit multiple pull requests. * Do not commit changes to files that are irrelevant to your feature or bugfix - (eg: .gitignore). + (e.g., .gitignore). * Be willing to accept constructive criticism as part of issuing a pull request, since the CIME developers are dedicated to ensuring that new features extend the system robustly and do not introduce new bugs. @@ -53,7 +55,7 @@ The goal is to maintain a diverse community that's pleasant for everyone. ## Reporting a bug When creating a new issue, please be as specific as possible. Include the version -of the code you were using, as well as what operating system you are running. +of the code you were using, as well as what operating system you are running. Include the commit SHA or tag (e.g., output of `git rev-parse HEAD`) and the branch name. If possible, include complete, minimal example code that reproduces the problem. ## Pull Requests @@ -62,34 +64,18 @@ We love pull requests from everyone. Fork, then clone the repo: git clone git@github.com:your-username/CIME.git -Additionally you may need to checkout the submodules with: +You will need to initialize and update submodules: cd CIME - git submodule update --init - -You will need to install CIME dependencies and edit config files -to tell CIME about your development machine. See the [CIME users guide](https://esmci.github.io/cime/users_guide/porting-cime.html) - -Run the scripts_regression_tests: - - cd CIME/tests - python scripts_regression_tests.py - -Alternatively with `pytest`: - - pytest CIME/tests - -Make your change. Add tests for your change. Make the tests pass to the same level as before your changes. + git submodule update --init --recursive - cd CIME/tests - python scripts_regression_tests.py +From here you can edit the code and run the unit tests following this [guide](https://esmci.github.io/cime/versions/master/html/contributing-guide.html#pytest). -Run [pre-commit](https://pre-commit.com/#usage) before committing changes and submitting a PR. +When running the ``unit`` tests you can specify any valid machine e.g. docker and the tests will run. - pip install pre-commit - pre-commit run -a +If you need to run the ``system`` tests you will need to have your respective model checked out and on a supported machine. Alternatively you can use CIME [container](https://esmci.github.io/cime/versions/master/html/contributing-guide.html#docker-container) which is used in our GitHub CI testing. -Commit the changes you made. Chris Beams has written a [guide](https://chris.beams.io/posts/git-commit/) on how to write good commit messages. +Before creating your PR you will need to run the code quality checkers; see this [guide](https://esmci.github.io/cime/versions/master/html/contributing-guide.html#code-quality). Push to your fork and [submit a pull request][pr]. @@ -102,8 +88,9 @@ We may suggest some changes or improvements or alternatives. Some things that will increase the chance that your pull request is accepted: * Write tests. -* Follow [PEP8][pep8] for style. (The `flake8` utility can help with this.) -* Write a [good commit message][commit]. +* Write documentation. +* Follow the [Code Quality guide](https://esmci.github.io/cime/versions/master/html/contributing-guide.html#code-quality) +* Write a good commit message, we recommend using [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) Pull requests will automatically have tests run by a Github Action. This includes running both the unit tests as well as `pre-commit`, which checks diff --git a/doc/source/contributing-guide.rst b/doc/source/contributing-guide.rst index c479ed864ca..5fbb9cb4f78 100644 --- a/doc/source/contributing-guide.rst +++ b/doc/source/contributing-guide.rst @@ -21,9 +21,9 @@ The `Case` class includes an array of the Case env classes. In the `configure` f Testing ------- -CIME splits it's testing into two categories `unit` and `sys`. +CIME splits its tests into two categories: `unit` and `sys`. -The `unit` category covers both doctests and unit tests. While the `sys` category covers regression tests. The tests are named accordingly e.g. `unit` tests are found as `CIME/tests/test_unit*`. +The `unit` category covers doctests and unit tests, while the `sys` category covers regression tests. Tests are named accordingly (e.g., unit tests: `CIME/tests/test_unit*`). How to run the tests ``````````````````````` @@ -41,13 +41,12 @@ To get started install `pytest` and `pytest-cov`. .. code-block:: bash + pip install -r test-requirements.txt pip install pytest pytest-cov - # or - # conda install -c conda-forge pytest pytest-cov Examples ........ -Runing all the ``sys`` and ``unit`` tests. +Running all the ``sys`` and ``unit`` tests. .. code-block:: bash @@ -59,13 +58,13 @@ Running only ``sys`` tests, ``sys`` can be replaced with ``unit`` to run only un pytest CIME/tests/test_sys.* -Runnig a specific test case. +Running a specific test case. .. code-block:: bash pytest CIME/tests/test_unit_case.py -A specific test can be ran with the followin. +A specific test can be run with the following. .. code-block:: bash @@ -80,7 +79,7 @@ You can pass either the module name or the file path of a test. Examples ........ -Runing all the ``sys`` and ``unit`` tests. +Running all the ``sys`` and ``unit`` tests. .. code-block:: bash @@ -98,7 +97,7 @@ Runnig a specific test case. python CIME/tests/scripts_regression_tests.py CIME.tests.test_unit_case -A specific test can be ran with the followin. +A specific test can be run with the following. .. code-block:: bash @@ -117,9 +116,7 @@ Installing pre-commit .. code-block:: bash - pip install pre_commit - # or - # conda install -c conda-forge pre_commit + pip install pre-commit Running pre-commit `````````````````` @@ -138,127 +135,53 @@ If you install these scripts then `pre-commit` will automatically run on `git co Docker container ---------------- -GitHub actions runs all CIME's tests in containers. The dockerfile can be found under the `docker/` directory. - -You can skip building the container and use the same container from the GitHub actions using the following commands. This will pull the latest [image](https://hub.docker.com/r/jasonb87/cime/tags), see the available [run modifiers](#running-the-container) to customize the container. - -The current container supports the ``GNU`` compiler and ``OpenMPI`` library. - -Running -``````` -The default environment is similar to the one used by GitHub Actions. It will clone CIME into `/src/cime`, set `CIME_MODEL=cesm` and run CESM's `checkout_externals`. This will create a minimum base environment to run both unit and system tests. - -The `CIME_MODEL` environment vairable will change the environment that is created. +CIME provides a container that the CI uses to run all the testing. This container -Setting it to `E3SM` will clone E3SM into `/src/E3SM`, checkout the submodules and update the CIME repository using `CIME_REPO` and `CIME_BRANCH`. +can also be used to test locally providing a reproducible environment. The -Setting it to `CESM` will clone CESM into `/src/CESM`, run `checkout_externals` and update the CIME repository using `CIME_REPO` and `CIME_BRANCH`. +compiler is ``GNU`` and the MPI implementation is ``OpenMPI``. -The container can further be modified using the environment variables defined below. +The image can be pulled from ``ghcr.io``. .. code-block:: bash - docker run -it --name cime --hostname docker cime:latest bash - - -.. code-block:: bash - - docker run -it --name cime --hostname docker -e CIME_MODEL=e3sm cime:latest bash - -.. note:: - - It's recommended when running the container to pass `--hostname docker` as it will match the custom machine defined in `config_machines.xml`. If this is omitted, `--machine docker` must be passed to CIME commands in order to use the correct machine definition. - -Environment variables -::::::::::::::::::::: + docker pull ghcr.io/esmci/cime:latest -Environment variables to modify the container environment. - -| Name | Description | Default | -| ---- | ----------- | ------- | -| INIT | Set to false to skip init | true | -| GIT_SHALLOW | Performs shallow checkouts, to save time | false | -| UPDATE_CIME | Setting this will cause the CIME repository to be updated using `CIME_REPO` and `CIME_BRANCH` | "false" | -| CIME_MODEL | Setting this will change which environment is loaded | | -| CIME_REPO | CIME repository URL | https://github.com/ESMCI/cime | -| CIME_BRANCH | CIME branch that will be cloned | master | -| E3SM_REPO | E3SM repository URL | https://github.com/E3SM-Project/E3SM | -| E3SM_BRANCH | E3SM branch that will be cloned | master | -| CESM_REPO | CESM repository URL | https://github.com/ESCOMP/CESM | -| CESM_BRANCH | CESM branch that will be cloned | master | - -Examples -:::::::: -.. code-block:: bash - - docker run -it -e INIT=false cime:latest bash +or can be built locally. The build context needs to be set to the root of the CIME repository. .. code-block:: bash - - docker run -it -e CIME_REPO=https://github.com/user/cime -e CIME_BRANCH=updates_xyz cime:latest bash -Persisting data -::::::::::::::: + docker build -t ghcr.io/esmci/cime:latest -f docker/Dockerfile . -The `config_machines.xml` definition as been setup to provided persistance for inputdata, cases, archives and tools. The following paths can be mounted as volumes to provide persistance. - -* /storage/inputdata -* /storage/cases -* /storage/archives -* /storage/tools +Running +``````` +The container does not provide any source, as such you will need to bind +mount the model+cime directory and define which model is being used. The +following example assumes the model is checked out in ``$SRC_PATH``. .. code-block:: bash - docker run -it -v ${PWD}/data-cache:/storage/inputdata cime:latest bash + docker run -it --rm --hostname docker -e CIME_MODEL=e3sm -v ${SRC_PATH}:/root/model -v ./storage:/root/storage -v ./inputdata:/root/inputdata -w /root/E3SM/cime ghcr.io/esmci/cime:latest bash -It's also possible to persist the source git repositories. +This example will drop into a shell where CIME commands or tests can be run. +The options are broken down below. -.. code-block:: bash - - docker run -it -v ${PWD}/src:/src cime:latest bash +- ``--hostname docker`` is required to tell CIME which machine definition to use. +- ``-e CIME_MODEL=e3sm`` defines the model. +- ``-v ${SRC_PATH}:/root/E3SM`` passes through the model source. +- ``-v ./inputdata:/root/inputdata`` persistent input data. +- ``-v ./storage:/root/storage`` persistent cases, baselines, timings, etc. +- ``-w /root/E3SM/cime`` set the current working directory to CIME's root. +- ``ghcr.io/esmci/cime:latest`` container image. +- ``bash`` the command to run in the container. -Local git respositories can be mounted as well. +You can even run CIME or testing without a shell. .. code-block:: bash - docker run -v ${PWD}:/src/cime cime:latest bash - - docker run -v ${PWD}:/src/E3SM cime:latest bash - -Building -```````` -The container provides 3 targets. - -* base - Base image with no batch system. -* slurm - Slurm batch system with configuration and single queue. -* pbs - PBS batch system with configuration and single queue. + docker run -it --rm --hostname docker -e CIME_MODEL=e3sm -v ${SRC_PATH}:/root/model -v ./storage:/root/storage -w /root/E3SM/cime ghcr.io/esmci/cime:latest pytest CIME/tests/test_unit* .. code-block:: bash - - docker build -t ghcr.io/ESMCI/cime:latest --target docker/ -Customizing -::::::::::: -When building the container some features can be customized. Multiple `--build-arg` arguments can be passed. + docker run -it --rm --hostname docker -e CIME_MODEL=e3sm -v ${SRC_PATH}:/root/model -v ./storage:/root/storage -w /root/E3SM/cime ghcr.io/esmci/cime:latest ./scripts/create_test SMS.f19_g16.S -.. code-block:: bash - - docker build -t ghcr.io/ESMCI/cime:latest --build-arg {name}={value} docker/ - -+------------------------+-----------------------------------------------+---------+ -| Argument | Description | Default | -+========================+===============================================+=========+ -| MAMBAFORGE_VERSION | Version of the condaforge/mambaforge image | 4.11.0-0| -| | used as a base | | -+------------------------+-----------------------------------------------+---------+ -| PNETCDF_VERSION | Parallel NetCDF version to build | 1.12.1 | -+------------------------+-----------------------------------------------+---------+ -| LIBNETCDF_VERSION | Version of libnetcdf, the default will | 4.8.1 | -| | install the latest | | -+------------------------+-----------------------------------------------+---------+ -| NETCDF_FORTRAN_VERSION | Version of netcdf-fortran, the default will | 4.5.4 | -| | install the latest | | -+------------------------+-----------------------------------------------+---------+ -| ESMF_VERSION | Version of ESMF, the default will install the | 8.2.0 | -| | latest | | -+------------------------+-----------------------------------------------+---------+ diff --git a/docker/.cime/config_machines.v2.xml b/docker/.cime/config_machines.v2.xml index 73b7ea56fb2..b36a660488a 100644 --- a/docker/.cime/config_machines.v2.xml +++ b/docker/.cime/config_machines.v2.xml @@ -9,14 +9,14 @@ gnu,gnuX openmpi CIME - /home/cime/timings + /root/storage/timings CIME - /home/cime/cases - /home/cime/inputdata - /home/cime/inputdata-clmforc - /home/cime/archive/$CASE - /home/cime/baselines/$COMPILER - /home/cime/tools/cprnc + /root/storage/cases + /root/inputdata + /root/inputdata-clmforc + /root/storage/archive/$CASE + /root/storage/baselines/$COMPILER + /root/storage/tools/cprnc make 4 e3sm_developer @@ -37,11 +37,12 @@ 1 1 - /opt/conda/envs/e3sm - FALSE - /opt/conda/envs/e3sm - /opt/conda/envs/e3sm - /opt/conda/envs/e3sm + /opt/spack-envs/view + /opt/spack-envs/view + /opt/spack-envs/view + /opt/spack-envs/view/bin:$ENV{PATH} + /opt/spack-envs/view/lib + /opt/spack-envs/view/lib/pkgconfig diff --git a/docker/.cime/docker.cmake b/docker/.cime/docker.cmake index f8439e5e9a2..9251b999c2c 100644 --- a/docker/.cime/docker.cmake +++ b/docker/.cime/docker.cmake @@ -3,18 +3,14 @@ if (COMP_NAME STREQUAL gptl) string(APPEND CPPDEFS " -DHAVE_NANOTIME -DBIT64 -DHAVE_SLASHPROC -DHAVE_GETTIMEOFDAY") endif() -string(APPEND CMAKE_C_FLAGS_RELEASE " -O2 -g") -string(APPEND CMAKE_Fortran_FLAGS_RELEASE " -O2 -g") -string(APPEND CMAKE_CXX_FLAGS_RELEASE " -O2 -g") - -string(APPEND CMAKE_C_FLAGS_DEBUG " -O0") -string(APPEND CMAKE_Fortran_FLAGS_DEBUG " -O0") -string(APPEND CMAKE_CXX_FLAGS_DEBUG " -O0") +string(APPEND CMAKE_C_FLAGS " -I/opt/spack-envs/view/include -O1 -g -fno-fast-math -frounding-math -fsignaling-nans -fno-inline -fno-aggressive-loop-optimizations") +string(APPEND CMAKE_Fortran_FLAGS " -I/opt/spack-envs/view/include -O1 -g -fno-fast-math -frounding-math -fsignaling-nans -fno-inline -fno-aggressive-loop-optimizations -ffpe-trap=invalid,zero,overflow") +string(APPEND CMAKE_CXX_FLAGS " -I/opt/spack-envs/view/include") # required for grid generation tests that use make if (CMAKE_SOURCE_DIR MATCHES "^.*TestGridGeneration.*$") - string(APPEND FFLAGS " -I/opt/conda/envs/$ENV{CIME_MODEL}/include") - string(APPEND SLIBS " -L/opt/conda/envs/$ENV{CIME_MODEL} -lnetcdf -lnetcdff") + string(APPEND FFLAGS " -I/opt/spack-envs/view/include") + string(APPEND SLIBS " -L/opt/spack-envs/view/lib -lnetcdf -lnetcdff") endif() # DEBUGGING variables diff --git a/docker/.cime/docker/config_machines.xml b/docker/.cime/docker/config_machines.xml index 79fb6f4898d..3aee97ec316 100644 --- a/docker/.cime/docker/config_machines.xml +++ b/docker/.cime/docker/config_machines.xml @@ -8,14 +8,14 @@ gnu,gnuX openmpi CIME - /home/cime/timings + /root/storage/timings CIME - /home/cime/cases - /home/cime/inputdata - /home/cime/inputdata-clmforc - /home/cime/archive/$CASE - /home/cime/baselines/$COMPILER - /home/cime/tools/cprnc + /root/storage/cases + /root/inputdata + /root/inputdata-clmforc + /root/storage/archive/$CASE + /root/storage/baselines/$COMPILER + /root/storage/tools/cprnc make 4 e3sm_developer @@ -36,11 +36,12 @@ 1 1 - /opt/conda/envs/cesm - FALSE - /opt/conda/envs/cesm - /opt/conda/envs/cesm - /opt/conda/envs/cesm + /opt/spack-envs/view + /opt/spack-envs/view + /opt/spack-envs/view + /opt/spack-envs/view/bin:$ENV{PATH} + /opt/spack-envs/view/lib + /opt/spack-envs/view/lib/pkgconfig diff --git a/docker/Dockerfile b/docker/Dockerfile index b9362775bcf..3d34a883b40 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,96 +1,49 @@ -ARG BASE_TAG=latest -FROM condaforge/miniforge3:${BASE_TAG} AS base - -WORKDIR /home/cime - -RUN mkdir -p \ - /home/cime/inputdata/cpl/gridmaps/oQU240 \ - /home/cime/inputdata/share/domains \ - /home/cime/timings \ - /home/cime/cases \ - /home/cime/archive \ - /home/cime/baselines \ - /home/cime/tools \ - && wget -O /home/cime/inputdata/cpl/gridmaps/oQU240/map_oQU240_to_ne4np4_aave.160614.nc \ - https://portal.nersc.gov/project/e3sm/inputdata/cpl/gridmaps/oQU240/map_oQU240_to_ne4np4_aave.160614.nc \ - && wget -O /home/cime/inputdata/share/domains/domain.ocn.ne4np4_oQU240.160614.nc \ - https://portal.nersc.gov/project/e3sm/inputdata/share/domains/domain.ocn.ne4np4_oQU240.160614.nc \ - && wget -O /home/cime/inputdata/share/domains/domain.lnd.ne4np4_oQU240.160614.nc \ - https://portal.nersc.gov/project/e3sm/inputdata/share/domains/domain.lnd.ne4np4_oQU240.160614.nc - -COPY cime.yaml cime.yaml - -RUN apt-get update && \ - DEBIAN_FRONTEND=noninteractive \ - apt-get install -y --no-install-recommends \ - perl libxml-libxml-perl && \ - rm -rf /var/lib/apt/lists/* - -RUN mamba install -y gosu tree - -RUN mamba create -n e3sm -y --file cime.yaml python=3.10 'libnetcdf=4.9.1=*openmpi*' \ - && conda remove -n e3sm --force perl \ - && conda clean -afy \ - && rm -rf /opt/conda/pkgs/* - -RUN mamba create -n cesm -y --file cime.yaml python=3.10 'libnetcdf>=4.9.2=*openmpi*' \ - && conda remove -n cesm --force perl \ - && conda clean -afy \ - && rm -rf /opt/conda/pkgs/* - -COPY .cime .cime -COPY entrypoint.sh /entrypoint.sh - -ENTRYPOINT [ "/entrypoint.sh" ] - -FROM base AS slurm - -RUN apt-get update && \ - DEBIAN_FRONTEND=noninteractive \ - apt-get install -y --no-install-recommends \ - munge slurmd slurm-client slurmctld && \ - rm -rf /var/lib/apt/lists/* && \ - sed -i"" "s/\(.*\)[^<]*\(<\/BATCH_SYSTEM>\)/\1slurm\2/g" ~/.cime/config_machines.xml - -COPY slurm/slurm.conf /etc/slurm-llnl/ -COPY slurm/config_batch.xml /root/.cime/ -COPY slurm/entrypoint_batch.sh /entrypoint_batch.sh - -FROM base AS pbs - -RUN apt-get update && \ - DEBIAN_FRONTEND=noninteractive \ - apt-get install -y --no-install-recommends \ - curl ca-certificates software-properties-common \ - gcc make libtool libhwloc-dev libx11-dev libxt-dev libedit-dev \ - libical-dev ncurses-dev python-dev tcl-dev tk-dev swig libexpat-dev libssl-dev \ - libxext-dev libxft-dev autoconf automake \ - postgresql-12 postgresql-server-dev-all postgresql-contrib \ - expat libedit2 python3 sendmail-bin sudo tcl tk && \ - add-apt-repository ppa:deadsnakes/ppa && \ - apt-get update && \ - DEBIAN_FRONTEND=noninteractive \ - apt-get install -y python3.7 python3.7-dev && \ - rm -rf /var/lib/apt/lists/* - -RUN mkdir /src && pushd /src && \ - curl -LO https://github.com/openpbs/openpbs/archive/refs/tags/v20.0.1.tar.gz && \ - tar -xvf v20.0.1.tar.gz && \ - cd openpbs-20.0.1 && \ - sed -i"" 's/\(#include "list_link.h"\)/\1\n#include /' /src/openpbs-20.0.1/src/lib/Libifl/list_link.c && \ - export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin && \ - ./autogen.sh && \ - PYTHON=/usr/bin/python3.7 \ - CFLAGS="`/usr/bin/python3.7m-config --cflags`" \ - LDFLAGS="`/usr/bin/python3.7m-config --ldflags`" \ - LIBS="-lpthread -lm -lpython3.7m" \ - ./configure --prefix=/opt/pbs && \ - make -j8 && \ - make install && \ - popd && \ - rm -rf /src && \ - sed -i"" "s/\(.*\)[^<]*\(<\/BATCH_SYSTEM>\)/\1pbs\2/g" ~/.cime/config_machines.xml - -COPY pbs/pbs.conf /etc/ -COPY pbs/config_batch.xml /root/.cime/ -COPY pbs/entrypoint_batch.sh /entrypoint_batch.sh +FROM ghcr.io/spack/ubuntu-noble:1.0.4 AS builder + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + build-essential \ + cmake \ + libopenblas-openmp-dev \ + libopenblas0-openmp \ + libxml-libxml-perl \ + libxml2-utils \ + openmpi-bin \ + libopenmpi-dev \ + perl \ + pkg-config \ + python3-dev \ + subversion \ + wget \ + && rm -rf /var/lib/apt/lists/* \ + && mkdir -p /opt/spack-envs + +COPY docker/spack.yaml /opt/spack-envs/spack.yaml + +RUN spack compiler find \ + && spack external find --not-buildable \ + && spack external find --not-buildable openmpi \ + && spack -e /opt/spack-envs install --fail-fast + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + vim \ + neovim \ + && rm -rf /var/lib/apt/lists/* + +COPY test-requirements.txt /requirements.txt + +RUN curl -LsSf https://astral.sh/uv/install.sh | sh \ + && source ~/.local/bin/env \ + && uv python install 3.10 \ + && uv venv \ + && source .venv/bin/activate \ + && uv pip install -r /requirements.txt + +COPY docker/entrypoint.sh /entrypoint.sh +COPY docker/.cime /root/.cime + +RUN SKIP_ENTRYPOINT=true source /entrypoint.sh \ + && download_input_data + +ENTRYPOINT ["/entrypoint.sh"] diff --git a/docker/cime.yaml b/docker/cime.yaml deleted file mode 100644 index 52d58039fc1..00000000000 --- a/docker/cime.yaml +++ /dev/null @@ -1,40 +0,0 @@ -name: base -channels: - - conda-forge -dependencies: -# development tools - - cmake<4.0 # some libraries still require 3.5.x - - make - - wget - - curl - - subversion - - m4 - - pkg-config - - vim - - rsync - - openssh - - tree -# python packages - - pytest - - pytest-cov - - pyyaml -# libraries - - lapack - - blas - # Testing hdf5 is out of scope for CIME - # - hdf5<1.14.0=*openmpi* - - netcdf-fortran=*=*openmpi* - - libpnetcdf=*=*openmpi* - - esmf=*=*openmpi* - - openmpi - - gcc_linux-64 - - gxx_linux-64 - - gfortran_linux-64 - - gcc=12.* - - gxx=12.* - - gfortran=12.* - - compilers - - c-compiler - - cxx-compiler - - fortran-compiler -prefix: /opt/conda diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh index 5e78a7c5ccf..194538c8e7f 100755 --- a/docker/entrypoint.sh +++ b/docker/entrypoint.sh @@ -1,58 +1,91 @@ #!/bin/bash -export USER=root -export LOGNAME=root -export USER_ID=${USER_ID:-1000} -export GROUP_ID=${GROUP_ID:-1000} +# Set up basic user, logname, and default group/user IDs +export USER="$(id -nu)" +export LOGNAME="${USER}" +# Set static home path where .cime exists and container entrypoint options SKIP_ENTRYPOINT="${SKIP_ENTRYPOINT:-false}" -function fix_mct_makefiles() { - fix_arflags "${1}/mct/Makefile" - fix_arflags "${1}/mpeu/Makefile" - fix_arflags "${1}/mpi-serial/Makefile" +# Build the cprnc tool from CIME sources +function build_cprnc() { + cprnc_dir="${PWD}/CIME/non_py/cprnc" + + if [[ ! -e "${cprnc_dir}" ]]; then + echo "CPRNC path does not exist. Change to CIME's root directory." + exit 1 + fi + + pushd "$(mktemp -d)" || exit 1 + + cmake "${cprnc_dir}" + + make + + # Needs to be copied into the machines configured tool path + cp cprnc "${HOME}/tools/cprnc" + + popd || exit 1 } -function fix_arflags() { - if [[ ! -e "${1}.bak" ]]; then - echo "Fixing AR variable in ${1}" - sed -i".bak" "s/\$(AR)/\$(AR) \$(ARFLAGS)/g" "${1}" - fi +# Download input data needed for model setup +# required for grid generation tests +function download_input_data() { + local storage="${HOME}/storage" + + mkdir -p "${HOME}/inputdata/cpl/gridmaps/oQU240" + mkdir -p "${HOME}/inputdata/share/domains" + mkdir -p "${storage}/cases" + mkdir -p "${storage}/timings" + mkdir -p "${storage}/archive" + mkdir -p "${storage}/baselines" + mkdir -p "${storage}/tools" + + wget -O "${HOME}/inputdata/cpl/gridmaps/oQU240/map_oQU240_to_ne4np4_aave.160614.nc" \ + https://portal.nersc.gov/project/e3sm/inputdata/cpl/gridmaps/oQU240/map_oQU240_to_ne4np4_aave.160614.nc + + wget -O "${HOME}/inputdata/share/domains/domain.ocn.ne4np4_oQU240.160614.nc" \ + https://portal.nersc.gov/project/e3sm/inputdata/share/domains/domain.ocn.ne4np4_oQU240.160614.nc + + wget -O "${HOME}/inputdata/share/domains/domain.lnd.ne4np4_oQU240.160614.nc" \ + https://portal.nersc.gov/project/e3sm/inputdata/share/domains/domain.lnd.ne4np4_oQU240.160614.nc } -if [[ "${SKIP_ENTRYPOINT}" == "false" ]]; then + +# Link correct config_machines file based on CIME_MODEL, also set ESMFMKFILE for cesm +function link_config_machines() { if [[ "${CIME_MODEL}" == "e3sm" ]]; then - ln -sf /home/cime/.cime/config_machines.v2.xml /home/cime/.cime/config_machines.xml + ln -sf "${HOME}/.cime/config_machines.v2.xml" "${HOME}/.cime/config_machines.xml" elif [[ "${CIME_MODEL}" == "cesm" ]]; then export ESMFMKFILE=/opt/conda/envs/cesm/lib/esmf.mk - ln -sf /home/cime/.cime/config_machines.v3.xml /home/cime/.cime/config_machines.xml + ln -sf "${HOME}/.cime/config_machines.v3.xml" "${HOME}/.cime/config_machines.xml" fi +} - if [[ "${USER_ID}" == "0" ]]; then - cp -rf /home/cime/.cime /root/ - - git config --global --add safe.directory "*" - - exec "${@}" - else - groupmod -g "${GROUP_ID}" -n cime ubuntu - usermod -d /home/cime -u "${USER_ID}" -g "${GROUP_ID}" -l cime ubuntu +if [[ "${CI:-false}" == "true" ]]; then + cp -rf /root/.cime "${HOME}" +fi - chown -R cime:cime /home/cime +link_config_machines - if [[ -n "${SRC_PATH}" ]] && [[ -e "${SRC_PATH}" ]]; then - chown -R cime:cime "${SRC_PATH}" +# Allow git to operate in any directory, for container/dev scenarios +if [[ -e "${PWD}/.git" ]]; then + git config --global --add safe.directory "*" +fi - git config --global --add safe.directory "*" - fi +export PATH=/opt/spack-envs/view/bin:$PATH +export PKG_CONFIG_PATH=/opt/spack-envs/view/lib/pkgconfig +export LD_LIBRARY_PATH=/opt/spack-envs/view/lib +export ESMFMKFILE=/opt/spack-envs/view/lib/esmf.mk - { - echo "source /opt/conda/etc/profile.d/conda.sh" - echo "conda activate base" - } > /home/cime/.bashrc +if [[ "${CI:-false}" == "false" ]]; then + source ${HOME}/.local/bin/env + source ${HOME}/.venv/bin/activate +fi - gosu "${USER_ID}" "${@}" - fi +# If not skipping entrypoint, set up user/group IDs and exec given command. +if [[ "${SKIP_ENTRYPOINT}" == "false" ]]; then + exec "${@}" fi diff --git a/docker/spack.yaml b/docker/spack.yaml new file mode 100644 index 00000000000..a069663508b --- /dev/null +++ b/docker/spack.yaml @@ -0,0 +1,18 @@ +spack: + specs: + - hdf5@1.12.3+cxx+fortran+hl+mpi+shared %gcc@13.3.0 + - netcdf-c@4.9.2+mpi %gcc@13.3.0 + - netcdf-fortran@4.6.1 %gcc@13.3.0 + - parallel-netcdf@1.12.3 %gcc@13.3.0 + - esmf@8.8.1+mpi+netcdf~pnetcdf~external-parallelio %gcc@13.3.0 + concretizer: + unify: when_possible + reuse: true + targets: + granularity: generic + host_compatible: false + packages: + all: + target: [x86_64_v3] + view: + default: /opt/spack-envs/view diff --git a/test-requirements.txt b/test-requirements.txt index d4abdec4d0f..9d22f450869 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1 +1,3 @@ evv4esm +pytest +pytest-cov