首页 / 资讯中心 / 文章详情

Read the Docs 的 readthedocs.yaml 配置文件:从设计文档到源码实现的完整解析

Read the Docs 的 readthedocs.yaml 配置文件:从设计文档到源码实现的完整解析 ★ FEATURED ARTICLE
后端文档【免费下载链接】readthedocs.orgThe source code that powers readthedocs.org项目地址https://gitcode.com/gh_mirrors/re/readthedocs.org点击查看免费下载Read the DocsRTD通过仓库根目录下的readthedocs.yaml文件将文档构建的关键决策权交还给项目维护者。本文以仓库中的设计文档 yaml-file.rst 为主线完整还原该配置文件的设计目标、设置归属划分、格式约定与版本策略并结合 readthedocs/config/ 下的解析与校验源码说明每一项设计决策在当前代码中是如何落地的——读完后你能够准确判断哪些项目设置该写进 YAML、哪些必须留在 Web 界面并理解一次构建中配置文件从查找、解析到参与构建的完整调用链。一、设计背景与目标设计文档开篇指出YAML 配置文件当时处于 beta 状态尚未支持 RTD 的全部选项该文档的定位就是讨论如何实现缺失特性的设计说明。其范围Scope包含六个目标值得逐条对照当前实现来看补全规范spec覆盖所有缺失选项保持规范内部的命名与语义一致性为最终用户提供完整的配置文档允许在 YAML 文件中显式声明所使用的规范版本收集/展示关于 YAML 文件与构建配置的元数据推动配置文件的采用adoption。其中“收集/展示构建元数据”与“推动采用”两条在今天的代码里都有清晰对应构建完成后配置对象会被序列化为字典存入构建记录见下文第七节而设计文档中“建议最小可用配置”的采用策略也已在产品层面落地。二、设置的归属什么能写进 YAML什么不能这是设计文档最核心的决策框架。RTD 的设置被划分为三类划分依据是设置是否依赖项目初始状态、是否计划移除、以及安全与隐私约束。2.1 不适用 YAML 文件的设置设计文档明确列出了不能放入 YAML 的设置理由是它们可能依赖项目初始设置、计划被移除、或涉及安全与隐私Project Name项目名称Repo URL仓库地址Repo type仓库类型Privacy level隐私级别当时计划移除Project description项目描述当时计划移除Single version单版本项目Default branch默认分支Default version默认版本Domains自定义域名Active versions活跃版本Translations翻译Subprojects子项目Integrations集成Notifications通知Language文档语言Programming Language编程语言Project homepage项目主页Tags标签Analytics code分析代码Global redirects全局重定向2.2 全局设置只存数据库为了与“按版本per-version设置”保持一致、避免混淆设计文档决定全局设置不存入 YAML 文件只存数据库。这解释了为什么上述域名、子项目、全局重定向等跨版本生效的属性始终只能在 Web 界面管理。2.3 版本级本地设置由 YAML 提供这类配置在构建某个版本时从当前版本的 YAML 文件中读取。设计文档列出当时已实现的部分文档类型Documentation type项目安装方式虚拟环境、requirements 文件、Sphinx 配置文件等附加构建格式pdf、epub 等Python 解释器版本按版本的重定向Per-version redirects对照当前源码前四项已有完整实现映射关系如下设计文档中的本地设置当前源码中的承载文档类型BuildConfigV2.doctype属性sphinx构建器 /mkdocs/ 自定义命令时为GENERIC见 config.py项目安装python.installpip / setuptools / uv / requirements与conda.environment见 config.py 的validate_python附加构建格式formats键合法值为htmlzip、pdf、epub或all关键字见 config.pyPython 解释器build.tools.pythonpython_interpreter属性据此推断解释器类型python / conda / mamba见 config.py按版本重定向从源码结构看当前BuildConfigV2的校验逻辑中不存在redirects键未知键会被validate_keys拒绝版本级重定向实际由 readthedocs/redirects/models.py 中的数据库模型管理未纳入 YAML 规范三、文件格式文件名、位置与书写约定3.1 文件命名与查找规则设计文档规定文件基于 YAML 1.2 规范编写必须位于仓库根目录且文件名为以下四种之一readthedocs.ymlreadthedocs.yaml.readthedocs.yml.readthedocs.yaml这一规则在源码中体现为一条正则与一个查找函数。config.py 定义了文件名匹配模式CONFIG_FILENAME_REGEX r^\.?readthedocs.ya?ml$find.py 中的find_one遍历指定目录返回第一个匹配该正则的文件def find_one(path, filename_regex): Find the first file in path that match filename_regex regex. _path os.path.abspath(path) for filename in os.path.listdir(_path) if False else os.listdir(_path): if re.match(filename_regex, filename): return os.path.join(_path, filename) return 实际代码为os.listdir上文仅示意循环逻辑。load 函数 在默认路径下调用find_one找不到文件时抛出ConfigError.DEFAULT_PATH_NOT_FOUND错误 IDconfig:path:default-not-found与设计文档“文件必须在根目录”的约定一致。此外加载时通过safe_open允许符号链接但限定链接目标必须解析在仓库目录内部防止路径逃逸。3.2 书写约定设计文档强制规范使用以下四条约定它们在实现中均有严格对应空列表用[]表示 —— 对应各validate_*列表校验的默认值如formats默认[]、build.commands默认[]见 config.py 的self.pop_config(formats, [])空值用null表示 —— 对应校验中None值的显式判断如conda.environment缺失、sphinx.configuration未提供时的分支处理全选用内部字符串关键字all表示 —— 源码中定义为常量ALL allconfig.py用于formats: all、submodules.include: all以及 uv 安装的groups: all/extras: all布尔字段只接受true/false—— 由 validation.py 的validate_bool统一校验如sphinx.fail_on_warning、submodules.recursive、mkdocs.fail_on_warning。四、规范Spec的编写方式与版本策略4.1 用校验模式描述规范设计文档提出规范应以**校验模式validation schema**的形式编写使“规范即代码”而不是停留在自然语言文档上。当前实现采用两层校验结构来落实这一点解析层parser.py 用yaml.safe_load安全解析且要求顶层必须是非空 mapping否则抛出ParseErrorconfig yaml.safe_load(stream) if not isinstance(config, dict): raise ParseError(Expected mapping) if not config: raise ParseError(Empty config)模型层models.py 用 Pydantic 模型描述每个配置键的合法结构基类ConfigBaseModel显式禁止多余字段class ConfigBaseModel(BaseModel): model_config ConfigDict( # Dont allow extra fields in the models. # It will raise an error if there are extra fields. extraforbid, )例如Sphinx模型固定了builder的合法取值与默认值models.pyclass Sphinx(ConfigBaseModel): configuration: str | None builder: Literal[sphinx, sphinx_htmldir, sphinx_singlehtml] sphinx fail_on_warning: bool False未知键拒绝所有合法键被逐个pop之后validate_keysconfig.py会检查raw_config中是否还残留任何键一旦发现即抛出ConfigError.INVALID_KEY_NAME。这使“规范之外的写法”在构建前就明确失败而不是被静默忽略。4.2 规范的版本化设计文档的版本策略有两条原则规范只使用主版本号如 1.0而不是 1.2用户在 YAML 文件的version键中声明要使用的版本为了兼容未写version的旧项目缺省使用“最新的兼容版本”文档撰写时为 1.0。当前代码记录了这一策略的演进config.py 中LATEST_CONFIGURATION_VERSION 2而 load 函数 的校验逻辑显示version缺省时按 2 处理显式写出则必须是2或字符串2否则抛出INVALID_VERSIONconfig:base:invalid-versionBuildConfigV2 的version属性固定为2且构建入口会直接拒绝 v1 配置见下文构建流程。也就是说从 1.x 到 2 的演进遵循了“只升主版本”的设计约束且旧版本已被强制废弃。五、构建流程中的配置文件设计文档给出的构建流程共七步当前实现与之一一对应关键代码位于 doc_builder/config.py 与 doc_builder/director.py更新仓库——BuildDirector.setup_vcs克隆仓库director.py并执行post_checkout构建任务检出当前版本—— 在 VCS 环境中checkout到目标版本从数据库获取设置—— Celery 任务在before_start阶段收集项目数据TaskData传入构建器解析 YAML 文件出错即构建失败—— load_yaml_config 调用readthedocs.config.load任何ParseError都会被包装为带语法错误详情的ConfigError.SYNTAX_INVALIDconfig:base:invalid-syntax合并 YAML 与数据库设置—— 配置对象完成后写入构建上下文director.pyself.data.config load_yaml_config( versionself.data.version, readthedocs_yaml_pathcustom_config_file, ) self.data.build[config] self.data.config.as_dict()按设置构建版本—— 后续setup_python_environment、system_dependencies安装build.apt_packages等环节全部消费该配置对象用户可查看构建所用设置—— 正是上一步self.data.build[config] self.data.config.as_dict()落库的结果as_dict()config.py按PUBLIC_ATTRIBUTESversion、formats、python、conda、build、doctype、sphinx、mkdocs、submodules、search导出使构建记录中持久化一份“实际生效的配置”元数据实现了设计文档中“Collect/show metadata”的目标。此外构建入口还有三道硬性门禁director.py体现了规范演进期的兼容策略version不是2v1 配置时抛出NO_CONFIG_FILE_DEPRECATED构建错误仍在使用已被移除的build.image键时抛出BUILD_IMAGE_CONFIG_KEY_DEPRECATED未声明build.os时抛出BUILD_OS_REQUIRED——即从 v2 起构建系统镜像必须由配置文件显式指定不再有隐式默认值。设计文档还提到一个安全细节如果项目配置了具有写权限的 SSH key且用户配置了build.jobs.post_checkout构建会被直接终止director.py 的SSH_KEY_WITH_WRITE_ACCESS错误防止构建任务被滥用为向仓库写回内容的手段。六、校验规则速览v2 规范的关键约束BuildConfigV2.validate()config.py按固定顺序执行各键的校验顺序本身也有讲究——validate_build必须先于validate_python/validate_conda后者依赖前者确定的build属性validate_doc_types必须先于 sphinx/mkdocs 校验self._config[formats] self.validate_formats() self._config[build] self.validate_build() self._config[conda] self.validate_conda() self._config[python] self.validate_python() self.validate_doc_types() self._config[mkdocs] self.validate_mkdocs() self._config[sphinx] self.validate_sphinx() self._config[submodules] self.validate_submodules() self._config[search] self.validate_search() if self.deprecate_implicit_keys: self.validate_deprecated_implicit_keys() self.validate_keys()结合各校验方法可归纳出 v2 规范中最容易踩坑的约束buildbuild.os必填且必须匹配构建镜像设置中的可用值build.tools的每个工具名与版本同样受设置表约束build.commands与build.jobs二选一同时出现会抛出BUILD_JOBS_AND_COMMANDS两者全空则抛出NOT_BUILD_TOOLS_OR_COMMANDSconfig.pybuild.jobs.build只允许html、pdf、epub、htmlzip四类任务对应BuildJobsBuildTypesmodels.py且除html外的构建类型必须已在formats中声明否则抛出BUILD_JOBS_BUILD_TYPE_MISSING_IN_FORMATSapt_packages包名经过白名单正则^[a-zA-Z0-9][a-zA-Z0-9.-]*$校验且禁止以-、/、.开头防止注入 apt 选项或从本地路径安装包config.pypython.install每个条目必须是三选一——method: uv此时command必填取值为sync或pip且sync不允许requirements、pip不允许groupsrequirements与path互斥requirements: path或path: pathmethod取pip或setuptoolsextra_requirements仅 pip 可用。使用 uv 时整个python.install列表只允许一条 uv 条目config.pysphinxsphinx与mkdocs不能同时出现SPHINX_MKDOCS_CONFIG_TOGETHERsphinx.builder支持html、htmldir、dirhtml、singlehtml并映射为内部构建器标识config.pysphinx.configuration若指定其文件名必须是conf.py否则抛出SPHINX_INVALID_CONFIG_FILEsubmodulesinclude与exclude不能同时使用SUBMODULES_INCLUDE_EXCLUDE_TOGETHER二者均支持all关键字searchranking是“路径模式 → 排名”的映射排名为 -10 到 10 的整数ignore默认为search.html、search/index.html、404.html、404/index.htmlconfig.py隐式键弃用deprecate_implicit_keys开启后由部署侧开关与固定时间窗口控制config.pysphinx/mkdocs键一旦使用就必须提供configuration路径且既未使用build.commands也未覆盖新构建任务时显式的sphinx键成为必需——这反映了平台逐步移除“按文件名猜测文档类型”隐式行为的迁移策略。配套的解析与校验测试位于 readthedocs/config/tests/ 目录覆盖了文件名查找、各键校验与错误消息等场景。七、配置文件与数据库的关系设计文档对二者关系的界定非常克制构建时从配置文件读取的设置连同其他元数据需要存入数据库但仅用于事后查阅不会回填populate到现有的项目字段。当前实现精确遵循了这一边界。doc_builder/config.py 的注释明确写道# TODO: review this function since we are removing all the defaults for BuildConfigV2 as well. # NOTE: all the configuration done on the UI will make no effect at all from now on.即Web 界面中遗留的构建类设置已完全不再生效YAML 文件成为唯一的构建配置来源而界面中不可由 YAML 表达的部分域名、子项目、全局重定向等第二节所列设置仍保存在数据库中。两个系统各司其职数据库负责全局、跨版本与账号安全相关设置YAML 负责版本级的构建参数构建记录中再落一份as_dict()快照供用户复核。八、推动配置文件的采用设计文档最后给出了面向用户的推广思路可作为理解该产品决策的注脚用户新建项目或进入设置页时平台可提示一份“最小可用配置”示例并说明哪些全局配置应放在界面而非文件中对于已有项目可以基于其当前设置在每次构建时向用户提示一份等价的内容配置文件。结合当前源码看这一采用策略的终点已经达成v1 配置被构建入口直接拒绝NO_CONFIG_FILE_DEPRECATED、build.os强制显式声明、UI 构建设置失效配置文件的地位从“可选的 beta 特性”转变为构建的准入门槛。九、一个符合 v2 规范的完整示例综合以上约定与校验规则一份最小而完整的readthedocs.yaml置于仓库根目录大致如下# 文件名可以是 readthedocs.yml / readthedocs.yaml / .readthedocs.yml / .readthedocs.yaml version: 2 # 规范版本目前只接受 2 build: os: ubuntu-22.04 # 必填取值必须匹配平台构建镜像设置中的可用 os 键 tools: python: 3.12 # 可用工具与版本以构建镜像设置RTD_DOCKER_BUILD_SETTINGS为准 apt_packages: [] # 可选包名受白名单正则约束禁止 - / . 开头 sphinx: configuration: docs/conf.py # 若指定文件名必须为 conf.py使用 sphinx 键时该项为必填 builder: html # 可选html / htmldir / dirhtml / singlehtml fail_on_warning: false # 可选只接受 true / false formats: [] # 可选htmlzip / pdf / epub或 all[] 表示仅构建 HTML python: install: - requirements: docs/requirements.txt # 三种写法之一requirements 文件 / path method / uv提交前可对照 readthedocs/config/exceptions.py 中的错误 ID 排查构建日志config:path:default-not-found找不到配置文件、config:base:invalid-syntaxYAML 语法错误、config:base:invalid-version版本不是 2、config:base:invalid-key出现规范之外的键等几乎能覆盖设计文档中“解析出错即构建失败”的所有失败路径。小结回顾 yaml-file.rst 的设计脉络设置的三级归属不适用 / 全局存库 / 版本级入 YAML划清了文件与界面的边界四条书写约定[]、null、all、布尔保证了规范的表达一致性基于校验模式的 spec 与只升主版本的策略让配置规范可以像库一样演进而**“解析失败即构建失败 配置快照入库”** 则把可预测性落到了每一次构建上。当前代码库 readthedocs/config/ 与 readthedocs/doc_builder/director.py 就是这份设计文档的落地形态——阅读实现时不妨把设计文档中的每一条 Scope 当作验收清单来对照。赞分享后端文档【免费下载链接】readthedocs.orgThe source code that powers readthedocs.org项目地址https://gitcode.com/gh_mirrors/re/readthedocs.org点击查看免费下载相关推荐Read the Docs Pull Request 构建器设计从设计文档到 external version 的完整落地Read the Docs Pull Request 构建器设计从设计文档到 external version 的完整落地 Read the Docs 的「P后端文档Read the Docs 文档 URL 解析设计从保留路径问题到 unresolver 的查找实现Read the Docs 文档 URL 解析设计从保留路径问题到 unresolver 的查找实现 本文围绕 Read the Docs 仓库中的设计文档后端文档Read the Docs 构建系统中的 build.apt_packages从设计文档到源码级的系统包安装实现Read the Docs 构建系统中的 build.apt_packages从设计文档到源码级的系统包安装实现 在 Read the Docs 的构建流水线后端文档上一篇MallChat数据库设计最佳实践电商IM系统的数据建模终极指南下一篇git-pissed 项目使用教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
阅读完成 · 觉得有帮助?
咨询建站