setup.py(setuptools)和源代码中的自动版本号?

时间:2011-07-22 06:47:31

标签: python git version setuptools distutils

状况:

我有一个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'如果我在标记后更改了代码。这很好。

问题是:

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

如果我在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 -...)?

7 个答案:

答案 0 :(得分:29)

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

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

你还有什么比这更复杂的我还没有理解?

答案 1 :(得分:22)

使用keyword expansion进行玩弄时的经典问题;)

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

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

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

答案 2 :(得分:7)

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

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

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

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

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

答案 3 :(得分:4)

similar SO question中关注OGHaza的解决方案后,我保存了一个我在setup.py中解析的文件_version.py。使用那里的版本字符串,我在setup.py中使用git标记。然后我将setup version变量设置为版本字符串和git commit hash的组合。所以这是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,
    ....

答案 4 :(得分:2)

正如另一个答案中所提到的,这与发布过程有关,而与开发过程无关,因此这本身不是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,这超出了此问题的范围,但是可以找到here和{{3 }}。您会在Makefile目标和setup.py命令之间找到很好的对应点。

答案 5 :(得分:1)

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,
 .....
)

答案 6 :(得分:1)

如果发现versioneer太麻烦了,可以尝试bump2version

只需在您的库的根目录中添加一个配置文件,该文件指示包含版本号的文件在哪里,然后通过其CLI输入:

bumpversion minor

发布次要版本(否则为patchmajor)。

还有其他标志选项和配置选项,例如自动标记存储库,您可以查看其官方文档。