Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/ci_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ jobs:
cargo test --locked \
--test arch_known_failures \
--test arch_unsupported \
--test backport_list \
--test baseline_easystack \
--test batch_versions \
--test bot_classify \
Expand All @@ -172,6 +173,7 @@ jobs:
--test easyblock_layers \
--test easyblock_owed \
--test extension_provides \
--test family_site_layer \
--test fetch_prs_script \
--test fix_cost \
--test from_commit \
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ docs/source/images/

# Local solve outputs
stack.lock.json
!tests/backport_fixtures/stack.lock.json
stack.cdx.json
build.list
stack.diff.md
Expand Down
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,27 @@ All notable changes to this project are documented here.
and older-EESSI inventories. A module in the target is `moved` and names
its EESSI version; an excluded module is `site-only`; every other missing
route is `gone` until it lands.
- `stack solve` warns on standard error when a foss solve's baseline easystack names an LLVM-family recipe (`lfoss`, `lompi`, `lfbf`, or `llvm-compilers`) and the stack diff marks that package added.
The foss universe cannot see that family, so the added row is the site layer by mistake.
The solve still exits 0.
Keep one lock per toolchain family, or pass `--baseline-also-toolchain` for that family.
`llvm-compilers-20.1.8` and `llvm-compilers-21.1.8` are the versions `data/eessi-generations.toml` lists for generations 2025b and 2026.1.
- `component-reach` and `rebuild-set` record a reach class: `cross-user`, `network-facing`, `local-only`, `not-reachable`, or `unclassified` when no built-in name and no `--reachability` file names the package.
`component-reach` uses the built-in names only.
`cross-user` and `network-facing` justify a backport.
Component-reach schema is 2.
Rebuild-set schema is 5.
The component-reach summary prints `reach=`.
`rebuild-set.md` ends with the class of each patched easyconfig.
`--reachability` on `rebuild-set` applies to that map as well as to `backport.md`.
- `stack sbom --backport-list` writes that list for a stack lock.
`--cve` resolves an advisory through Open Source Vulnerabilities (OSV).
`--osv-cache` reads recorded responses and, without `--cve`, every id in `queries.json`.
`--reachability` is the same `name class` file as `rebuild-set`.
Blast radius is the module plus every locked module that depends on it through a runtime or build edge.
`local-only`, `not-reachable`, and `unclassified` are recorded and left off the list.
A `.md` path also writes the sibling `.json`.
`eb_stack_sbom` takes the same optional arguments and reports `listed`, `recorded`, and `stack` when the list is requested.
- `stack shard` splits one Jenkins build list into shards that can
build at the same time. A line's set is its closure over
`--easyconfigs` minus the modules in `--installed-modules-dir` Lmod
Expand Down
50 changes: 39 additions & 11 deletions docs/orgmode/explanation/sbom-blast-radius.org
Original file line number Diff line number Diff line change
Expand Up @@ -804,12 +804,40 @@ sed -n '27,29p' /tmp/sbom-demo/rebuild/rebuild-set.md
| `git-2.52.0-GCCcore-15.2.0.eb` | `git/2.52.0-GCCcore-15.2.0` | changed | must-rebuild | patched easyconfig |
#+end_src

#+begin_src bash
grep 'Reach class' -A 2 /tmp/sbom-demo/rebuild/rebuild-set.md
#+end_src

#+begin_src text
Reach class of each patched easyconfig. `cross-user` and `network-facing` justify a backport. `local-only` and `not-reachable` do not. `unclassified` is not a judgment.

- `git-2.52.0-GCCcore-15.2.0.eb`: network-facing
#+end_src

The report has one row: the changed git easyconfig, class must-rebuild.
The easystack entries do not depend on that git module, so the walk does
not add a second row. The report also notes each ~from-commit~ pin whose
tree was not passed and says the release tree was used for that pin.
~--check-static~ was not passed, and the report says so. Classes follow
the easyconfig graph only.
The easystack entries do not depend on that git module, so the walk does not add a second row.
The report also notes each ~from-commit~ pin whose tree was not passed and says the release tree was used for that pin.
~--check-static~ was not passed, and the report says so.
Classes follow the easyconfig graph only. git is ~network-facing~, so this patched easyconfig would justify a backport.

A stack lock has the same classes.
~stack sbom --backport-list~ reads recorded Open Source Vulnerabilities (OSV) responses and the software bill of materials (SBOM) reverse closure.
~cross-user~ and ~network-facing~ are the list.
The fixture lock has cURL (network-facing, blast radius 2 because git depends on it) and notebook (cross-user). zlib is ~local-only~ in the site file, so it is recorded and left off the list.

