Hydra 发布流程实战指南:从版本号维护、towncrier 变更记录到 PyPI 打包上传
【免费下载链接】hydraHydra is a framework for elegantly configuring complex applications项目地址: https://gitcode.com/GitHub_Trending/hyd/hydra
本篇技术指南围绕 Hydra 官方仓库中 1.0 版本的发布流程文档 展开,完整梳理 Hydra 版本发布的核心操作链路:检出发布分支、更新版本号、用 towncrier 生成变更记录、构建 pip 包并上传 PyPI。文章结合仓库源码(版本读取、打包命令、towncrier 配置)逐层深入,帮助维护者或对 Hydra 工程实践感兴趣的读者掌握一套可复制的 Python 开源项目发布方法论,同时了解该流程在后继版本中如何演进为工具化、自动化。
一、文档定位:Hydra 1.0 时代的五步发布流程
version-1.0 发布流程文档 是一份非常精炼的维护者操作手册,全文只有五个步骤,原文如下:
- 检出 master 分支(Checkout master)
- 在
hydra/__init__.py中更新 Hydra 版本号 - 使用 towncrier 更新
NEWS.md - 为 hydra-core 创建 pip 包:
python setup.py sdist bdist_wheel - 上传 pip 包:
python -m twine upload dist/*
文档同时注明"发布流程未来可能自动化"(The release process may be automated in the future)。这份文档的价值在于它完整勾勒了 Python 开源项目手工发布的黄金流程——尽管后续 Hydra 已将其升级为基于tools/release/release.py与 GitHub Actions 的自动化发布(详见 当前发布文档),但"版本号单一来源 + 变更记录自动生成 + 标准打包上传"的核心思想一脉相承。下文将逐步拆解每一步的底层实现与仓库证据。
二、第一步:从干净分支开始(Checkout master)
发布必须基于一个干净、经过充分测试的分支。在 1.0 时代,Hydra 的默认主干分支名为master;在当前的仓库中,CONTRIBUTING.md 已改为要求"从main创建功能分支",主干分支更名为main。
# 获取最新代码(以当前仓库为例) git clone https://gitcode.com/GitHub_Trending/hyd/hydra cd hydra git checkout master # 1.0 时代的主干分支;当前仓库请使用 main git pull --ff-only分支纪律是发布质量的第一道防线:只有通过持续集成验证的提交才应进入发布流程,任何未合并的功能、未完成的重构都不应出现在发布分支上。
三、第二步:更新版本号——hydra/__init__.py是唯一事实来源
文档明确要求更新hydra/__init__.py中的版本号。查看当前仓库的 hydra/init.py:
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved # Source of truth for Hydra's version __version__ = "1.4.0.dev9" from hydra import utils ...文件头部用注释标注了# Source of truth for Hydra's version(Hydra 版本号的唯一事实来源),这正是整个发布流程的锚点。
3.1 setup.py 如何消费这个版本号
setup.py 并不硬编码版本,而是通过build_helpers中的find_version()从__init__.py正则提取:
version=find_version("hydra", "__init__.py"),build_helpers/build_helpers.py 中find_version的实现:
def find_version(*file_paths: str) -> str: with codecs.open(os.path.join(*file_paths), "r") as fp: version_file = fp.read() version_match = re.search(r"^__version__ = '\"['\"]", version_file, re.M) if version_match: return version_match.group(1) raise RuntimeError("Unable to find version string.")这意味着:只改hydra/__init__.py一处,setup.py构建出的包元数据就会自动携带新版本号,彻底避免"源码版本与包版本不一致"的经典事故。这也是"单一事实来源(Single Source of Truth)"原则在发布工程中的典型应用。
3.2 版本号的拼写规范
Hydra 采用 PEP 440 版本语法,例如1.4.0.dev9、1.4.0rc1、1.4.0、1.4.1。当前发布文档进一步明确了发布类型的推导规则:
.devN后缀 → 开发版(dev release)rcN后缀 → 发布候选版(release candidate)x.y.0→ 正式稳定版x.y.z(z>0)→ 补丁版(patch release)
从 hydra/version.py 可以看到仓库对版本字符串的解析正则,用于主/次版本号提取,这从侧面印证了版本字符串在 Hydra 运行时中也被广泛消费。
3.3 插件包的版本管理
hydra-core 之外,仓库中的各插件(如 hydra_optuna_sweeper、hydra_submitit_launcher)各自拥有独立的包与版本号,定义在hydra_plugins/<plugin>/__init__.py中。在协调发布(coordinated release)时,核心与所有插件需要同步提升版本,这一需求后来催生了 release.py 中的action=set_version与set=hydra-full-release机制(见本文第七节)。
四、第三步:用 towncrier 更新 NEWS.md
"Update NEWS.md with towncrier"是文档给出的第二步,其含义是:使用 towncrier 工具把散落在news/目录下的变更片段(news fragment)汇总渲染进NEWS.md。
4.1 towncrier 配置:全部定义在 pyproject.toml
pyproject.toml 中[tool.towncrier]段完整声明了 Hydra 的变更记录管线:
[tool.towncrier] package = "hydra" package_dir = "" filename = "NEWS.md" directory = "news/" title_format = "{version} ({project_date})" template = "news/_template.rst" issue_format = "[#{issue}](https://github.com/hydra-ecosystem/hydra/issues/{issue})" start_string = "<!-- TOWNCRIER -->\n"关键字段说明:
directory = "news/":变更片段存放目录,即仓库根目录下的 news/ 文件夹;filename = "NEWS.md":渲染结果写入的发布说明文件,即仓库根目录的 NEWS.md;title_format = "{version} ({project_date})":每个版本标题的格式,例如1.3.2 (2023-02-22)——这与 NEWS.md 开头"1.3.2 (2023-02-22)"的样式完全吻合,是配置生效的直接证据;template = "news/_template.rst":渲染模板,仓库中的 news/_template.rst 按分类生成 "Features"、"Bug Fixes" 等小节;issue_format:为每条变更自动附加 issue/PR 编号链接。
4.2 news fragment:以编号.类型命名的变更片段
CONTRIBUTING.md 对变更片段有明确规范:所有非平凡的用户可见变更都应添加一个 news fragment,文件名采用<issue或PR编号>.<类型>,例如news/1234.bugfix。仓库 news/ 目录中现存的片段(如1431.bugfix、1781.maintenance、2577.feature、3206.feature等)正是这一约定的实况。
towncrier 支持七种变更类型(在 pyproject.toml 的[[tool.towncrier.type]]中定义):
| 目录名 | 变更类型 |
|---|---|
feature | 新功能 |
api_change | API 变更(重命名、弃用、移除) |
bugfix | 缺陷修复 |
plugin | 插件相关 |
config | 配置结构变更 |
docs | 文档改进 |
maintenance | 维护性改动 |
CONTRIBUTING.md 还规定:一个 PR 可同时创建多个片段(例如同时存在1234.feature与1234.api_change),且跨插件与核心的变更应分别放到对应目录的news/下。渲染时 towncrier 会按类型聚合这些片段并自动去重。
4.3 渲染产物:NEWS.md 的长期累积
NEWS.md 是 towncrier 渲染结果的累积档案,目前已有 700 余行,覆盖从早期到 1.3.x 的全部版本发布说明,例如:
1.3.2 (2023-02-22) ================== ### Features - Add a `hydra.utils.get_object` function ... (#2139)发布时以目标版本号运行 towncrier 后,新的版本章节会被插入<!-- TOWNCRIER -->标记处(即start_string指定的位置),随后在版本提交中一并推送。
五、第四步:构建 pip 包——python setup.py sdist bdist_wheel
文档给出的打包命令是:
python setup.py sdist bdist_wheel该命令一次性生成两类分发包:
sdist:源码分发包dist/hydra_core-<version>.tar.gz;bdist_wheel:构建好的 wheel 包dist/hydra_core-<version>-py3-none-any.whl。
5.1 setup.py 背后的自定义构建命令
Hydra 的打包远非"默认 setuptools 行为"这么简单。setup.py 通过cmdclass注册了五个自定义命令:
cmdclass={ "antlr": ANTLRCommand, "clean": CleanCommand, "sdist": SDistCommand, "build_py": BuildPyCommand, "develop": Develop, },从 build_helpers/build_helpers.py 的实现可以看到构建链路的核心逻辑:
BuildPyCommand(继承build_py.build_py):运行时先执行clean,再运行 antlr 生成解析器,最后执行标准 build;SDistCommand(继承sdist.sdist):同样先clean再运行 antlr;Develop(继承develop.develop):用于pip install -e .开发模式,也需要 antlr 生成;ANTLRCommand:调用java -jar build_helpers/bin/antlr-4.11.1-complete.jar,针对 OverrideLexer.g4 与 OverrideParser.g4 生成 Python3 解析器到hydra/grammar/gen/,随后通过_fix_imports()将生成代码中的import antlr4改写为from omegaconf.vendor import antlr4(使用 OmegaConf 内置的 vendor 版本,避免外部依赖);CleanCommand:清理__pycache__、.egg-info、build、multirun、outputs等发布无关的垃圾文件。
因此,python setup.py sdist bdist_wheel实际执行的是一条"清理 → 生成 ANTLR 语法解析器 → 修复导入 → 构建 sdist 与 wheel"的完整流水线,保证发布产物中语法解析代码与源码始终同步。
5.2 发布产物应包含哪些文件
MENIFEST.in(MANIFEST.in)控制 sdist 中收录的额外文件:
include hydra/py.typed include requirements/requirements.txt global-exclude *.pyc global-exclude __pycache__ recursive-include hydra/* * recursive-include build_helpers *.py *.jar其中hydra/py.typed是 PEP 561 类型标注标记,build_helpers下的*.jar则是 antlr 构建所需。构建完成后应核对dist/目录,确认包名、版本号与预期一致。
六、第五步:上传 PyPI——python -m twine upload dist/*
最后一步将dist/下全部构建产物上传至 PyPI:
python -m twine upload dist/*twine 是 PyPI 官方推荐的包上传工具(相比直接setup.py upload更安全,支持 HTTPS 与凭证管理)。执行前需确认:
- 本机已安装 twine:
pip install twine; - 已配置 PyPI 凭证(
~/.pypirc或环境变量TWINE_USERNAME/TWINE_PASSWORD); - 目标版本在 PyPI 上尚未被占用(重复上传同版本会失败)。
发布完成后应做冒烟验证:在全新虚拟环境中pip install hydra-core==<新版本>,运行一个最简单的@hydra.main应用确认可用,再回到开发分支把版本号推进到下一个开发版本。
6.1 重要演进:1.0 之后"不再本地 twine"
需要特别指出的是,这份 1.0 文档描述的"本地 twine upload"流程在当前仓库中已被废弃。当前 发布文档 明确写道:
Do not run
twine uploadlocally for Hydra releases.(Hydra 发布不得在本地执行 twine upload。)
原因在于发布上传环节已迁移到 GitHub Actions 的 Trusted Publishing(可信发布)机制:维护者只负责准备与校验,真正的 PyPI 上传由 CI 工作流完成,凭证不再暴露在本地环境。这是"文档第一行所说'未来可能自动化'"在仓库中的实际兑现。
七、流程演进:从手工五步到 release.py + GitHub Actions
1.0 文档开头那句 "The release process may be automated in the future" 在后继版本中逐渐成真。当前仓库已经沉淀出一套半自动化发布工具链:
7.1 工具入口:tools/release/release.py
tools/release/release.py 定义了六种发布动作:
class Action(Enum): check = 1 build = 2 bump = 3 set_version = 4 validate_versions = 5 dev_release = 6其中set_version对应手工流程中的"更新版本号",build对应"构建包",check则承担"上传前核对"职责——例如action=check会通过 PyPI JSON API(get_metadata,见 release.py)比较所选包在目标仓库的已发布版本,避免重复发布。
7.2 配置化:包集合(package set)
发布工具的默认配置见 tools/release/conf/config.yaml,其中defaults默认选择set: hydra-full-release;hydra-full-release.yaml 定义了该集合由 hydra-core 加九个捆绑插件组成(hydra_ax_sweeper、hydra_colorlog、hydra_joblib_launcher、hydra_nevergrad_sweeper、hydra_optuna_sweeper、hydra_ray_launcher、hydra_rq_launcher、hydra_submitit_launcher等,对应 conf/packages/ 下的包配置)。
一个典型的发布准备命令:
python tools/release/release.py \ action=check \ set=hydra-full-release \ repository=pypi这实际上是把 1.0 文档中的"人工核对"升级为"程序化核对":校验发布线、推导目标分支、比对 PyPI 已发布版本、运行发布级检查矩阵,但上传动作交给Publish to PyPI工作流通过 Trusted Publishing 完成。
7.3 与 1.0 流程的对照
| 1.0 手工流程(本文档) | 当前工具化流程 |
|---|---|
git checkout master | 按发布线(main/x.y_branch)由工具推导目标分支 |
手工改hydra/__init__.py版本号 | action=set_version批量设置核心与插件版本 |
| 手工跑 towncrier 更新 NEWS.md | 稳定版发布时仍由 towncrier 生成 NEWS.md |
python setup.py sdist bdist_wheel | action=build(带clean_build_dir、require_artifacts、build_policy等策略) |
python -m twine upload dist/* | GitHub ActionsPublish to PyPI+ Trusted Publishing,禁止本地 twine |
八、发布前自检清单
将 1.0 文档的五个步骤与仓库现状结合,可沉淀为一份通用发布检查清单:
- 分支:确认在干净的主干/发布分支上,工作树无未提交改动;
- 版本号:
hydra/__init__.py中__version__为预期的 PEP 440 版本;插件协调发布时同步各插件版本; - 变更记录:
news/目录已按<编号>.<类型>添加全部片段,运行 towncrier 渲染后确认NEWS.md包含本版本全部条目(可参考 news/_template.rst 与 NEWS.md 的既有格式); - 构建:
python setup.py sdist bdist_wheel成功,antlr 解析器正常生成,dist/下 sdist 与 wheel 齐全; - 上传:1.0 流程为
python -m twine upload dist/*;当前仓库则交由 GitHub Actions 发布,绝不本地 twine upload; - 收尾:发布成功后立即将版本号推进到下一个开发版本(如
1.4.0.dev3→1.4.0.dev4),保证开发分支报告的版本永远"尚未发布"。
这套流程虽然源自 Hydra 1.0 时代的手工操作文档,但其"单一版本来源、变更记录工具化、标准构建产物、受控上传"四要素,对任何想要规范发布 Python 包的开源项目维护者都具有直接的参考价值;而仓库后续的 release.py 与 当前发布文档 则展示了同一流程在规模化、多包场景下如何优雅演进。
【免费下载链接】hydraHydra is a framework for elegantly configuring complex applications项目地址: https://gitcode.com/GitHub_Trending/hyd/hydra
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考