Polaris Version Update Testing
date: 2026/10/14
Contributors:
Xylar Asay-Davis
Claude
Summary
A pull request that updates Polaris to a new version, such as #664 or #835,
usually changes the Spack environments and often the submodules. Its
checklist asks for the MPAS-Ocean mpaso_pr suite, and for the Omega
CTests and omega_pr suite, on every supported machine, compiler and MPI
library. The checklist is run twice: first with test Spack environments,
then with the shared environments deployed for the new version.
This design adds utils/polaris_update_testing, a utility for that
testing. It reuses the code in utils/pr_testing that the Omega PR testing
utility shares (see Omega Pull Request Testing). The new parts are the rows,
the step that deploys Spack environments, and reports on the Polaris pull
request.
Success means that #835’s deployment can be tested and reported with the
utility, and that a reviewer can tick each box of its checklist from the
Testing comments alone.
Requirements
Requirement: Every supported row is tested.
The rows are every machine, compiler and MPI library that the new version supports, for MPAS-Ocean and for Omega.
Requirement: Each report says which Spack environments it tested.
A report says whether it tested test environments or the shared ones, and where they are.
Requirement: The requester controls deploying Spack environments.
Deploying shared environments changes what every Polaris user on a machine loads.
Requirement: A baseline is optional.
When there is one, it is the previous version, from Polaris main, with
its shared environments.
Requirement: Each machine’s results are reported in one comment.
Requirement: Progress across machines is visible.
The requester can see which rows have results with each kind of environment, which were not run and why, and which results are for a commit that is no longer the pull request’s head.
Requirement: Commits, provenance and permissions are handled as for Omega pull requests.
The requirements of Omega Pull Request Testing hold, except those about linting and the Omega pull request template: every machine tests the same commits, each report carries its own provenance, results are reported as Polaris and CTest write them, the requester controls every outward-facing action, testers do not tick the checklist, and a matching baseline is reused wherever it was run.
Algorithm Design
Algorithm Design: Every supported row is tested.
The rows come from docs/developers_guide/supported_machines.yaml at the
pull request’s head. Each row is a machine, compiler, MPI library and
model. An MPAS-Ocean row runs mpaso_pr. An Omega row runs the CTests and
omega_pr. A machine and compiler that support both models give two rows.
Omega is built from the pull request’s e3sm_submodules/Omega pin, in the
Omega worktrees the Omega utility makes. MPAS-Ocean is built from the
e3sm_submodules/E3SM-Project submodule of the Polaris checkout.
MPAS-Ocean builds inside its source tree, so the rows that share a tree
build one at a time (see Decisions).
Algorithm Design: Each report says which Spack environments it tested.
The environments are read from each row’s load script, not set by the
requester. A load script whose Spack environment is under the machine’s
[deploy] spack path uses the shared environments. Any other uses test
environments. The comment’s heading names the kind, and the report gives
the path, the Polaris version and the mache version, and says if mache
came from a branch rather than a release.
Rationale: The report states what was loaded, so it cannot claim the shared environments when a test deployment was sourced by mistake. #835 starts with the shared environments, since its test environments were already tested by hand.
Algorithm Design: The requester controls deploying Spack environments.
deploy prints the ./deploy.py --deploy-spack command for a machine. It
deploys the compilers and MPI libraries of the machine’s supported rows at
the pull request’s head. With a test path, it adds
--spack-path. With a mache fork and branch, for a mache that is not
released yet, it adds --mache-fork and --mache-branch. On Perlmutter, pm-cpu and pm-gpu each get their own
command. Only with --run does deploy run the command, under nohup,
with its output in a log. Afterward, deploy checks that each of the
machine’s rows has a load script in the checkout, and that the script
uses the environment just deployed.
Algorithm Design: A baseline is optional.
init --baseline main pins the current main of E3SM-Project/polaris as
the baseline. The baseline runs from a second Polaris checkout that
contains that commit and not the pull request’s head. It uses that
checkout’s load scripts, and so the previous version’s shared
environments. Omega and MPAS-Ocean come from that checkout’s submodule
pins.
A row whose compiler the pull request renames uses the former name for the baseline, as in the Omega utility. A row the previous version does not support runs without a baseline, and its report says so.
Rows that the current main cannot run, such as Aurora’s Omega rows after
mache 5.1.0 (#896), get their own manifest, with init --baseline <older commit> --rows .... A manifest whose baseline is not main adds the
baseline’s short hash to its test branch and run directory, so it does not
collide with the manifest for the other rows.
Baseline differences can come from the environments, from the submodule updates or from Polaris changes in the pull request. Each report names the commits on both sides, so the tester can tell which changed.
Algorithm Design: Each machine’s results are reported in one comment.
Each machine gets one comment for each kind of environment, on the Polaris pull request. Perlmutter’s pm-cpu and pm-gpu rows share one, as in the Omega utility. A table has a line for each row. Below it, each row has a collapsed block with the files Polaris and CTest wrote, pasted unchanged.
Algorithm Design: Progress across machines is visible.
Each comment’s marker, prefixed polaris-update-test, lists its rows and
the kind of environment. status lists each row once for each kind of
environment, with its latest result.
Algorithm Design: Commits, provenance and permissions are handled as for Omega pull requests.
init pins the pull request’s head, its Omega and E3SM-Project pins and
the rows in a manifest. The manifest is committed on top of the head, in a
branch on the requester’s Polaris fork. Testers fetch that branch. Their
Polaris checkout must contain the head, with this utility merged in
locally if the head does not have it.
The outward-facing steps are init --push, deploy --run,
setup --submit and post. Each needs the requester’s permission in the
agent’s session, as in the Omega utility. report only writes its comment,
and post posts it once someone has read it.
Implementation
Implementation: Every supported row is tested.
The shared Row gains an optional model, which the Omega utility leaves
out. pr_test_setup builds MPAS-Ocean as well as Omega:
polaris_suite()already takes the suite and model. For MPAS-Ocean it passes no--branch, so the build uses the checkout’s submodule.A lock on the E3SM-Project submodule, like the lock on an Omega worktree, makes rows build one at a time.
A finished MPAS-Ocean build is recognized by its
ocean_model.BaselineCriteria.build_typemay beNone, since MPAS-Ocean’s provenance has no build type.
Only Omega rows set up CTests and compare build warnings.
polaris_update_test.py init --pr 835 [--baseline main] [--rows ...]
[--merge-omega-pr <number>]...
[--merge-e3sm-pr <number>]...
[--notes <text>] [--push]
--merge-omega-pr merges an Omega pull request, such as a build fix still
in review, into the Omega the pull request pins. --merge-e3sm-pr merges an
E3SM pull request after any Omega ones, such as an update to E3SM master
that Omega has yet to merge. The merge is pushed to the requester’s E3SM
fork, recorded in the manifest’s omega_merge, and built by every Omega row
in place of the pin. Checkouts are still checked against the pin. MPAS-Ocean
rows build the E3SM the pull request pins.
The manifest, polaris_update_test.yaml:
schema_version: 1
requester: <GitHub user>
pull_request: 835
pr_head: <sha>
polaris_version: 1.2.0-alpha.1
omega_commit: <sha>
e3sm_commit: <sha>
omega_merge: # or null
commit: <sha> # built in place of omega_commit
pull_requests: [{pull_request: 600, commit: <sha>}]
e3sm_pull_requests: [{pull_request: 9000, commit: <sha>}]
fork: <the requester's E3SM fork>
branch: polaris-update-test/835-<sha7>-omega
baseline: # or null
polaris_commit: <sha>
polaris_version: 1.1.0-alpha.7
omega_commit: <sha>
e3sm_commit: <sha>
rows:
- {machine: chrysalis, compiler: gnu, mpi: openmpi, model: mpas-ocean}
- {machine: chrysalis, compiler: gnu, mpi: openmpi, model: omega}
notes: <free text for the requester>
Implementation: Each report says which Spack environments it tested.
setup reads the Spack environment from the loaded load script and
compares it with the machine’s [deploy] spack path. It records the kind
of environment, shared or test, with the path and the versions, in
the row’s setup.json. mache came from a branch if pip installed the
loaded version from a directory, as ./deploy.py --mache-fork does; a
conda package’s metadata names conda as its installer. Row directories are kept apart by kind:
<work_base>/polaris-pr<N>-<head sha7>/<shared|test>/
report_<machine>.md
<machine>_<compiler>_<model>/
build/ <suite>/ ctest/ setup.json
Implementation: The requester controls deploying Spack environments.
polaris_update_test.py deploy --fork <fork> --branch <branch>
[--machine <machine>] [--spack-path <path>]
[--recreate] [--mache-fork <org>/mache]
[--mache-branch <branch>] [--run]
The compilers and MPI libraries come from the rows in
supported_machines.yaml at the pull request’s head, all of them, even if
the manifest tests only some. AGENTS.md tells an agent to ask before
--run on each machine, and to say whether it deploys test or shared
environments. With permission, deploy --run deploys the shared
environments as well as test ones (see Decisions).
Implementation: A baseline is optional.
polaris_update_test.py setup --fork <fork> --branch <branch> [--submit]
[--baseline-load-script <script>]
[--no-baseline <reason>]
setup sets up every row the loaded load script tests. The baseline runs
from the checkout of main that the settings’ polaris_main names, with
its load script for the same compiler, or for the compiler’s name before a
rename, as in the Omega utility. --baseline-load-script names another. setup checks that the
checkout contains the manifest’s baseline commit and pins its Omega and
E3SM-Project commits. The baseline is found or set up with the shared
get_baseline(), so an Omega baseline that the Omega utility ran from the
same main is reused.
Implementation: Each machine’s results are reported in one comment.
polaris_update_test.py report [--machine <machine>] [--spack <kind>]
[--notes <file>] [--not-run <reason>]
[--agent <name>]
## Testing on chrysalis with the shared Spack environments
<!-- polaris-update-test {...} -->
Tested E3SM-Project/polaris#835 (`<sha>`), Polaris 1.2.0-alpha.1 with
mache 5.0.0, Omega `<sha>` and E3SM `<sha>`, with the shared Spack
environments in `<path>`. The baseline is `<sha>`, Polaris 1.1.0-alpha.7,
Omega `<sha>` and E3SM `<sha>`, with its shared Spack environments, so
differences can come from any of these.
| Row | Suite | CTests | New build warnings |
|---|---|---|---|
| chrysalis, gnu, openmpi (MPAS-Ocean) | `mpaso_pr`: 24 of 24 passed | | |
| chrysalis, intel, openmpi (Omega) | `omega_pr`: 29 of 29 passed | 59 of 59 passed | 0 |
| chrysalis, gnu, openmpi (Omega) | `omega_pr`: 29 of 29 passed | 59 of 59 passed | 0 |
<details>...</details>
The comment uses the shared format_rows(), assemble() and
deliver(), with E3SM-Project/polaris as the repository.
Implementation: Progress across machines is visible.
polaris_update_test.py status --pr 835 [--fork <fork> --branch <branch>]
status uses the shared get_row_entries() and format_status(). A row
is named <machine>/<compiler>/<model>/<shared|test>.
Implementation: Commits, provenance and permissions are handled as for Omega pull requests.
utils/polaris_update_testing/ holds AGENTS.md, README.md,
polaris_update_test.py and its polaris_update_*.py modules. AGENTS.md
links to the Omega utility’s AGENTS.md for the rules and the ssh steps
the two share, rather than repeating them. The settings come from
~/.config/polaris_pr_test.cfg, the Omega utility’s file, so the two
share work_base, its Omega worktrees and its baselines.
Testing
Testing and Validation: Every supported row is tested.
Unit tests read the rows from a fixture supported_machines.yaml, and set
up an MPAS-Ocean row with a stand-in for polaris suite. They check that
two MPAS-Ocean rows from one tree wait on its lock.
Testing and Validation: Each report says which Spack environments it tested.
Unit tests read fixture load scripts with a Spack environment under the shared path and under a test path.
Testing and Validation: The requester controls deploying Spack environments.
Unit tests build the deploy command from fixture machine configs,
including Perlmutter’s two and one with a mache branch, and check that
nothing runs without --run. Others tell a mache from a branch from
conda packages and pip installs of a release.
Testing and Validation: A baseline is optional.
Unit tests set up a row with and without a baseline, and with a baseline load script that uses a compiler’s former name.
Testing and Validation: Each machine’s results are reported in one comment.
Unit tests build a comment with MPAS-Ocean and Omega rows, and check that the files appear byte for byte.
Testing and Validation: Progress across machines is visible.
Unit tests parse markers for both kinds of environment.
Testing and Validation: Commits, provenance and permissions are handled as for Omega pull requests.
The trial is #835’s deployment, against a main baseline, on Chrysalis,
Perlmutter and Aurora over ssh and with an agent on Frontier.
Decisions
A separate utility that shares code with the Omega one. An update pull request is on another repository, has MPAS-Ocean rows, deploys Spack environments and needs no lint step. One command line for both would put two workflows in one
AGENTS.md.deploy --rundoes the deployment, shared environments included. Handing the command back for the requester to run would defeat the utility. The requester’s permission for each machine is what guards the shared environments.The two utilities share one settings file,
~/.config/polaris_pr_test.cfg, which replaced the Omega utility’s~/.config/omega_pr_test.cfg.The pull request’s head is tested, not a merge into
main. Deployment runs from the pull request’s branch, and an update pull request is brought up to date withmainbefore it merges.The kind of environment is read from the load scripts, so a report states what was loaded (see the algorithm design for each report saying which Spack environments it tested).
deploycovers only the compilers and MPI libraries of supported rows. A machine’s config can list combinations that are no longer supported, such as Frontier’scrayamdin #835, or that do not build, such as pm-gpu’sintelgpu(E3SM-Project/mache#526). The first trial deployed everything the config listed and stopped on both.MPAS-Ocean builds in the checkout’s submodule, one row at a time. An E3SM worktree for each row would cost a large checkout for builds that take minutes.
New build warnings are compared for Omega rows only, at first. The parser and build log names are Omega’s. MPAS-Ocean can follow.
No lint step. Polaris’s own CI checks the pull request.