You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Python中正确更新__version__?能否通过Git+CI自动管理版本?

太懂这种困惑了——维护好几处硬编码的版本号简直是重复劳动,完全违背了单一数据源(SSOT)的原则!下面我给你详细说说怎么用CI实现Git托管版本的方案,再聊聊为啥很多热门Python包还在沿用老办法。

一、用Git Tag + CI实现版本统一的可行方案

核心思路就是只把Git Tag作为唯一的版本数据源,然后在CI流程里自动把版本号填充到所有需要的地方:

1. 先清理代码里的硬编码版本

  • 把__init__.py里的__version__改成占位符,比如:
    __version__ = "UNKNOWN"
    
  • 移除setup.py/setup.cfg里的硬编码版本,改成从外部读取(后面CI会生成这个外部文件)。

2. 配置CI拉取完整Git历史

大部分CI工具默认只拉取最近一次提交,没法读取所有Tag,所以要设置完整克隆:

  • GitHub Actions里,在actions/checkout步骤加fetch-depth: 0:
    - uses: actions/checkout@v4
      with:
        fetch-depth: 0
    
  • GitLab CI里设置GIT_DEPTH: 0。

3. 在CI中生成合规的版本号

用git describe获取版本信息,再转换成符合PEP 440的格式:

  • 拿到最近的正式Tag(去掉前缀v):
    # 输出示例:1.2.3
    VERSION=$(git describe --tags --abbrev=0 | sed 's/^v//')
    
  • 如果是开发版本(在Tag之后有新提交),生成带开发标识的版本:
    # 输出示例:1.2.3.dev5+gabc123(5是提交数,abc123是哈希前缀)
    FULL_VERSION=$(git describe --tags | sed -E 's/^v(.*)-([0-9]+)-g([0-9a-f]+)$/\1.dev\2+g\3/; s/^v(.*)$/\1/')
    

4. 自动填充所有需要版本的文件

  • 更新__init__.py:用sed或Python脚本替换占位符:
    sed -i "s/__version__ = \"UNKNOWN\"/__version__ = \"$FULL_VERSION\"/" your_package/__init__.py
    
  • 更新打包配置:比如在CI里生成version.txt,让setup.py读取它:
    echo "$FULL_VERSION" > version.txt
    
    然后setup.py里写:
    with open("version.txt", "r") as f:
        version = f.read().strip()
    
    setup(
        name="your-package",
        version=version,
        # 其他配置项
    )
    
  • 自动生成CHANGELOG:用git-chglog这类工具,基于Git Tag和提交记录自动生成CHANGELOG,CI里运行命令后直接作为发布资产或者提交到仓库。

5. 发布环节的注意事项

确保CI的流程是:打Git Tag → 触发CI → 生成版本号 → 替换所有文件 → 构建wheel/sdist → 发布到PyPI,这样发布的包里面版本信息完全正确。

二、为啥大多数热门Python包还在硬编码版本?

其实不是他们不想用SSOT,而是有现实的考量:

  • 兼容性与简单性:很多Python生态的工具(比如依赖扫描、本地调试)默认依赖代码里的硬编码版本号。对于小项目来说,硬编码的维护成本比搭建CI动态版本流程低得多。
  • 离线可用性:如果用户下载了代码压缩包(不是Git克隆),或者克隆时没有拉取Tag,依赖Git的版本方案会显示UNKNOWN,体验很差;硬编码版本则能随时查看。
  • 历史遗留问题:很多热门包起步早,当时CI工具没现在普及,Python打包生态对动态版本的支持也不完善,一旦沿用下来,改动的成本就很高。
  • 避免CI依赖:不同CI平台的Git配置可能有差异,处理PEP 440格式也需要额外脚本,硬编码版本更稳定可靠,不用操心CI环境的问题。
  • PEP 440的严格限制:git describe的原生输出不一定符合PEP 440规范,需要额外处理预发布、开发版本的格式,这增加了复杂度,很多团队觉得没必要。

总的来说,用Git Tag作为单一版本源是非常优雅的方案,适合有成熟CI流程、追求SSOT的项目。如果你走这条路,记得在代码里留个 fallback(比如当无法从Git获取版本时,显示一个默认的开发版本号),避免离线场景下的尴尬。

内容的提问来源于stack exchange,提问作者nowox

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 18:57:45