【问题标题】:Automatic version number both in setup.py (setuptools) AND source code?setup.py(setuptools)和源代码中的自动版本号?
【发布时间】:2011-10-10 19:43:58
【问题描述】:

情况:

我有一个 python 库,它由 git 控制,并与 distutils/setuptools 捆绑在一起。我想根据 git 标签自动生成版本号,既适用于setup.py sdist 和类似的命令,也适用于库本身。

对于第一个任务,我可以使用git describe 或类似的解决方案(请参阅How can I get the version defined in setup.py (setuptools) in my package?)。

例如,当我在标签“0.1”中并调用“setup.py sdist”时,我得到“mylib-0.1.tar.gz”;或 'mylib-0.1-3-abcd.tar.gz' 如果我在标记后更改了代码。这很好。

问题是:

当我想让这个版本号可用于库本身时,问题就出现了,因此它可以在 User-Agent HTTP 标头中将其作为“mylib/0.1-3-adcd”发送。

如果我像在How can I get the version defined in setup.py (setuptools) in my package? 中一样添加setup.py version 命令,那么这个version.py 是在制作标签之后生成的,因为它使用标签作为值。但在这种情况下,我需要在制作版本标记后再提交一次以使代码保持一致。反过来,这需要一个新标签才能进一步捆绑。

问题是:

如何打破这个依赖循环(generate-commit-tag-generate-commit-tag-...)?

【问题讨论】:

标签: python git version setuptools distutils


【解决方案1】:

你也可以反转依赖:将版本放在mylib/__init__.py,在setup.py中解析该文件以获取版本参数,并在命令行使用git tag $(setup.py --version)创建你的标签。

git tag -a v$(python setup.py --version) -m 'description of version'

还有什么更复杂的你想做而我不明白的吗?

【讨论】:

  • 你的意思是git tag -a v$(setup.py --version) -m 'description of version v$(setup.py --version)',对吧?
  • 我遇到的问题是__init__.py 导入您的模块,然后导入您的外部依赖项,setup.py 将在处理__init__.py 之后安装这些依赖项你的版本号。 Ergo:这仅在您没有外部依赖项时才有效。
  • 是的,这就是为什么我说要解析(即使用打开、读取和字符串匹配操作)该文件,而不是导入它。
  • 如果因为setup.py 未标记为可执行(例如,您从github 克隆)而收到“权限被拒绝”,只需将python 添加到命令中:git tag -a v$(python setup.py --version) -m 'description of version'
  • 有没有一个例子说明__init__.py 的版本号应该是什么样子?你将如何从setup.py 解析它?
【解决方案2】:

玩弄keyword expansion 时的经典问题;)

关键是要意识到您的标签是发布管理过程的一部分,而不是开发(及其版本控制)过程的一部分。

换句话说,由于您在问题中说明的循环,您不能在开发存储库中包含发布管理数据。

在生成包(即“发布管理部分”)时,您需要将该信息写入您的库将为其 User-Agent HTTP 标头查找和使用(如果该文件存在)的文件中。

【讨论】:

  • 所以,据我了解,最好只在包(tar.gz&K)中提供这个生成的 version.py,同时在从 工作副本代码。并且根本不要将此 version.py 置于代码控制之下,以便在工作副本中,但只是在生成包时暂时。对吗?
  • @Sergey:是的,这就是一般的想法:“根本不将这个 version.py 置于代码控制之下”:我确认。
  • 你能举个例子说明如何做到这一点吗?
【解决方案3】:

由于这个主题仍然存在并且有时会出现在搜索结果中,我想提一下另一个解决方案,它于 2012 年首次出现,现在或多或少可用:

https://github.com/warner/python-versioneer

它的工作方式与所有提到的解决方案不同:您手动添加 git 标签,库(和 setup.py)读取标签,并动态构建版本字符串。

版本字符串包括最新标签、与该标签的距离、当前提交哈希、“脏度”和其他一些信息。它有几种不同的版本格式。

但它仍然没有所谓的“自定义构建”的分支名称;当两个分支基于同一个提交时,提交距离有时会令人困惑,因此最好只标记和释放一个选定的分支(主)。

【讨论】:

【解决方案4】:

Eric 的想法是简单的方法,以防万一这是我使用的代码(Flask 的团队就是这样做的):

import re
import ast

