What to remember

Keypoints for each chapter are produced in reading order directly from the chapters instead of being manually maintained. The Carpentries call this a digest (Wilson [2016]). It has two uses. Prior to a chapter, it indicates if the chapter’s purpose is already known. After completing the book, it is the page to reread instead of the entire book.

eb-stack ports a recipe and bumps one

  • A port reads conda-forge or Spack and writes an easyconfig. package plan --format conda-forge or --format spack.

  • A bump reads an easyconfig and writes the same software on a new toolchain generation. Application version, sources and patches stay; every pin that lives on the toolchain moves.

  • Ranges become pins. A Spack eigen@3.4: is Eigen-3.4.0 on that generation, or a residual if the robot has no candidate.

  • Nothing in inspect, plan or bump compiles the package. campaign run calls eb.

Reading a failed build

  • The exit code narrows the search before any log does: 127 is not found, 126 is not executable, 2 from make means a rule failed and the cause is higher up.

  • The step in the error message rules out every step before it.

  • find_base_dir descends while a directory has exactly one entry, so the start directory can differ from the archive's top.

  • A failed build keeps its build directory, and config.log or CMakeError.log in it says what the build system actually tried.

  • cmd.sh reproduces the failing command in the environment it failed in; eb -x cannot see any fault that depends on the contents of an archive.

Writing one from nothing

  • Five parameters are enforced: name, version, homepage, description, toolchain. The MANDATORY category lists eight.

  • Search before writing; a copy of an existing easyconfig with a new version is almost always the cheaper correct answer.

  • --inject-checksums downloads, backs the file up, reorders parameters and does not overwrite an existing checksum without --force.

  • A local variable in an easyconfig has to be named local_*.

  • --check-style and --check-contrib are what upstream CI runs; neither runs the software, which is why a test report from a human is required.

Writing an easyblock

  • Reach for configopts first, a generic easyblock second, and your own code only when more than one easyconfig will need it.

  • configure_step, build_step and install_step are the three the abstract class does not implement.

  • extra_options is how a parameter is born; setvar is how an environment variable is set so the dry run can see it.

  • --include-easyblocks shadows the shipped easyblock, and --list-easyblocks=detailed is how to confirm which file loaded.

  • Under --module-only most steps do not run, so state carried between steps arrives as None.

Testing, and the four things it can mean

  • Four different things are called testing: the package's own suite, the sanity check, a test report, and a portable run on hardware that matters about. None of them subsumes another.

  • runtest is a whole command on the base class and an argument to make under ConfigureMake.

  • --ignore-test-failure records a failure, --skip-test-step avoids it, and skipsteps removes it for everyone.

  • The RPATH check already exempts libcuda.so.1 and the other driver libraries, so anything else it reports is real.

  • Upstream CI never builds an easyconfig, which is why the evidence is a human's test report and why an author cannot merge their own.

Standing where the build stood

  • Every install already carries the easyconfig, the log, a test report, a devel module and a reprod/ directory holding the easyblock source.

  • module load $EBDEVEL<NAME> reproduces the environment that build had, including $PATH and the modules tool's own bookkeeping.

  • eb --dump-env-script writes the toolchain's environment for a recipe before any build, and does not overwrite.

  • BuildEnv turns a toolchain's environment into a module other people can load, and copies in the RPATH wrappers.

  • What reprod/ does not carry: the framework version, the sources, the dependency installations, and the operating system.

Reading a stack off the wire: CernVM-FS

  • A repository is verifiable from one signature the root catalog's hash and every catalog names its children's.

  • Objects are named by the hash of their compressed bytes, so any client can check what it was given without trusting the transport.

  • A catalog is a SQLite database keyed by the MD5 of the absolute path, cut into nested catalogs that are fetched on first access.

  • Two keys: the repository key signs each revision, the master key signs the whitelist, and only the master public key has to reach clients out of band.

  • A cache miss is a sequence, and the client trusts its cache once the object is in it.

The same stack somewhere else

  • Three routes onto a machine: a native client with root, cvmfsexec or a container without it, and the same two on an ephemeral runner where the cache is the whole of the install.

  • The compatibility layer is why the host distribution stops mattering.

  • eessi_archdetect.sh -a cpupath returns an ordered list and the first target that exists on disk wins, so read $EESSI_SOFTWARE_SUBDIR rather than assuming.

  • Overrides are not validated: a target that does not exist gives an empty module avail.

  • A site adds software and drivers through a variant symlink, without writing to /cvmfs and without invalidating a signature.