#+begin_src bash
eb-stack stack sbom \
--lock tests/backport_fixtures/stack.lock.json \
--out /tmp/sbom-demo/backport.cdx.json \
--backport-list /tmp/sbom-demo/backport.md \
--osv-cache tests/backport_fixtures/osv \
--reachability tests/backport_fixtures/reach.txt
#+end_src

#+begin_src text
sbom=/tmp/sbom-demo/backport.cdx.json components=7 recipes_read=7 recipes_unreadable=0 with_artifact_facts=0
backport=2 recorded=3 stack=7 out=/tmp/sbom-demo/backport.md
#+end_src

* A component named by a CVE

Expand All @@ -832,15 +860,15 @@ eb-stack component-reach \
#+end_src

#+begin_src text
component=curl version=>=8,<9 generation=2026.1 carriers=2 changed=1 must-rebuild=1 review=0 no-rebuild=1 out=/tmp/sbom-demo/component-curl
component=curl version=>=8,<9 generation=2026.1 reach=network-facing carriers=2 changed=1 must-rebuild=1 review=0 no-rebuild=1 out=/tmp/sbom-demo/component-curl
#+end_src

#+begin_src bash
sed -n '5,16p' /tmp/sbom-demo/component-curl/component-reach.md
#+end_src

#+begin_src text
2 carriers. Closure: 1 must-rebuild, 0 review, 1 no-rebuild.
2 carriers. Reach: `network-facing`. Closure: 1 must-rebuild, 0 review, 1 no-rebuild.

| Easyconfig | Module | Role | Version | Installed |
|---|---|---|---|---|
Expand Down Expand Up @@ -1023,10 +1051,10 @@ eb_easyblock_reach
eb_repro_summary
#+end_src

That is 50 tools. ~eb_stack_sbom~ writes the CycloneDX file ~stack sbom~
writes. Its ~tools/list~ description reads: "Emit CycloneDX from an existing
stack lock, reading dependency edges and source checksums from the
easyconfigs the lock names." ~eb_stack_sbom~ reads the Cbc lock:
That is 50 tools.
~eb_stack_sbom~ writes the CycloneDX file ~stack sbom~ writes.
Its ~tools/list~ description reads: "Emit CycloneDX from an existing stack lock, reading dependency edges and source checksums from the easyconfigs the lock names. With backport_list, also write the modules whose reach class justifies a backport."
~eb_stack_sbom~ reads the Cbc lock:

#+begin_src bash
printf '%s\n' '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"eb_stack_sbom","arguments":{"lock":"/tmp/sbom-demo/stack.lock.json","out":"/tmp/sbom-demo/mcp-stack.cdx.json"}}}' \
Expand Down
13 changes: 8 additions & 5 deletions docs/orgmode/howto/component-reach.org
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ eb-stack component-reach \
The command prints:

#+begin_example
component=curl version=>=8,<9 generation=2026.1 carriers=2 changed=1 must-rebuild=1 review=0 no-rebuild=1 out=/tmp/component-reach
component=curl version=>=8,<9 generation=2026.1 reach=network-facing carriers=2 changed=1 must-rebuild=1 review=0 no-rebuild=1 out=/tmp/component-reach
#+end_example

cURL 8.17.0 is the module. git 2.52.0 depends on that cURL and is the
Expand All @@ -50,12 +50,15 @@ eb-stack component-reach \
The command prints:

#+begin_example
component=curl version=>=8,<9 generation=2026.1 carriers=23 changed=2 must-rebuild=1 review=106 no-rebuild=16 out=/tmp/component-reach-full
component=curl version=>=8,<9 generation=2026.1 reach=network-facing carriers=23 changed=2 must-rebuild=1 review=106 no-rebuild=16 out=/tmp/component-reach-full
#+end_example

The changed files are ~cURL-8.17.0-GCCcore-15.2.0.eb~ and
~cargo-c-0.10.23-GCCcore-15.2.0.eb~. The must-rebuild row is
~cURL/8.17.0-GCCcore-15.2.0~, reason ~patched easyconfig~.
The changed files are ~cURL-8.17.0-GCCcore-15.2.0.eb~ and ~cargo-c-0.10.23-GCCcore-15.2.0.eb~.
The must-rebuild row is ~cURL/8.17.0-GCCcore-15.2.0~, reason ~patched easyconfig~.
The summary's ~reach=~ is the class of the component name: ~cross-user~, ~network-facing~, ~local-only~, ~not-reachable~, or ~unclassified~ when the built-in names do not include it.
~component-reach~ reads no site file.
~cross-user~ and ~network-facing~ justify a backport.
cURL is ~network-facing~.

