HaoZeke/eb-stack
GitHub: HaoZeke/eb-stack
eb-stack 将 conda-forge、Spack 和 EasyBuild 的外部配方统一转化为依赖求解后的标准化软件包捆绑包,并生成 CycloneDX SBOM 和经构建验证的 EasyBuild 配方。
Stars: 0 | Forks: 0
**将外部和 EasyBuild 配方转化为已解析、经构建评估的软件包捆绑包。**
`eb-stack` 将 conda-forge、Spack 和 EasyBuild 配方解析为一个标准化的软件包计划。它会生成一个计划中的 CycloneDX SBOM,使用 Resolvo 解析每个产品配置(profile),为每个可安装的变体(variant)写入一个约定俗成的 `.eb` 文件,并通过持久化的远程构建活动(campaign)来驱动这些配方。
[](https://github.com/HaoZeke/eb-stack/actions/workflows/ci_test.yml)
[](https://github.com/HaoZeke/eb-stack/actions/workflows/ci_docs.yml)
[](https://crates.io/crates/eb_stack)
[](https://eb-stack.rgoswami.me)
[](LICENSE)
## 流水线
```
conda-forge / Spack / EasyBuild
│
▼
canonical package.plan.json ─── package.sbom.cdx.json
│
▼
profile materialization + stack policy + Resolvo
│
├── locks/
.lock.json
└── easyconfigs///.eb
│
▼
target-routed campaign + typed findings
```
新软件包和版本升级(bump)使用相同的制品。版本升级是一个源自 EasyBuild 的计划,包含 SBOM 和 Resolvo 锁定,而不是一个独立的文本重写。
## 声明阶梯
独立报告这些声明:
1. **resolves(已解析)** — 每个请求的配置都有一个 Resolvo 锁定并生成了配方;
2. **builds(已构建)** — 每个生成的配方都能在配置的目标上通过 EasyBuild 完成构建;
3. **binary-verified(二进制已验证)** — 每个声明的配置验证命令都能成功执行。
`package inspect` 不建立任何声明。`package plan` 和 `package bump` 只能建立 `resolves`。`campaign run` 负责另外两个声明。
## 安装
该 crate 在 crates.io 上的名称为 `eb_stack`;安装的二进制文件为 `eb-stack`。
从源代码构建时需要 Rust 1.88 或更高版本。
预构建二进制文件(最快):
```
cargo binstall eb_stack
```
Shell 安装程序(Linux/macOS):
```
curl -LsSf https://github.com/HaoZeke/eb-stack/releases/latest/download/eb_stack-installer.sh | sh
```
通过 crates.io:
```
cargo install eb_stack --locked
```
通过源码:
```
git clone https://github.com/HaoZeke/eb-stack.git
cd eb-stack
cargo test --locked
cargo build --locked --release
install -m755 target/release/eb-stack ~/.local/bin/eb-stack
```
在合适的构建主机上构建 Rust 二进制文件。EasyBuild 的安装应属于由公共目标配置选择的特定目标,而不一定在运行 CLI 的机器上。
文档:[https://eb-stack.rgoswami.me](https://eb-stack.rgoswami.me)
## 从全新克隆中进行检查
仓库包含外部配方和软件包配置的测试固件(fixtures),因此无需 EasyBuild 安装或外部 robot 树即可测试解析边界:
```
eb-stack package inspect \
--source fixtures/foreign_ingest/conda_eon/recipe.yaml \
--format conda-forge \
--toolchain-name foss \
--toolchain-version 2026.1 \
--package-config examples/packages/common.toml \
--package-config examples/packages/eon.toml \
--out-dir /tmp/eon-inspect
python3 -m json.tool /tmp/eon-inspect/package.plan.json >/dev/null
python3 -m json.tool /tmp/eon-inspect/package.sbom.cdx.json >/dev/null
```
这将写入标准化的构建清单和计划中的 CycloneDX SBOM。它不会解析依赖、生成配方或做出构建声明;这些阶段需要 EasyBuild robot 树和已配置的构建目标。
## 文档
手册围绕操作者的任务进行组织:
- [外部配方到已验证的软件包](docs/orgmode/tutorial.org);
- [软件包捆绑包 schema](docs/orgmode/reference/package-bundles.org);
- [分层构建目标](docs/orgmode/reference/targets.org);
- [活动状态和类型化的发现](docs/orgmode/reference/campaigns.org);
- [修复失败的活动](docs/orgmode/howto/repair-campaign.org);
- [命令行参考](docs/orgmode/reference/cli.org)。
源手册位于 `docs/orgmode/` 中;文档工作流将 RST 导出到 `docs/source/` 并验证 Sphinx 站点。
## 升级现有配方
```
eb-stack package bump \
--source tests/repro_fixtures/gromacs/GROMACS-2024.4-foss-2023b.eb \
--toolchain-name foss \
--toolchain-version 2024a \
--easyconfigs tests/repro_fixtures/universe_foss_2024a \
--out-dir /tmp/gromacs-2024a
```
输出的配方为 `/tmp/gromacs-2024a/easyconfigs/g/GROMACS/GROMACS-2024.4-foss-2024a.eb`;同一目录包含其清单、SBOM 和 `default` 配置锁定。对覆盖层重复使用 `--easyconfigs`,并对 EESSI 或站点首选项使用 `--stack-policy`。`preferred` 锁定可能会在 Resolvo 内部回退;而 `locked` 锁定则不会。
已知的维护者测试固件涵盖了 `foss-2023b` → `foss-2024a` 中的 GROMACS、ScaFaCoS、MDTraj、Fiona、PuLP 和 numba。
## 计划新软件包
软件包策略是公开的、分层的 TOML。解析器保留外部名称和语法,无需区分软件包名称分支;Resolvo 机械地匹配大小写/标点符号等效的 robot 名称。显式的生态系统别名、EasyBuild 元数据、构建策略和可独立安装的配置位于软件包配置中。
每个配置生成一个 `.eb` 文件;仅 MPI/OpenMP 工具链选项不需要后缀。
配置可以声明 `platform` 和 `architecture` 目标上下文。外部选择器在依赖解析之前会根据这些值进行评估,因此特定目标的依赖会变成真正的 Resolvo 需求,而不是在计划于另一台主机上运行时悄无声息地消失。
Spack 适配器在不导入配方的情况下将 Python 解析为 AST。它静态评估字面量数据、有界循环、格式化和 `when` 作用域;不支持的运行时表达式会变成带有源码位置的残留信息,而不是被猜测为依赖项。
解析器说明仅供参考。计划阶段仅使用类型化的残留记录,因此关于成功物化动态源码逻辑的说明不会意外地将任务排入 Hermes 工作队列。真正的残留信息包含类别、严重性、摘要以及可选的证据和来源溯源信息。
Conda 修订号以及编译器/标准库/交叉构建模板需求属于工具链诊断,而不是软件包编写工作。普通软件包身份中未知的模板仍然是显式的残留信息。
当两个名称共享同一个版本域时,别名使用 `foreign = "EasyBuild"`。组件到提供者的映射使用 `foreign = { provider = "EasyBuild", constraint = "drop" }`,因此组件版本(例如 Python 软件包版本)永远不会强加于其包含的 EasyBuild 提供者。
软件包策略还有两个通用的 EasyBuild 输入:
```
[build.easyconfig_parameters]
general_packages = ["ASPHERE", "KSPACE", "MOLECULE"]
[[build.patches]]
filename = "Orbit-2.0-portability.patch"
sha256 = "4f43b42fdcf84d0cf634d993dd944f252c8243dc612a919fe2825d56f937c8eb"
source = "patches/Orbit-2.0-portability.patch"
[[dependencies.requirements]]
name = "VTK"
roles = ["run"]
```
默认情况下,声明了 `build.patches` 的层会替换之前的补丁列表。在 `[build]` 中设置 `patches_mode = "merge"` 可以保留从 conda-forge 或 Spack 提取的补丁,同时添加 EasyBuild 策略补丁。具有相同文件名的合并条目会替换该制品,因此经过审查的校验和和源码路径可以细化外部元数据而不会产生重复。
Easyconfig 参数是类型化数据,而不是 Python 片段。需求在 Resolvo 选择版本之前进入标准化清单和 CycloneDX SBOM。软件包计划需要每个源码和补丁的 SHA-256;生成的校验和保持位置顺序,先是源码,后是补丁。本地补丁资产会在每个生成的配方旁边进行验证和复制。以校验和为地址的远程补丁在 `patches` 中保留其确切的 URL;EasyBuild 会在构建过程中下载并验证它们。
```
eb-stack package plan \
--source fixtures/foreign_ingest/conda_eon/recipe.yaml \
--format conda-forge \
--toolchain-name foss \
--toolchain-version 2026.1 \
--package-config examples/packages/common.toml \
--package-config examples/packages/eon.toml \
--easyconfigs /path/to/easybuild-easyconfigs/easybuild/easyconfigs \
--easyconfigs fixtures/eon_foss_2026_1/easyconfigs \
--stack-policy examples/stacks/eon-foss-2026.1.toml \
--out-dir /tmp/eon
```
内置的 eOn 软件包策略为 **核心 + `with_rgpot`**:Fortran/CuH2 客户端、readcon-core 以及进程内 RGPOT(引擎在运行时执行 dlopen)。默认关闭 Fat metatomic/xTB/serve;这些名称对应的 conda 宿主依赖项被排除在解析范围之外。堆栈策略仅针对 safemath 补丁路径首选 Eigen 5。`fixtures/eon_packaging` 和 `fixtures/eon_foss_2026_1` 下的历史全产品固件保留作为旧的 feedstock 对齐表面的回归快照。
对于 QMCPACK 的 `package.py`,请使用 `--format spack`、`examples/packages/common.toml` 和 `examples/packages/qmcpack.toml`。其 Spack 版本由提交而不是存档哈希锁定,因此计划还需要 `--source-checksum 511d5f368db002f2f77504619e1ada8d4a3034200d25feef6773d12a6ed6d18e`。
解析器输出保留源码跨度、conda 选择器、Spack 条件/冲突、依赖角色以及残留的动态逻辑。
在不解析或生成配方的情况下进行检查:
```
eb-stack package inspect \
--source fixtures/foreign_ingest/spack_qmcpack/package.py \
--format spack \
--toolchain-name foss \
--toolchain-version 2026.1 \
--package-config examples/packages/common.toml \
--package-config examples/packages/qmcpack.toml \
--out-dir /tmp/qmcpack-inspect
```
## 检查配方
```
eb-stack recipe format /tmp/eon/easyconfigs/e/eOn/*.eb
eb-stack recipe lint /tmp/eon/easyconfigs/e/eOn/*.eb
eb-stack recipe check \
--recipe /tmp/eon/easyconfigs/e/eOn/eOn-2.16.0-foss-2026.1.eb \
--easyconfigs /path/to/robot \
--easyconfigs /tmp/eon/easyconfigs
```
校验和是按位置的:先是源码,然后是补丁。缺少依赖项的输出包括兼容的层级成员和在其他世代(generation)中找到的候选者。修复配方或伴随配置;不要绕过检查。
## 配置和运行构建
目标按 传输 -> 执行器 -> 运行时 -> EasyBuild 工作负载 进行分层:
```
eb-stack target list --config examples/targets/base.toml --config ~/.config/eb-stack/site.toml
eb-stack target doctor \
--config examples/targets/base.toml \
--config ~/.config/eb-stack/site.toml \
--target site-builder
eb-stack campaign run \
--bundle /tmp/eon \
--config examples/targets/base.toml \
--config ~/.config/eb-stack/site.toml \
--target site-builder \
--state /tmp/eon.campaign.json
```
`campaign run` 是一个前台命令。请将其保持在站点正常的终端或服务监督器下,并从另一个 shell 使用 `campaign status` 检查状态。一次只能有一个运行、声明或解析操作写入状态文件。如果控制进程退出,进程锁定会自动释放;在重新运行中断的状态之前,请确认其路由的容器、调度程序作业或主机命令不再处于活动状态。
传输可以是本地或 SSH;执行可以是直接执行或 Slurm;运行时可以是 host、Podman 或 Docker。站点主机名、路径、模块和调度程序规模属于站点层。容器目标必须使用特定于 ABI 的安装、工作和临时根目录;只有源码存档应该跨运行时共享。
Cargo 会在祖先目录中搜索 `.cargo/config.toml`。如果目标存储位于绑定挂载的宿主目录之下,请将活动子树再次挂载到中立的容器路径(例如 `/eb-stack-campaigns`),然后将该路径用于运行时 `workdir` 和 EasyBuild `work_root`。[目标参考](docs/orgmode/reference/targets.org)包含完整的 TOML 模式。
可运行的本地目标默认为两个并行的 EasyBuild 作业,以便在常见的工作站上仍能进行占用大量内存的 C++ 编译。将 `EASYBUILD_PARALLEL`、调度程序 CPU 和调度程序内存视为一个分配:仅当目标具有测量的余量时才提高并行度。被内核终止或耗尽虚拟内存的编译器进程是可重试的 `resource` 发现,而不是所选依赖项不兼容的证据。
有关本地公开示例,请构建 `skills/new-package/container/rocky9/Containerfile`,填充 `/tmp/eb-stack/robot`,并使用 `examples/targets/local-podman.toml`。将捆绑包保留在 `/tmp/eb-stack/bundles` 下,以便宿主和容器路径匹配。
Hermes 和 OMP 命名的是活动角色,而不是必需的编排软件。
Hermes 是唯一的所有者,它读取类型化的发现、应用修复,并通过请求的声明阶梯进行重试。OMP worker 是可选的并发参与者,它们仅通过发现的所有权进行协调。
活动失败将以类型化发现的形式持久化。OMP worker 通过拥有的队列操作协调修复:
```
eb-stack campaign finding claim --state /tmp/eon.campaign.json \
--id attempt:1:finding:1 --owner omp-worker-1
eb-stack campaign finding resolve --state /tmp/eon.campaign.json \
--id attempt:1:finding:1 --owner omp-worker-1 \
--action "corrected package configuration" \
--evidence "recipe check exits successfully" \
--change examples/packages/eon.toml
```
状态文件保留尝试记录、活动配方、类型化的失败命令和紧凑日志、所有权、修复证据以及声明阶梯。成功的重试会取代匹配的未解决发现。
## 解析整个堆栈
```
eb-stack stack solve \
--easyconfigs fixtures/gromacs_2025_to_next/easyconfigs \
--policy fixtures/gromacs_2025_to_next/policies/prefer_newer.json \
--baseline-easyconfigs fixtures/gromacs_2025_to_next/easyconfigs \
--lock-out stack.lock.json \
--sbom-out stack.cdx.json \
--build-list-out build.list \
--stack-diff-out stack.diff.md
```
## MCP
`eb-stack mcp` 通过 stdio 公开了相同的 version-one 工作流:
- `eb_package_inspect`、`eb_package_plan`、`eb_package_bump`;
- `eb_recipe_check`、`eb_recipe_format`、`eb_stack_solve`;
- `eb_target_list`、`eb_target_doctor`;
- `eb_campaign_run`、`eb_campaign_status`;
- `eb_campaign_finding_claim`、`eb_campaign_finding_resolve`。
## 技能
- [新软件包](skills/new-package/SKILL.md):conda-forge/Spack -> profiles -> bundle -> Hermes/OMP 活动。
- [年度升级](skills/annual-bump/SKILL.md):EasyBuild 配方 -> SBOM + Resolvo 升级捆绑包 -> 活动。
- [上游 PR](skills/upstream-pr/SKILL.md):配方集 -> 维护者可接受的 easybuild-easyconfigs PR,从真实合并和拒绝的 PR 中提炼而来。
公开的议题和 PR 界面属于人类操作者。这些技能生成配方集和证据;它们不会打开或修改上游议题或 PR。
## 测试
```
cargo test --locked --all-targets
```
该测试套件涵盖了外部语法适配器、条件、CycloneDX 生成、配置物化、堆栈策略回退、感知层级的 Resolvo 锁定、变体生成、已知升级重现、目标路由、持久化活动、二进制验证、发现所有权、CLI 和 MCP。
## 许可证
[MIT](LICENSE) · [行为准则](CODE_OF_CONDUCT.md) · [安全性](SECURITY.md) · [贡献](CONTRIBUTING.md)标签:CycloneDX, EasyBuild, Python安全, Rust, SBOM, Spack, 依赖解析, 包管理, 可视化界面, 硬件无关, 网络流量审计, 通知系统