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