尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Read the Docs“未来构建器”设计剖析:用 build.jobs 与 build.commands 定制构建流水线,并以 metadata.yaml 契约取代构建期黑魔法

Read the Docs“未来构建器”设计剖析:用 build.jobs 与 build.commands 定制构建流水线,并以 metadata.yaml 契约取代构建期黑魔法 后端文档【免费下载链接】readthedocs.orgThe source code that powers readthedocs.org项目地址https://gitcode.com/gh_mirrors/re/readthedocs.org点击查看免费下载本文以 Read the Docs 仓库中的设计文档 future-builder.rst 为主体完整梳理“未来构建器”Future Builder的提出背景、设计目标、构建步骤划分与metadata.yaml数据契约并结合仓库中已经落地的配置校验与构建调度源码BuildJobs 模型、BuildDirector 调度器、V2 配置校验说明这一设计是如何演变成今天build.jobs/build.commands功能的。读完本文你将掌握Read the Docs 构建流水线的完整步骤划分、build.jobs与build.commands的写法与限制、平台与用户之间的数据契约以及设计稿与当前实现之间的差异。1. 背景构建过程被平台完全掌控的代价设计文档开宗明义地指出了当时的核心矛盾Read the Docs 完全控制构建过程用户只能通过.readthedocs.yaml文件修改非常有限的行为。为了支持个性化需求核心团队不得不在平台侧实现sphinx.fail_on_warning、submodules这类功能implementation and maintenance cost to the core team实现与维护成本高而且即便如此对需要更强控制力的高级用户仍然不够。这一背景可以从当前仓库源码中得到印证BuildDirector.setup_vcs() 负责克隆仓库、检出提交并加载配置文件整个 VCS 流程由平台驱动BuildDirector.setup_environment() 负责安装系统依赖、创建语言环境virtualenv/conda/uv并安装依赖BuildDirector.build() 负责按formats生成 HTML、HTML zip、PDF、ePub 四种产物。设计文档的出发点正是把构建步骤显式化explicit允许用户按需求覆盖其中任意一步而不是为每个需求单独发明一个配置键。文档中也诚实地注明该文档自撰写以来经历了很多变化build.jobs与build.commands已经落地但是在未定义正式契约contract的前提下实现的且与文中的设想存在细微差异——这一点与当前仓库的实际代码状态一致下文会具体对比。2. 设计目标Goals设计文档给出了 15 条明确目标可以归为四个层面兼容性层面保持现有 builder 原样工作Keep the current builder working as-is保持前后向兼容通过中间步骤intermediate steps渐进过渡允许平台以有契约的方式新增功能而不必担心破坏旧构建。用户分层层面为新手、中级、高级用户定义清晰的支持路径允许用户覆盖单条命令、追加 pre/post 钩子命令或完全自定义所有命令提供不落地成配置文件就能传命令参数的途径例如fail_on_warning。架构简化层面消除必须访问构建过程这一 Read the Docs 内部假设把构建期的魔法如向conf.py.tmpl注入扩展翻译成与用户之间的明确契约将readthedocs-sphinx-ext的功能改写为 HTML 后处理功能降低核心团队的维护复杂度Sphinx 由官方负责其他工具交由社区引入build.builder: 2不预装预定义包以承载新特性并通过教育用户推动其从v1迁移到v2最终废弃魔法。3. 三级用户模型与构建步骤划分设计文档把用户分成三级并对应不同的构建控制方式用户级别构建控制方式新手 / 简单使用Read the Docs 控制所有命令即现有 builder中级用户可覆盖一条或多条命令并运行 pre/post 钩子高级用户控制 builder 执行的所有命令据此文档识别出构建器运行的 8 个步骤Checkout检出代码Expose project data via environment variables以环境变量暴露项目数据*Create environment创建 virtualenv / conda 环境Install dependencies安装依赖Build documentation构建文档Generate defined contract生成metadata.yaml契约文件Post-process HTMLHTML 后处理*Upload to storage上传到存储*带\*的步骤由 Read the Docs 托管用户不能覆盖。对照当前源码这一划分基本得到实现并且比设计稿更细BuildJobs 模型 中定义的可覆盖 job 键已经扩展为 14 个class BuildJobs(ConfigBaseModel): Object used for build.jobs key. pre_checkout: list[str] [] post_checkout: list[str] [] pre_system_dependencies: list[str] [] post_system_dependencies: list[str] [] pre_create_environment: list[str] [] create_environment: list[str] | None None post_create_environment: list[str] [] pre_install: list[str] [] install: list[str] | None None post_install: list[str] [] pre_build: list[str] [] build: BuildJobsBuildTypes BuildJobsBuildTypes() post_build: list[str] []与设计稿相比有两个值得注意的演进新增了pre_system_dependencies/post_system_dependencies对应build.apt_packages系统依赖安装这一步设计稿的 8 步里没有单列这一步build子键支持 4 种输出格式BuildJobsBuildTypes 定义了html、pdf、epub、htmlzip四种可覆盖的构建类型而设计稿示例中只列出了html/pdf/epub。同时pre_checkout虽然保留在模型里但在 BuildDirector.setup_vcs() 中是被刻意注释掉不执行的——因为克隆完成前平台还不知道用户写了什么配置设计稿 8 步中 pre 钩子的执行时机问题在实现中被规避了。4. 定义的契约metadata.yaml设计文档提出的核心抽象是数据契约在 Read the Docs 上构建的项目在运行完最后一条命令后必须提供metadata.yaml文件其中包含平台添加自身集成搜索、flyout 菜单、规范链接等所需的全部数据。若该文件缺失或格式错误构建将失败并向用户说明是metadata.yaml出了问题。文档强调平台不限制该文件的生成方式Python 生成、Bash 生成、甚至静态放进仓库都可以平台只在用 Sphinx 构建时自己负责生成它。文档给出的 Sphinx 构建示例如下# metadata.yaml version: 1 tool: name: sphinx version: 3.5.1 builder: html readthedocs: html_output: ./_build/html/ pdf_output: ./_build/pdf/myproject.pdf epub_output: ./_build/pdf/myproject.epub search: enabled: true css_identifier: #search-form input[nameq] analytics: false flyout: false canonical: docs.myproject.com language: en同时文档也明确警告该契约当时尚未最终定义这只是我们期望它能长什么样的示例。4.1 当前实现中的契约演进readthedocs-build.yaml从源码结构看契约概念已经以另一种形式落地BuildDirector.store_readthedocs_build_yaml() 会从 HTML 产物目录读取用户项目生成的readthedocs-build.yaml把数据存入version.build_data最终供/_/readthedocs-config.jsonAPI 端点使用。注意当前实现与当年设计稿的三点差异契约文件名是readthedocs-build.yaml而非metadata.yaml且从构建产物目录_readthedocs/html/读取而非工作区该文件当前是可选的——不存在时只记录 debug 日志并跳过源码中有明确的 TODO决定它是否强制、以及是否校验内容契约 schema 尚未实现# TODO: validate the YAML generated by the user与设计文档contract is not defined yet的警告吻合。这正体现了设计稿逐步引入契约的策略先让机制存在再决定是否强制。5. 配置文件设计build.jobs 与 build.commands设计文档主张所有用户使用同一个配置文件现有.readthedocs.yaml通过新增两个键来实现不同层级的控制build.jobs与build.commands。兼容性规则若配置文件中两者都不存在Read the Docs 会原样执行现有 builder保证所有已正常构建的项目不受影响一旦用户使用了jobs:或commands:构建失败时平台只负责检查metadata.yaml并运行集成注入代码不为其具体命令负责。5.1 build.jobs覆盖单条命令 pre/post 钩子build.jobs允许用户执行一个或多个 pre/post 钩子和/或覆盖一个或多个命令。设计文档列举的典型场景包括给sphinx-build追加额外参数构建前需要先执行一条命令使用个人/私有 PyPI 源以pip install -e方式安装项目本身可编辑安装禁用 git shallow clone给pip install传--constraint约束文件在 install 之前先做某些准备先改文件再安装等用 conda lock 文件创建环境构建完成后运行检查例如sphinx-build -W -b linkcheck . _build/html用--system-site-packages创建 virtualenv 等。设计文档给出的完整示例覆盖全部步骤# .readthedocs.yaml build: builder: 2 jobs: pre_checkout: checkout: git clone --branch main https://github.com/readthedocs/readthedocs.org post_checkout: pre_create_environment: create_environment: python -m virtualenv venv post_create_environment: pre_install: install: pip install -r requirements.txt post_install: pre_build: build: html: sphinx-build -T -j auto -E -b html -d _build/doctrees -D languageen . _build/html pdf: latexmk -r latexmkrc -pdf -f -dvi- -ps- -jobnametest-builds -interactionnonstopmode epub: sphinx -T -j auto -b epub -d _build/doctrees -D languageen . _build/epub post_build: pre_metadata: metadata: ./metadata_sphinx.py post_medatada:文档特别注明所有这些命令都会带着全部已暴露的环境变量执行——这与源码一致BuildDirector.run_build_job() 的 docstring 明确写出用户命令receives same environment variables as regular commands这些变量由 get_rtd_env_vars() 与 get_build_env_vars() 提供如READTHEDOCS、READTHEDOCS_VERSION、READTHEDOCS_PROJECT、READTHEDOCS_OUTPUT、READTHEDOCS_VIRTUALENV_PATH等。只覆盖子集也完全可以。文档强调用户只提供部分 job 时未提供的部分仍走平台默认命令。例如项目需要在构建前先生成 Doxygen XMLBreathe 场景只需# .readthedocs.yaml build: builder: 2 jobs: pre_build: cd ../doxygen; doxygen这个部分覆盖语义在源码中体现得非常清晰——create_environment()、install() 以及 build_html() 等方法都遵循同一模式若config.build.jobs.xxx is not None则执行run_build_job(该job)并跳过默认行为否则走平台默认路径如language_environment.setup_base()、install_core_requirements()等。此外还有两条来自源码、设计稿未完全展开的执行细节pre_checkout/post_checkout两个 job 在VCS 环境克隆容器中运行其余 job 在构建环境中运行且都在仓库克隆目录checkout_path下执行、以非 root 的构建用户身份运行安全约束若项目配置了带写权限的 SSH key 且用户又定义了post_checkout任务checkout() 会直接使构建失败——因为该钩子可能被滥用为向仓库写入内容。5.2 build.commands完全接管构建build.commands让用户对构建过程执行的所有命令拥有完全控制权。适用场景项目的自定义构建流程与平台流程完全对不上does not map ours存在无法/不愿作为通用规则覆盖的特殊需求用 Sphinx 以外的工具构建文档。设计文档示例# .readthedocs.yaml build: builder: 2 commands: - git clone --branch main https://github.com/readthedocs/readthedocs.org - pip install -r requirements.txt - sphinx-build -T -j auto -E -b html -d _build/doctrees -D languageen . _build/html - ./metadata.py当前实现 BuildDirector.run_build_commands() 在其基础上增加了两条平台级保障逐条命令执行后若检测到pip install、conda create/install、poetry install、cargo install等命令会自动执行asdf reshim重新生成 shim保证新安装的版本可被后续命令调用所有命令执行完后会检查约定的 HTML 输出目录是否存在不存在则抛出BUILD_COMMANDS_WITHOUT_OUTPUT构建错误——即使用build.commands的项目必须产出可被平台托管的 HTML 目录这是高级用户完全接管与平台仍需交付托管结果之间的边界。5.3 配置校验规则validate_build_config_with_os() 实现了设计文档中两者皆无则走默认 builder的兼容承诺同时加了几条硬性校验对应 notifications.py 中定义的用户可见错误消息build.os必填build.tools与build.commands至少提供一个否则报NOT_BUILD_TOOLS_OR_COMMANDSAt least one of the following configuration options is required:build.toolsorbuild.commandsbuild.jobs与build.commands不能同时使用BUILD_JOBS_AND_COMMANDS——这与设计文档中二者的定位部分覆盖 vs 完全接管一致每个build.jobs键必须属于 BuildJobs 模型字段 之一build.jobs的值必须是字符串列表build.jobs.build.type中除html外的构建类型pdf/epub/htmlzip必须同时出现在顶层formats中否则报 BUILD_JOBS_BUILD_TYPE_MISSING_IN_FORMATS当用户使用了build.commands时validate_deprecated_implicit_keys() 会跳过对sphinx/mkdocs键的要求完全接管模式下无需声明文档工具而仅覆盖build.jobs时new_jobs_overriden 属性会检查用户是否覆盖了create_environment、install或任一build.*类型从而放宽必须显式声明 sphinx/mkdocs的默认要求。值得注意的是build.builder: 2这一设计稿中的版本开关并未按原样落地当前仓库中build块的必填项是ostools/commands版本化由配置文件的version: 2承担。设计文档自身的 note 也预告了这一点build.jobs and build.commands are already implemented without defining a contract yet, and with small differences from the idea described here。用户面向的正式写法见 config-file/v2.rstbuild.jobs、build.jobs.build、build.commands三节。6. 灰度落地的中间步骤Intermediate steps for rollout设计文档规划了 9 个渐进式迁移步骤核心思想是先把魔法从构建期挪到契约期再开放覆盖能力移除conf.py.tmpl中注入的所有数据迁移到metadata.yaml定义metadata.yaml所需的结构作为契约确定需要暴露的环境变量例如部分来自html_context的变量并让所有命令带着这些变量执行使用该契约构建文档保留readthedocs-sphinx-ext作为conf.py.tmpl中唯一安装/加载的包引入不含任何魔法的build.builder: 2配置构建支撑build.jobs与build.commands的完整基础设施编写新键的使用指南把readthedocs-sphinx-ext的功能改写为 HTML 后处理功能。对照当前仓库可确认其中多数步骤已完成构建命令不再依赖conf.py.tmpl模板注入Sphinx 构建由 doc_builder/backends/sphinx.py 的后端类直接执行环境变量在 get_rtd_env_vars() 中集中暴露build.jobs/build.commands已完整可用用户指南已写入 config-file/v2.rst。而定义 metadata.yaml 契约与改写 readthedocs-sphinx-ext两项仍处于未完成状态readthedocs-build.yaml的校验 TODO 仍在源码中与设计文档 note 中document was merged as-is without a cleaned up的自我描述相符。7. 最终说明Final notes与对新工具接入的影响设计文档结尾给出了一批方向性结论值得与当前实现一并对照v1到v2的迁移要求用户显式声明依赖不再预装预定义包对应当前build.tools必须显式声明且 install_build_tools() 通过 asdf 按用户选定版本安装不打算在v1上支持build.jobs以减少核心团队维护负担平台将能以无需深度集成的方式构建新工具的文档。按文档的设想用新工具在 Read the Docs 上构建只需三件事用户通过覆盖默认命令执行自己的命令集项目/构建产出平台期望的metadata.yaml契约部分或全部平台集成会附加到 HTML 输出这些集成在平台核心中实现。平台不负责其他工具的额外格式PDF、ePub 等重点投入 Sphinx 支持并以工具无关tool-agnostic的方式做好集成以便复用移除conf.py.tmpl操作意味着若未来引入sphinx.yaml之类的文件平台不需要为它实现同等级别的模板操作。从源码结构看这条路线已经产生实际收益build.commands下documentation_type会被置为GENERIC见 run_build_commands 与 config.doctype 的推导逻辑平台对用什么工具构建不再有任何假设——这正是设计文档delegating other tools to the community目标的体现。8. 小结future-builder.rst是 Read the Docs 构建系统从平台黑魔法走向显式契约 用户可控流水线的路线图。其核心主张可归纳为三点步骤显式化把 checkout → 环境 → 安装 → 构建 → 产物处理拆成可命名的 job用户按新手/中级/高级三级逐步获得控制力契约化平台不再假设自己控制全部命令而是要求构建产出可被平台消费的数据契约设计稿中的metadata.yaml当前实现演进为readthedocs-build.yaml渐进式落地build.jobs部分覆盖 钩子与build.commands完全接管二选一、互斥使用配置校验config.py保证两者皆缺时旧行为完全不变。当前仓库 readthedocs/config/models.py、readthedocs/doc_builder/director.py 与 docs/user/config-file/v2.rst 分别提供了该设计的模型定义、运行时实现与用户文档是理解设计稿 → 实现差异job 集合扩充、pre_checkout弃用、契约文件改名且暂未强制的第一手材料。赞分享后端文档【免费下载链接】readthedocs.orgThe source code that powers readthedocs.org项目地址https://gitcode.com/gh_mirrors/re/readthedocs.org点击查看免费下载相关推荐IncusOS性能优化提升容器运行效率的10个实用技巧IncusOS性能优化提升容器运行效率的10个实用技巧 IncusOS作为一款专为运行Incus容器设计的Immutable Linux操作系统其性能优化对Read the Docs 平台源码研读从「Docs as Code」理念到文档构建流水线的实现Read the Docs 平台源码研读从「Docs as Code」理念到文档构建流水线的实现 本文以 Read the Docs 仓库的 README.r后端文档Ray 文档构建告警诊断实战用 sphinx-fix 技能从 Sphinx 警告流定位并修复 Read the Docs 构建失败Ray 文档构建告警诊断实战用 sphinx fix 技能从 Sphinx 警告流定位并修复 Read the Docs 构建失败 导读 Ray 仓库本仓库人工智能分布式训练强化学习任务调度模型推理服务后端上一篇PojavLauncher内存管理终极指南3个步骤优化LargeHeap模式与RAM分配提升游戏流畅度下一篇RuoYi-Cloud微服务权限系统从架构设计到实战部署的完整指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表