mod eb_maintainer

module eb_maintainer

Maintainer-acceptability checks distilled from real upstream reviews.

Primary sources:

  • easybuild-easyconfigs PR #26435 (CHANGES_REQUESTED): cross-generation dependency pins (“mixing two different toolchain generations”) and staged/incomprehensible shell in preconfigopts/postinstallcmds.

  • easybuild-easyconfigs PR #26480 review: hard-coded dependency toolchain tuples the robot would resolve itself, test suites that exist but are disabled or never run, and thin builds where the tree convention is to install packages as fat as possible.

These are mechanical gates for recipe check / recipe lint. They do not replace eb --check-contrib or a SUCCESS test report.

Functions

fn check_build_failure_modes(source_text: &str) -> Vec<MaintainerFinding>

Checks distilled from builds that failed on a site pipeline rather than from a review. Each one names a mechanism that produces a build the recipe reads as correct.

fn check_cross_generation_pins(recipe: &ResolvedEasyconfig) -> Vec<MaintainerFinding>

Cross-generation dependency pins: hard error (#26435).

fn check_dep_toolchain_pins(recipe: &ResolvedEasyconfig) -> Vec<MaintainerFinding>

Dependency toolchain tuples the robot would resolve itself: warning (#26480).

Cross-generation pins are the hard error above; this catches the softer review class where the pin is in the recipe hierarchy but still hard-coded. EasyBuild only hard-codes dependency toolchains in very exceptional cases (defining a higher-level toolchain); everywhere else the robot walks the subtoolchains of the recipe generation.

fn check_duplicate_upstream(recipe: &ResolvedEasyconfig, candidates: &[Candidate]) -> Vec<MaintainerFinding>

Re-adding an easyconfig the robot tree already ships: hard error (#26480).

Do/don’t rule 8. A PR that rewrites a file develop already has at the same name-version-toolchain is pure churn: reviewers see an unexplained diff against a working recipe, and the contributor’s own version is usually worse (different source URL, missing dependencies) because it was written blind.

candidates is the robot tree the recipe will be built against. A candidate that is the recipe’s own file is ignored, whichever way its path is spelled.

fn check_fat_build(text: &str) -> Vec<MaintainerFinding>

Fat-build and run-the-tests review classes from #26480: warnings.

fn check_git_source_archive(text: &str) -> Vec<MaintainerFinding>

A git source archived as .tar.gz.

get_source_tarball_from_git picks the compression from the extension of the name the recipe asks for, and treats .tar.xz as both its default and its reproducible format. Asking for .tar.gz is off that path. A git source also carries no checksum, so EasyBuild cannot tell a good cached archive from a bad one and will reuse whatever sits under that name.

fn check_hardcoded_gpu_arch(text: &str) -> Vec<MaintainerFinding>

A GPU architecture list written into the recipe by hand.

EasyBuild passes the build host’s compute capabilities to the build system, from --cuda-compute-capabilities or the cuda_compute_capabilities easyconfig parameter, and a site sets a different value per architecture. A literal list in configopts therefore agrees with at most one build host, and a build system that cross-checks its own architecture option against CMAKE_CUDA_ARCHITECTURES raises FATAL_ERROR when the two disagree, which ends the configure step in about a second.

A %(cuda_*)s template is the same value EasyBuild would pass, so it is exempt.

fn check_install_log_copy(text: &str) -> Vec<MaintainerFinding>

Build-tree diagnostics copied into the install prefix.

Every EasyBuild install already carries $EBROOT/easybuild with the full build log, the test report, the easyconfig as built and a reprod directory, all readable by anyone who can read the module. Copying a log out of the build tree duplicates that.

fn check_maintainer_acceptability(recipe: &ResolvedEasyconfig, source_text: &str) -> MaintainerReport

Full maintainer-acceptability report from resolved recipe + source text.

fn check_maintainer_acceptability_text(source_text: &str) -> MaintainerReport

Text-only path (lint without full resolve): shell monsters + rough cross-gen regex for four-element foss/gfbf pins that disagree with the recipe toolchain line.

fn check_shell_monsters(text: &str) -> Vec<MaintainerFinding>

Shell-monster / staged-build patterns: warning by default, escalated to error when the PR shape matches #26435 (many preconfigopts += or cargo cinstall stage).

fn check_unwrapped_compiler_rpath(text: &str) -> Vec<MaintainerFinding>

A build that drives an unwrapped compiler and never asks for DT_RPATH.

Two mechanisms meet here, and each on its own produces an install that fails the RPATH sanity check with the same message.

EasyBuild injects RPATH through wrapper scripts around the toolchain’s own compiler commands, so a build driven by clang meets no wrapper and links with whatever the recipe passes. CMakeMake does not compensate: it sets CMAKE_SKIP_RPATH only for CMake older than 3.5.

Passing -Wl,-rpath,... alone is still not enough. ld and lld default to --enable-new-dtags and write DT_RUNPATH, while sanity_check_rpath greps readelf -d output for the literal (RPATH), which DT_RUNPATH does not satisfy. rpath_args.py inserts --disable-new-dtags ahead of everything else for exactly this reason, and a recipe that goes around the wrappers has to carry that flag itself.

Enums

enum MaintainerSeverity

Severity of a maintainer-acceptability finding.

Error

Hard reject: same class as the #26435 cross-generation pin.

Warning

Strong reject: same class as the #26435 “incomprehensible” shell pipeline.

Structs and Unions

struct MaintainerFinding

One maintainer-acceptability finding.

code: String

Stable finding code, e.g. EB001.

severity: MaintainerSeverity

Whether this blocks upstream acceptance or merely warns.

message: String

What a maintainer would say about it.

evidence: Option<String>

The line or value it was raised on.

Implementations

impl MaintainerFinding

Functions

fn is_error(&self) -> bool

Whether this finding blocks acceptance.

struct MaintainerReport

Composite result for CLI/MCP.

findings: Vec<MaintainerFinding>

Everything found, errors and warnings alike.

Implementations

impl MaintainerReport

Functions

fn has_warnings(&self) -> bool

Whether anything non-blocking was found.

fn ok_for_upstream(&self) -> bool

Whether the recipe carries no blocking finding.