web-dev-qa-db-fra.com

Numéro de version automatique à la fois dans setup.py (setuptools) ET dans le code source?

SITUATION:

J'ai une bibliothèque python, qui est contrôlée par git, et fournie avec distutils/setuptools. Et je veux générer automatiquement un numéro de version basé sur les balises git, à la fois pour setup.py sdist et les commandes similaires, et pour la bibliothèque elle-même.

Pour la première tâche, je peux utiliser git describe ou des solutions similaires (voir Comment puis-je obtenir la version définie dans setup.py (setuptools) dans mon package? ).

Et quand, par exemple, je suis dans une balise "0.1" et que j'appelle "setup.py sdist", j'obtiens "mylib-0.1.tar.gz"; ou 'mylib-0.1-3-abcd.tar.gz' si j'ai modifié le code après le balisage. C'est bon.

LE PROBLÈME EST:

Le problème survient lorsque je veux avoir ce numéro de version disponible pour la bibliothèque elle-même, afin qu'elle puisse l'envoyer dans l'en-tête HTTP User-Agent en tant que "mylib/0.1-3-adcd".

Si j'ajoute setup.py version commande comme dans Comment puis-je obtenir la version définie dans setup.py (setuptools) dans mon package? , alors cette version.py est générée APRÈS la création de la balise, car elle utilise la balise comme une valeur. Mais dans ce cas, je dois faire un autre commit après que la balise de version soit faite pour rendre le code cohérent. Ce qui, à son tour, nécessite une nouvelle balise pour un regroupement ultérieur.

LA QUESTION EST:

Comment briser ce cercle de dépendances (generate-commit-tag-generate-commit-tag -...)?

37
Sergey Vasilyev

Vous pouvez également inverser la dépendance: mettez la version dans mylib/__init__.py, analysez ce fichier dans setup.py pour obtenir le paramètre de version et utilisez la balise git $ (setup.py --version) sur la ligne de commande pour créer votre balise.

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

Voulez-vous faire quelque chose de plus compliqué que je n’ai pas compris?

32
Éric Araujo

Un problème classique lorsque vous jouez avec expansion des mots clés ;)

La clé est de réaliser que votre balise fait partie du processus de gestion des versions, et non du processus de développement (et de son contrôle de version).

Dans un autre Word, vous ne pouvez pas inclure de données de gestion des versions dans un référentiel de développement, en raison de la boucle illustrée dans votre question.

Vous devez, lors de la génération du package (qui est la "partie de gestion des versions"), écrire ces informations dans un fichier que votre bibliothèque recherchera et utilisera (si ledit fichier existe) pour son en-tête HTTP User-Agent.

23
VonC

Étant donné que ce sujet est toujours vivant et arrive parfois aux résultats de recherche, je voudrais mentionner une autre solution qui est apparue pour la première fois en 2012 et est maintenant plus ou moins utilisable:

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

Cela fonctionne différemment de toutes les solutions mentionnées: vous ajoutez des balises git manuellement, et la bibliothèque (et setup.py) lit les balises et construit la chaîne de version de manière dynamique.

La chaîne de version inclut la dernière balise, la distance par rapport à cette balise, le hachage de validation actuel, la "saleté" et d'autres informations. Il a quelques formats de version différents.

Mais il n'a toujours pas de nom de branche pour les soi-disant "builds personnalisés"; et la distance de validation peut parfois être déroutante lorsque deux branches sont basées sur le même commit, il est donc préférable de marquer et de publier une seule branche sélectionnée (maître).

8
Sergey Vasilyev

L'idée d'Eric était la solution la plus simple, juste au cas où cela serait utile, voici le code que j'ai utilisé (l'équipe de Flask l'a fait de cette façon):

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
dim_user

Suite à la solution d'OGHaza dans un similaire SO question je garde un fichier _version.py que j'analyse dans setup.py. Avec la chaîne de version à partir de là, je balise git dans setup.py. Ensuite, j'ai défini la variable de version de configuration sur une combinaison de chaîne de version plus le hachage git commit. Voici donc la partie pertinente de 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
Sven