Four things that actually happened

  • Every one of the four was green somewhere, so the question is never "did it pass" but what it passed and where.

  • A symlink such as /sw/arch resolves against the node underfoot on, which makes an ldd on a login node evidence about the login node.

  • A failing check on a package that cannot be fixed is a report about something else; deleting the report does not fix the something else.

  • A parameter whose meaning depends on the shape of the source has to be re-read whenever the source changes.

Your first week on somebody’s stack

  • A site's pipeline, build lists and hooks are what stand between an easyconfig and a module; find all three before your first build.

  • A version bump is a change of source: re-read every parameter that describes the shape of the archive.

  • A dry run cannot see a fault that depends on the contents of an archive.

  • On a heterogeneous system, measure library resolution on the architecture that will run the binary, not on the one underfoot.

  • Every claim about a build carries the machine and the architecture it was measured on, or it is not a claim.

Extensions, and what an entry in exts_list inherits

  • exts_default_options reaches only entries that carry a version; a bare-string entry gets no options at all.

  • The merge is a shallow update, so a per-extension list replaces the default list instead of extending it.

  • An unknown per-extension option is dropped at debug level, with no warning: a typo in a bundle of two hundred extensions is silent.

  • Eight parameters plus start_dir are reset before an extension runs,

  • exts_filter is one mechanism doing two jobs, skipping what is already installed and checking what was just installed, and modulename: False switches off both.

An easystack is the list the bot will build

  • An easystack is a YAML list of easyconfig filenames. Per-item options: become eb flags with the dashes stripped, and last-wins means those flags beat the command line.

  • EESSI's path is easystacks/software.eessi.io/<ver>/eessi-<ver>-eb-<ebver>-<tc>.yml. The EasyBuild version in the name is the module the bot loads.

  • from-commit (or from-pr) is how an unmerged easyconfig is built. Deployment into the repository waits on the EasyBuild merge.

  • A rebuild lives under rebuilds/ and is the only case that passes --rebuild instead of --robot.

The bot is three processes and a comment

  • A bot instance is smee plus an event handler plus a job manager. A comment matching ^bot: is what the handler acts on. Opening a pull request is not.

  • for: is the installation prefix. on: is the allocated node. optarch stays native. CPU-to-CPU cross-compilation is not implemented.

  • software-layer/bot/build.sh pins a software-layer-scripts commit and execs that bot/build.sh. The job's .diff is what selects the easystack files.

  • A green job produces a signed tarball. bot:deploy uploads it. A Stratum-0 ingest is what makes a module. Three human permissions stand on that path.

A site stack next to EESSI, not inside it

  • EESSI_SITE_SOFTWARE_PREFIX is set before the EESSI module loads. EESSI-extend then writes an architecture-specific prefix and puts it on MODULEPATH.

  • Approach 1 is that prefix on a shared filesystem. Approach 2 is the same bot and the same scripts, aimed at a site CernVM-FS repository. The webinar recommends the owned prefix over stuffing everything into the host_injections symlink.

  • 2025.06 aims NVIDIA driver symlinks with EESSI_NVIDIA_OVERRIDE_DEFAULT. 2023.06 used EESSI_HOST_INJECTIONS. They are not interchangeable.

  • A site MPI that is ABI-compatible with EESSI's OpenMPI goes in rpath_overrides/OpenMPI/system/lib. The modules above MPI do not get rebuilt.

Inspect writes a work queue, not a recipe

  • package inspect writes package.plan.json and a planned CycloneDX SBOM. It establishes no claim.

  • Residuals are the work queue. Notes are not. Severity is mechanical, judgment, or blocking.

  • package plan requires --stack-policy. The lock and the emitted .eb are what establish resolves.

Format, lint, and check are three different questions

  • recipe format rewrites physical lines and nothing else.

  • recipe lint reports the same surface and does not write.

  • recipe check is the robot and metadata gate. A missing dependency stays a missing dependency.

A stack lock is not a package lock

  • stack solve is the multi-root annual-bump solver. Its policy is JSON. The package command's --stack-policy is TOML. They are not interchangeable.

  • build.list is the install order. stack.diff.md is the pull request. stack.lock.json is the Resolvo assignment.

  • A lock-only SBOM has no dependency edges. Edges are written only when --sbom-out is passed to stack solve.