_version_re = re.compile(r'__version__\s+=\s+(.*)')

with open('app_name/__init__.py', 'rb') as f:
    version = str(ast.literal_eval(_version_re.search(
        f.read().decode('utf-8')).group(1)))

setup(
    name='app-name',
    version=version,
 .....
)

【讨论】:

    【解决方案5】:

    按照similar SO question 中的 OGHaza 解决方案,我保留了一个文件 _version.py,我在 setup.py 中对其进行了解析。使用那里的版本字符串,我在 setup.py 中添加了 git 标签。然后我将设置版本变量设置为版本字符串加上 git 提交哈希的组合。所以这里是 setup.py 的相关部分:

    from setuptools import setup, find_packages
    from codecs import open
    from os import path
    import subprocess
    
    here = path.abspath(path.dirname(__file__))
    
    import re, os
    VERSIONFILE=os.path.join(here,"_version.py")
    verstrline = open(VERSIONFILE, "rt").read()
    VSRE = r"^__version__ = ['\"]([^'\"]*)['\"]"
    mo = re.search(VSRE, verstrline, re.M)
    if mo:
        verstr = mo.group(1)
    else:
        raise RuntimeError("Unable to find version string in %s." % (VERSIONFILE,))
    if os.path.exists(os.path.join(here, '.git')):
        cmd = 'git rev-parse --verify --short HEAD'
        git_hash = subprocess.check_output(cmd)
        # tag git
        gitverstr = 'v' + verstr
        tags =  subprocess.check_output('git tag')
        if not gitverstr in tags:
            cmd = 'git tag -a %s %s -m "tagged by setup.py to %s"' % (gitverstr, git_hash, verstr)        
            subprocess.check_output(cmd)
        # use the git hash in the setup
        verstr += ', git hash: %s' % git_hash
    
    setup(
        name='a_package',
        version = verstr,
        ....
    

    【讨论】:

    • verstr += ', git hash: %s' % git_hash 不兼容 PEP440,所以我将其更改为 verstr += '+git.%s' % git_hash
    【解决方案6】:

    如果您发现versioneer 过于复杂,可以尝试bump2version

    只需在库的根目录中添加简单的bumpversion configuration file。此文件指示存储库中存储版本号的字符串的位置。然后,要在所有指定位置更新版本以用于次要版本,只需键入:

    bumpversion minor
    

    如果您想发布补丁或主要版本,请使用 patchmajor

    这不仅仅是关于凹凸版本。还有其他的 flag-options 和 config 选项,例如自动标记存储库,您可以查看官方文档。

    【讨论】:

      【解决方案7】:

      正如另一个答案中提到的,这与发布过程有关,而不是与开发过程有关,因此它本身不是git 问题,而是您的发布工作过程如何。

      一个非常简单的变体是使用这个:

      python setup.py egg_info -b ".`date '+%Y%m%d'`git`git rev-parse --short HEAD`" build sdist
      

      引号之间的部分可以自定义,但是我尝试使用典型的 Fedora/RedHat 软件包名称。

      值得注意的是,即使 egg_info 暗示与 .egg 的关系,实际上它也是通过工具链使用的,例如 bdist_wheel 也必须在开头指定。

      通常,您的预发布和发布后版本应位于 setup.py 或任何类型的 import version.py 之外。 here 详细介绍了有关版本控制和 egg_info 的主题。

      例子:

      • v1.3.4dev.20200813gitabcdef0
      • v1.3.4 位于 setup.py 或您想要的任何其他变体中
      • dev 和 20200813gitabcdef0 是在构建过程中生成的(上例)
      • 在构建过程中生成的所有文件都不会在 git 中检查(通常在 .gitignore 中,默认情况下会对其进行过滤);有时会有一个单独的“部署”存储库或类似的存储库,与源存储库完全分开

      更复杂的方法是将您的发布工作流程编码在 Makefile 中,这超出了本问题的范围,但是可以在 herehere 中找到很好的灵感来源。你会发现 Makefile 目标和 setup.py 命令之间有很好的对应关系。

      【讨论】:

        猜你喜欢
        • 2013-06-18
        • 2013-11-23
        • 1970-01-01
        • 1970-01-01
        • 2011-01-04
        • 1970-01-01
        • 2012-03-24
        • 2018-06-11
        • 1970-01-01
        相关资源
        最近更新 更多