~--easystack~ and ~--installed~ narrow the walk to a deployed prefix.
~--filter-deps~ drops dependency tuples before the closure walk. When
Expand Down
6 changes: 6 additions & 0 deletions docs/orgmode/howto/rebuild-set.org
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ The command prints:
changed=2 rows=8 must-rebuild=2 review=2 no-rebuild=4 missing=1 easystacks=2 skipped_easyconfigs=0 out=/tmp/rebuild-out
#+end_example

~rebuild-set.md~ ends with the reach class of each changed easyconfig.
~cross-user~ and ~network-facing~ justify a backport.
~local-only~ and ~not-reachable~ do not.
~unclassified~ means neither the built-in names nor a ~--reachability~ file names the package.
zlib and ~Absent-1.0.eb~ on this fixture are ~unclassified~.

* Run it on a mounted stack

From a clone of ~EESSI/software-layer~, with the 2026.06 stack mounted.
Expand Down
50 changes: 50 additions & 0 deletions docs/orgmode/howto/solve-lock.org
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,15 @@ that name is also in the lock. A path that cannot be read contributes
nothing for that component. ~--sbom-out~ during ~stack solve~ writes the
same document while the candidate graph is still in memory.

~--backport-list~ writes the locked modules whose reach class justifies a backport.
Pass ~--cve~ or ~--osv-cache~.
~--osv-cache~ reads recorded Open Source Vulnerabilities (OSV) responses and does not query the network when ~--cve~ is omitted.
~--reachability~ adds or replaces a class.
Blast radius is the module and every locked module that depends on it through a runtime or build edge.
~cross-user~ and ~network-facing~ are the list.
~local-only~, ~not-reachable~, and ~unclassified~ are recorded and left off it.
See [[file:../reference/cli.org][the CLI reference]].

* Order every pinned build

~stack solve~ picks one version of each name. ~stack order~ keeps every
Expand All @@ -81,5 +90,46 @@ eb-stack stack order \
requirement. The hash file is one ~<sha256> <module>~ line per build.
See [[file:../reference/cli.org][the CLI reference]].

* One lock per toolchain family

A foss solve only sees the foss hierarchy.
The LLVM family is ~lfoss~, ~lompi~, ~lfbf~, and ~llvm-compilers~.
OpenMPI under ~llvm-compilers-21.1.8~ is in that family.
~data/eessi-generations.toml~ lists ~llvm-compilers-20.1.8~ for generation 2025b and ~llvm-compilers-21.1.8~ for generation 2026.1, because an LLVM release is not a generation name.

An easystack entry such as ~GROMACS-2025.0-lfoss-2025b.eb~ is absent from a foss baseline.
The foss solve then selects the foss build of the same name, and the stack diff marks that row added.
Added is the site layer.
Keep one lock per toolchain family: solve ~lfoss~ on its own.
Passing ~--baseline-also-toolchain lfoss/2025b~ keeps that family's hierarchy in the foss baseline, and the warning stays quiet for ~lfoss~.

#+begin_src bash
eb-stack stack solve \
--easyconfigs fixtures/gromacs_2025_to_next/easyconfigs \
--toolchain foss/2025b \
--root GROMACS \
--baseline-easystack /tmp/lfoss.yml \
--lock-out /tmp/foss.lock.json \
--stack-diff-out /tmp/foss.diff.md
#+end_src

~/tmp/lfoss.yml~ has one entry:

#+begin_src yaml
easyconfigs:
- GROMACS-2025.0-lfoss-2025b.eb
#+end_src

The solve still exits 0.
Standard error names the unresolved entry.
It also prints a warning that the added GROMACS row is the LLVM family:

#+begin_src text
warning: GROMACS (GROMACS-2025.0-lfoss-2025b.eb) is the LLVM family (lfoss). A foss solve cannot reach it, so the site layer includes GROMACS (fixtures/gromacs_2025_to_next/easyconfigs/foss-2025b/GROMACS-2025.0-foss-2025b.eb). Keep one lock per toolchain family: solve lfoss on its own, or pass --baseline-also-toolchain lfoss/2025b.
#+end_src

The path in that warning is the foss easyconfig the solve selected.
A package the foss closure only version-bumps stays off this warning.

See [[file:../reference/policy.org][the policy reference]] and
[[file:../reference/formats.org][the format reference]].
Loading
Loading