扩展 Spack CI 生成器:从 `spack ci generate` 到自定义 Pipeline 平台生成器
发布时间:2026/9/18 3:35:46 作者:尧图编辑部 阅读量:1,286

扩展 Spack CI 生成器从spack ci generate到自定义 Pipeline 平台生成器【免费下载链接】spackA flexible package manager that supports multiple versions, configurations, platforms, and compilers.项目地址: https://gitcode.com/GitHub_Trending/sp/spackSpack 的ci模块负责把支持 CI 的 Spack 环境转换为可供 CI 平台消费的流水线文件pipeline。本文以仓库中 lib/spack/spack/ci/README.md 为骨架结合 ci/init.py、ci/common.py、ci/generator_registry.py、ci/gitlab.py 以及 cmd/ci.py 与 test/cmd/ci.py 等源码完整讲解流水线生成的内部架构并给出为 GitLab、GitHub Actions 等新平台注册自定义生成器的具体步骤。读完本文你将掌握spack ci generate的完整调用链、PipelineDag/SpackCIConfig/PipelineOptions三大核心对象的分工以及如何用generator装饰器写出可被 Spack 识别的新生成器。一、ci 模块的整体定位Spack 的 CI 支持并不是把某个平台如 GitLab的配置写死而是采用通用图 平台生成器的两层架构通用层platform-agnostic负责构建流水线图一个 DAG 森林、以多种方式修剪prune该图并从 Spack 配置中收集所有构建作业的属性。这些功能统一实现在ci包的顶层模块__init__.py中无论目标平台是哪种这部分逻辑都基本一致。平台特定层platform-specific针对具体 CI 平台的逻辑GitLab、GitHub Actions 等放在独立模块中通过统一的生成器接口注册进来。目前仓库内ci目录包含 4 个文件__init__.py通用流水线功能与generate_pipeline入口common.pyPipelineDag、PipelineOptions、PipelineType、SpackCIConfig、CDashHandler等公共数据结构generator_registry.py生成器注册表与generator装饰器gitlab.py目前唯一的正式生成器generate_gitlab_yaml。从源码看README 所说当前模块只有 gitlab 一个生成器在 ci/gitlab.py 中得到了印证generator(gitlab)装饰的generate_gitlab_yaml是唯一注册进生产代码的生成器另外在单元测试 test/cmd/ci.py 中还定义了一个仅供测试使用的unittestgenerator用于验证注册与调用机制。二、整体生成流程从环境到流水线文件README 明确指出生成一条流水线的过程是创建一个支持 CI 的 Spack 环境 → 激活它 → 运行spack ci generate可附带--output-file等参数指定输出位置。对应到命令行实现cmd/ci.py 中的ci_generate函数会通过spack.cmd.require_active_env强制要求一个已激活的环境调用spack_ci.generate_pipeline(env, args)完成流水线生成。spack ci generate的核心参数定义于 cmd/ci.py 附近包括参数默认值作用--output-file.gitlab-ci.yml仓库根目录生成的流水线 YAML 输出路径--prune-dag/--no-prune-dag开启是否跳过在镜像上已是最新的 spec 作业--prune-unaffected/--no-prune-unaffected关闭是否跳过与 git 变更无关的 spec 作业--prune-externals/--no-prune-externals开启是否跳过被标记为 external 的 spec 作业--check-index-only关闭仅依据 buildcache 索引判断 spec 状态而不逐个拉取 spec 文件此外若要向 CDash 上报构建结果需要预先设置SPACK_CDASH_AUTH_TOKEN环境变量参见ci_generate的 docstring。生成后的流水线文件通过 ci/gitlab.py 写出先调用syaml.anchorify(sorted_output)利用 YAML 锚点压缩输出体积再以 ruamel.yaml 序列化到options.output_file。三、通用流水线功能三大核心对象通用层的数据结构全部集中在 ci/common.py 中它们是自定义生成器必须打交道的对象。3.1 PipelineDag将 spec 列表转化为无向类型的有向图PipelineDag 把一组 spec 变成一棵不区分边类型的简单有向图README 称之为森林即多棵树的集合节点以dag_hash()为 key见PipelineDag.keyPipelineNode同时记录parents与children集合建图构造时用traverse.traverse_nodes(..., deptypedt.ALL_TYPES, rootTrue)收集全部节点再用traverse.traverse_edges建立父子边prune(key)删除某个节点并把它与父、子节点的边重新连接——这是DAG 修剪的核心操作traverse_nodes(direction)拓扑序遍历directionchildren从根到叶、directionparents从叶到根产出(depth, node)其中depth是从起点到该节点的最长路径长度get_dependencies(node)返回节点的直接依赖列表即子节点。3.2 SpackCIConfig把配置转换为生成器可用的中间表示IRSpackCIConfig 读取环境ci配置节生成一份平台无关的中间表示IR供生成器消费预定义了一组命名作业any、build、copy、cleanup、noop、reindex、signingIR 顶部包含rebuild-index、broken-specs-url、broken-tests-packages、target等全局信息其中target默认是gitlab对应生成器注册名对每个 release spec 初始化作业对象并写入SPACK_JOB_SPEC_DAG_HASH、SPACK_JOB_SPEC_PKG_NAME、SPACK_JOB_SPEC_PKG_VERSION、SPACK_JOB_SPEC_COMPILER_NAME、SPACK_JOB_SPEC_COMPILER_VERSION、SPACK_JOB_SPEC_ARCH、SPACK_JOB_SPEC_VARIANTS等变量这些变量正是下游spack ci rebuild作业依赖的输入generate_ir()负责把ci配置中的作业定义包括 dynamic-mapping 等高级匹配规则折叠成最终的 IR 字典。3.3 PipelineOptions所有选项的统一容器PipelineOptions 汇总了可以通过命令行、配置/YAML 或环境变量指定的全部选项包括env激活的 Spack 环境buildcache_destination二进制包推送的目标镜像artifacts_root产物存放根目录默认jobs_scratch_diroutput_file输出文件路径check_index_only是否只查询 buildcache 索引prune_untouched/prune_up_to_date/prune_unaffected/prune_external四类修剪开关pipeline_type流水线类型见下节require_signing是否强制要求 buildcache 签名cdash_handler与 CDash 通信的处理器forward_variables需要从当前环境透传给下游作业的环境变量名列表。3.4 PipelineType流水线运行场景PipelineType 是一个枚举同时提供驼峰与蛇形两种别名COPY_ONLYspack_copy_only仅做 buildcache 拷贝的流水线PROTECTED_BRANCHspack_protected_branch受保护分支如 develop流水线PULL_REQUESTspack_pull_requestPull Request 流水线。流水线类型会直接影响生成器行为。例如在 gitlab 生成器中public与protected是 Spack 保留标签PR 流水线的作业会被打上public标签受保护分支流水线的作业则被打上protected标签ci/gitlab.py从而把作业调度到不同类型的 runner 上。四、平台特定功能generator注册机制平台相关逻辑必须放到独立模块中。要为一个新平台定义生成器README 给出了明确的四项要求下面逐条结合源码展开。要求 1在ci下新增文件定义被generator装饰的生成器函数注册机制的实现在 ci/generator_registry.py_generators是一个以平台名为 key、生成器函数为 value 的模块级字典generator(name)装饰器负责把函数注册进_generators[name]get_generator(name)按名取出生成器若不存在则抛出UnknownGeneratorException继承自SpackError报错信息为No registered generator for name。GitLab 生成器就是一个标准范例ci/gitlab.pygenerator(gitlab) def generate_gitlab_yaml(pipeline: PipelineDag, spack_ci: SpackCIConfig, options: PipelineOptions): ...要求 2在ci/__init__.py中 import 新模块使生成器完成注册这一点在 ci/init.py 中有明确注释# Import any modules with generator functions from here, so they get # registered without introducing any import cycles. from .gitlab import generate_gitlab_yaml # noqa: F401之所以强调在__init__.py中导入是因为 import 这个副作用执行generator(...)装饰会在包加载时发生从而把生成器注册进generator_registry._generators同时包加载顺序也能避免循环导入问题。如果你的新模块是ci/gha.py就在同样位置补一行from .gha import generate_gha_yaml。要求 3生成器函数签名必须是(PipelineDag, SpackCIConfig, PipelineOptions)按此顺序这是生成器接口的契约。三个参数的含义分别是pipeline已经过修剪的流水线图代表所有需要构建的 specspack_ci包含流水线中所有作业配置属性的SpackCIConfig对象options从 yaml、环境、命令行聚合而来的全部选项。测试模块中的自定义生成器 test/cmd/ci.py 是符合该契约的最小实现它遍历pipeline.traverse_nodes(directionchildren)把每个 spec 名字写入输出文件generator(unittestgenerator) def generate_unittest_pipeline( pipeline: PipelineDag, spack_ci: SpackCIConfig, options: PipelineOptions ): Define a custom pipeline generator for the target unittestgenerator. output_file options.output_file assert output_file is not None with open(output_file, w, encodingutf-8) as fd: fd.write(unittestpipeline\n) for _, node in pipeline.traverse_nodes(directionchildren): release_spec node.spec fd.write(f {release_spec.name}\n)要求 4生成器必须产出一个包含生成流水线的输出文件输出文件路径由options.output_file给出。gitlab 生成器在输出路径未指定时默认写入仓库根目录的.gitlab-ci.yml并会在需要时自动创建目标目录ci/gitlab.py。五、源码级剖析gitlab 生成器做了什么以generate_gitlab_yaml为参照可以完整看到通用图 → 平台 YAML的转换过程解析目录与产物布局以CI_PROJECT_DIR缺省为当前工作目录为基准将artifacts_root归一为相对路径随后创建concrete_environment、logs、reproduction、tests、user_data等产物目录固化环境把spack.yaml写入concrete_environment/spack.yaml并重写其中的相对 include 路径使其相对新目录有效同时把spack.lock一并复制保证下游作业可以精确复现环境生成中间表示调用spack_ci.generate_ir()获得作业 IR按拓扑级排布阶段对非 COPY_ONLY 流水线用pipeline.traverse_nodes(directionparents)按层级生成stage-0、stage-1… 的阶段名为每个 spec 构造needs依赖作业 生成作业自身并注入SPACK_SPEC_NEEDS_REBUILD等变量作业增强为每个作业合并 artifacts 路径when: always、retry默认max: 2失败条件集合见 ci/gitlab.py 的JOB_RETRY_CONDITIONS以及interruptible: True服务作业按需生成copyCOPY_ONLY 流水线、sign-pkgs受保护分支且有外部签名脚本时、wait-for-build-jobsnoop 作业充当 reindex 前的屏障、rebuild-index重建 buildcache 索引等作业流水线级变量写出SPACK_ARTIFACTS_ROOT、SPACK_CONCRETE_ENV_DIR、SPACK_VERSION、SPACK_CHECKOUT_VERSION、SPACK_PIPELINE_TYPE、SPACK_REBUILD_CHECK_UP_TO_DATE、SPACK_REQUIRE_SIGNING等全局变量供下游spack ci rebuild作业消费兜底逻辑若没有生成任何构建作业则输出一个no-specs-to-rebuildnoop 作业并通过workflow: rules: [{when: always}]保证子流水线总能运行。这套产出物正是spack ci rebuildcmd/ci.py所依赖的它从环境变量读取SPACK_ARTIFACTS_ROOT、SPACK_JOB_LOG_DIR、SPACK_CONCRETE_ENV_DIR、SPACK_JOB_SPEC_DAG_HASH等判断 spec 是否已在镜像上再决定是否从源码重建。六、测试如何验证生成器机制ci模块的单元测试test/cmd/ci.py直接印证了 README 中单测定义了一个小的自定义生成器的说法并覆盖了注册机制的边界情况注册与调用test_ci_generate系列测试会调用ci_cmd(generate, --output-file, ...)验证生成器按PipelineDag→SpackCIConfig→PipelineOptions的顺序被正确驱动未知生成器报错test_ci_generate_unknown_generatortest/cmd/ci.py专门验证未识别的 ci target 能被检测出来并给出清晰可操作的错误信息——这正是generator_registry.get_generator抛出UnknownGeneratorException的场景动态映射测试同时覆盖了ci配置中dynamic-mapping结合远程 endpoint、require/allow/ignore字段对作业属性的注入行为。七、实践为你的平台编写生成器综合 README 与源码落地一个新平台生成器的完整步骤可以归纳为在lib/spack/spack/ci/下新建模块例如gha.py定义一个函数from .common import PipelineDag, PipelineOptions, SpackCIConfig from .generator_registry import generator generator(gha) def generate_gha_yaml(pipeline: PipelineDag, spack_ci: SpackCIConfig, options: PipelineOptions): output_file options.output_file # 遍历 pipeline、读取 spack_ci.generate_ir() 与 options # 输出平台可消费的流水线文件到 output_file ...在 ci/init.py 顶部导入该模块如from .gha import generate_gha_yaml # noqa: F401确保包加载时完成注册在环境的ci配置节中把target设置为你的生成器名默认是gitlab见 ci/common.py 中 IR 的target: self.ci_config.get(target, gitlab)随后激活环境并运行spack ci generate --output-file 路径若平台名写错spack ci generate会以No registered generator for name的方式立即报错由 generator_registry.py 触发。需要说明的是虽然spack_ci.generate_ir()已经产出平台无关的 IR但每个平台对阶段、依赖如 GitLab 的needs、标签、产物与重试语义的要求各不相同这些平台细节仍需在生成器内部自行处理——这正是 README 把通用功能与平台特定功能分开设计的根本原因。【免费下载链接】spackA flexible package manager that supports multiple versions, configurations, platforms, and compilers.项目地址: https://gitcode.com/GitHub_Trending/sp/spack创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考