A campaign finding is a typed failure, not a log

  • \*.campaign.json stores three independent claims. A completed run with no verify command is builds and not binary_verified.

  • A finding is a class, a disposition, a stage, and the exact routed command. The class names the first repair surface.

  • Claim is exclusive. Resolve records the action. Retry uses the same state path. The audit trail is not deleted.

A wrapper is the range EasyBuild will not write

  • An easyconfig still cannot write a range. A ModuleRC .modulerc is how a site answers an old name with a newer module.

  • check_version default True requires the requested version to be a prefix of the wrapped one.

  • In EasyBuild 5, -D does not imply -r. The plan is -Dr.

Four EasyBuild 5 surfaces the earlier chapters skipped

  • An install lock is a mutex on a shared prefix. A Resolvo lock is an assignment of versions. They share a word and nothing else.

  • --bwrap installs in a namespace and then moves. /tmp noexec breaks the RPATH wrappers.

  • EBPYTHONPREFIXES makes a second Python prefix visible. Entry points are a 5.3 addition; an import test does not cover them.

  • A Dataset module is data. EASYBUILD_INSTALLPATH_DATA keeps it off the software prefix. Software depends on it like any other module.

A ReFrame test is a class

  • A ReFrame test is a decorated Python class. Without @rfm.simple_test the class is never registered.

  • RunOnlyRegressionTest skips compile. The executable is assumed to exist.

  • valid_systems and valid_prog_environs are filters, not comments. ['*'] means any.

  • ReFrame does not look at the exit code. A sanity function is the verdict, and a missing pattern is a failure.

The pipeline has six stages

  • Six stages, in order: setup, compile, run, sanity, performance, cleanup. A run-only test no-ops compile.

  • Construction is not a stage. __init__ runs for tests that will be skipped.

  • Sanity and performance run on the ReFrame host, against files the job wrote. The stage directory has to be shared with the nodes.

  • run is submit; run_complete is wait. A queued job fails in the second.

The configuration file is the machine

  • The test describes what to run. The configuration describes where. A number of cores in the test is a number of cores on one machine.

  • The builtin configuration always defines generic:default and builtin. A site file adds to it.

  • A partition is a scheduler, a launcher and a list of environments. valid_systems matches system:partition.

  • RFM_CONFIG_FILES, RFM_CHECK_SEARCH_PATH and $RFM_PREFIX are the three variables a site sets once.

A performance function is a number with a unit

  • A performance function returns a number and declares a unit. It does not decide correctness; the sanity function does.

  • A reference is (value, lower, upper, unit) per system:partition. A miss fails the performance stage only.

  • No reference still logs the number. That log is how a baseline is built.

  • Extract the application's own figure of merit. ReFrame's run timer includes the queue.

Compile or run-only

  • RegressionTest compiles, then runs. RunOnlyRegressionTest runs a binary that already exists. CompileOnlyRegressionTest compiles and stops.

  • An EasyBuild shop's application tests are run-only: the module is the thing under test, and valid_prog_environs = ['default'].

  • Sources live in src/ next to the test file and are copied into the stage directory.

  • The job script is generated. The test class does not write #SBATCH.

Parameters multiply the suite

  • parameter multiplies the suite. variable does not; it is an overridable default.

  • The product is parameters × partitions × environments. Four small lists become a suite nobody will wait for.

  • Hooks after init see one case. Module names and task counts are set there.

  • --tag CI filters a run. It does not delete the other cases from the class.

A portable test leaves the machine in the config

  • A cluster name, a core count and a module string in the test are three facts about one machine.

  • The mixin reads the partition. "One full node" is a request; the configuration supplies the number.

  • find_modules builds the parameter list from the tree that is actually loaded.

  • A missing feature or too little memory is a skip, not a failed job.

Software maps to tests

  • A mapping file is how a new module inherits tests. A missing line is a silent skip the next morning.

  • EESSI's suite is one tree with three filters: contribution (CI), nightly, end user.

  • CSCS lays out config/, checks/, ci/. The machine, the tests, and the invocation stay apart.

  • CSC's computing-environment course is not this course. ReFrame is taught as its own unit, which is why it is a part of this book.

Reading a failed ReFrame run

  • Failing phase is the first field. The stage directory is the second.

  • rfm_job.sh is what ran. rfm_job.out is what it printed. On failure both stay in the stage; on success they move to output/.

  • --restore-session --failed reruns the failures and restores passed dependencies.

  • A skip is not a pass. A missing listing is a filter, and the debug log is the file that recorded it.