Automatic version number both in setup.py (setuptools) AND source code?

前端 未结 7 1306
没有蜡笔的小新
没有蜡笔的小新 2020-12-24 11:21

SITUATION:

I have a python library, which is controlled by git, and bundled with distutils/setuptools. And I want to automatically generate version

相关标签:
7条回答
  • 2020-12-24 11:38

    Eric's idea was the simple way to go, just in case this is useful here is the code I used (Flask's team did it this way):

    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,
     .....
    )
    
    0 讨论(0)
  • 2020-12-24 11:40

    Following OGHaza's solution in a similar SO question I keep a file _version.py that I parse in setup.py. With the version string from there, I git tag in setup.py. Then I set the setup version variable to a combination of version string plus the git commit hash. So here is the relevant part of 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,
        ....
    
    0 讨论(0)
  • 2020-12-24 11:45

    You could also reverse the dependency: put the version in mylib/__init__.py, parse that file in setup.py to get the version parameter, and use git tag $(setup.py --version) on the command line to create your tag.

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

    Is there anything more complicated you want to do that I haven’t understood?

    0 讨论(0)
  • 2020-12-24 11:45

    Since this topic is still alive and sometimes gets to search results, I would like to mention another solution which first appeared in 2012 and now is more or less usable:

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

    It works in different way than all mentioned solutions: you add git tags manually, and the library (and setup.py) reads the tags, and builds the version string dynamically.

    The version string includes the latest tag, distance from that tag, current commit hash, "dirtiness", and some other info. It has few different version formats.

    But it still has no branch name for so called "custom builds"; and commit distance can be confusing sometimes when two branches are based on the same commit, so it is better to tag & release only one selected branch (master).

    0 讨论(0)
  • 2020-12-24 11:47

    As was mentioned in another answer, this is related to the release process and not to the development process, as such it is not a git issue in itself, but more how is your release work process.

    A very simple variant is to use this:

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

    The portion between the quotes is up for customization, however I tried to follow the typical Fedora/RedHat package names.

    Of note, even if egg_info implies relation to .egg, actually it's used through the toolchain, for example for bdist_wheel as well and has to be specified in the beginning.

    In general, your pre-release and post-release versions should live outside setup.py or any type of import version.py. The topic about versioning and egg_info is covered in detail here.

    Example:

    • v1.3.4dev.20200813gitabcdef0
    • The v1.3.4 is in setup.py or any other variation you would like
    • The dev and 20200813gitabcdef0 is generated during the build process (example above)
    • None of the files generated during build are checked in git (usually in .gitignore they are filtered by default); sometimes there is a separate "deployment" repository, or similar, completely separate from the source one

    A more complex way would be to have your release work process encoded in a Makefile which is outside the scope of this question, however a good source of inspiration can be found here and here. You will find good correspondeces between Makefile targets and setup.py commands.

    0 讨论(0)
  • 2020-12-24 11:48

    If you found versioneer excessively convoluted, you can try bump2version.

    Just add a configuration file in the root of your library, that indicates where are the files containing the version number, and then via its CLI, you type:

    bumpversion minor
    

    to release a minor version (patch or major otherwise).

    There are other flag-options, and config options, such as tagging automatically the repository, for which you can check the official documentation.

    0 讨论(0)
提交回复
热议问题