tools (utils): better repository version handling:
- removed save_dir_path and .hg_data/pickle method: it is not needed to save repository data anymore as its version is now saved on installation by setuptools_scm - added "distance" (number of commits since last tag) - use twisted's which method to find the hg executable - result is now cached on first execution - fixed dirstate method - at last resort, try to find the repository version using the package version, as it is saved there by setuptools_scm
This commit is contained in:
parent
b5a4d8c58d
commit
3951ea451a
1 changed files with 74 additions and 54 deletions
|
|
@ -21,10 +21,13 @@
|
|||
|
||||
import unicodedata
|
||||
import os.path
|
||||
from sat.core.constants import Const as C
|
||||
from sat.core.log import getLogger
|
||||
log = getLogger(__name__)
|
||||
import datetime
|
||||
from dateutil import parser as dateutil_parser
|
||||
from twisted.python import procutils
|
||||
import subprocess
|
||||
import calendar
|
||||
import time
|
||||
import sys
|
||||
|
|
@ -34,6 +37,11 @@ import textwrap
|
|||
import functools
|
||||
|
||||
|
||||
NO_REPOS_DATA = u'repository data unknown'
|
||||
repos_cache_dict = None
|
||||
repos_cache = None
|
||||
|
||||
|
||||
def clean_ustr(ustr):
|
||||
"""Clean unicode string
|
||||
|
||||
|
|
@ -103,22 +111,19 @@ def generatePassword(vocabulary=None, size=20):
|
|||
vocabulary = [chr(i) for i in range(0x30,0x3A) + range(0x41,0x5B) + range (0x61,0x7B)]
|
||||
return u''.join([random.choice(vocabulary) for i in range(15)])
|
||||
|
||||
def getRepositoryData(module, as_string=True, is_path=False, save_dir_path=None):
|
||||
def getRepositoryData(module, as_string=True, is_path=False):
|
||||
"""Retrieve info on current mecurial repository
|
||||
|
||||
Data is gotten by using the following methods, in order:
|
||||
- using "hg" executable
|
||||
- looking for a ".hg_data" file in the root of the module
|
||||
this file must contain the data dictionnary serialized with pickle
|
||||
- looking for a .hg/dirstate in parent directory of module (or in module/.hg if
|
||||
is_path is True), and parse dirstate file to get revision
|
||||
- checking package version, which should have repository data when we are on a dev version
|
||||
@param module(unicode): module to look for (e.g. sat, libervia)
|
||||
module can be a path if is_path is True (see below)
|
||||
@param as_string(bool): if True return a string, else return a dictionary
|
||||
@param is_path(bool): if True "module" is not handled as a module name, but as an
|
||||
absolute path to the parent of a ".hg" directory
|
||||
@param save_path(str, None): if not None, the value will be saved to given path as a pickled dict
|
||||
/!\\ the .hg_data file in the given directory will be overwritten
|
||||
@return (unicode, dictionary): retrieved info in a nice string,
|
||||
or a dictionary with retrieved data (key is not present if data is not found),
|
||||
key can be:
|
||||
|
|
@ -126,13 +131,23 @@ def getRepositoryData(module, as_string=True, is_path=False, save_dir_path=None)
|
|||
- branch: branch name
|
||||
- date: ISO 8601 format date
|
||||
- tag: latest tag used in hierarchie
|
||||
- distance: number of commits since the last tag
|
||||
"""
|
||||
global repos_cache_dict
|
||||
if as_string:
|
||||
global repos_cache
|
||||
if repos_cache is not None:
|
||||
return repos_cache
|
||||
else:
|
||||
if repos_cache_dict is not None:
|
||||
return repos_cache_dict
|
||||
|
||||
if sys.platform == "android":
|
||||
# FIXME: workaround to avoid trouble on android, need to be fixed properly
|
||||
return u"Cagou android build"
|
||||
from distutils.spawn import find_executable
|
||||
import subprocess
|
||||
KEYS=("node", "node_short", "branch", "date", "tag")
|
||||
repos_cache = u"Cagou android build"
|
||||
return repos_cache
|
||||
|
||||
KEYS=("node", "node_short", "branch", "date", "tag", "distance")
|
||||
ori_cwd = os.getcwd()
|
||||
|
||||
if is_path:
|
||||
|
|
@ -140,12 +155,22 @@ def getRepositoryData(module, as_string=True, is_path=False, save_dir_path=None)
|
|||
else:
|
||||
repos_root = os.path.abspath(os.path.dirname(module.__file__))
|
||||
|
||||
hg_path = find_executable('hg')
|
||||
try:
|
||||
hg_path = procutils.which('hg')[0]
|
||||
except IndexError:
|
||||
log.warning(u"Can't find hg executable")
|
||||
hg_path = None
|
||||
hg_data = {}
|
||||
|
||||
if hg_path is not None:
|
||||
os.chdir(repos_root)
|
||||
try:
|
||||
hg_data_raw = subprocess.check_output(["hg","log", "-r", "-1", "--template","{node}\n{node|short}\n{branch}\n{date|isodate}\n{latesttag}"])
|
||||
hg_data_raw = subprocess.check_output(["hg","log", "-r", "-1", "--template","{node}\n"
|
||||
"{node|short}\n"
|
||||
"{branch}\n"
|
||||
"{date|isodate}\n"
|
||||
"{latesttag}\n"
|
||||
"{latesttagdistance}"])
|
||||
except subprocess.CalledProcessError:
|
||||
hg_data = {}
|
||||
else:
|
||||
|
|
@ -157,68 +182,63 @@ def getRepositoryData(module, as_string=True, is_path=False, save_dir_path=None)
|
|||
else:
|
||||
hg_data = {}
|
||||
|
||||
if not hg_data:
|
||||
# .hg_data pickle method
|
||||
log.debug(u"Mercurial not available or working, trying other methods")
|
||||
if save_dir_path is None:
|
||||
log.debug(u"trying .hg_data method")
|
||||
|
||||
try:
|
||||
with open(os.path.join(repos_root, '.hg_data')) as f:
|
||||
import cPickle as pickle
|
||||
hg_data = pickle.load(f)
|
||||
except IOError as e:
|
||||
log.debug(u"Can't access .hg_data file: {}".format(e))
|
||||
except pickle.UnpicklingError:
|
||||
log.warning(u"Error while reading {}, can't get repos data".format(f.name))
|
||||
|
||||
if not hg_data:
|
||||
# .hg/dirstate method
|
||||
log.debug(u"trying dirstate method")
|
||||
if is_path:
|
||||
os.chdir(repos_root)
|
||||
else:
|
||||
os.chdir(os.path.abspath(os.path.join('..', repos_root)))
|
||||
os.chdir(os.path.abspath(os.path.dirname(repos_root)))
|
||||
try:
|
||||
with open('.hg/dirstate') as hg_dirstate:
|
||||
hg_data['node'] = hg_dirstate.read(20).encode('hex')
|
||||
hg_data['node_short'] = hg_data['node'][:12]
|
||||
except IOError:
|
||||
log.warning(u"Can't access repository data")
|
||||
log.debug(u"Can't access repository data")
|
||||
|
||||
# we restore original working dir
|
||||
os.chdir(ori_cwd)
|
||||
|
||||
# data saving
|
||||
if save_dir_path is not None and hg_data:
|
||||
if not os.path.isdir(save_dir_path):
|
||||
log.warning(u"Given path is not a directory, can't save data")
|
||||
if not hg_data:
|
||||
log.debug(u"Mercurial not available or working, trying package version")
|
||||
try:
|
||||
import pkg_resources
|
||||
pkg_version = pkg_resources.get_distribution(C.APP_NAME_FILE).version
|
||||
version, hg_node, hg_distance = pkg_version.split('-')
|
||||
except ImportError:
|
||||
log.warning("pkg_resources not available, can't get package data")
|
||||
except pkg_resources.DistributionNotFound:
|
||||
log.warning("can't retrieve package data")
|
||||
except ValueError:
|
||||
log.warning(u"package version doesn't fit: {pkg_version}".format(pkg_version=pkg_version))
|
||||
else:
|
||||
import cPickle as pickle
|
||||
dest_path = os.path.join(save_dir_path, ".hg_data")
|
||||
try:
|
||||
with open(dest_path, 'w') as f:
|
||||
pickle.dump(hg_data, f, 2)
|
||||
except IOError as e:
|
||||
log.warning(u"Can't save file to {path}: {reason}".format(
|
||||
path=dest_path, reason=e))
|
||||
if version != C.APP_VERSION:
|
||||
log.error("Incompatible version ({version}) and pkg_version ({pkg_version})".format(
|
||||
version=C.APP_VERSION, pkg_version=pkg_version))
|
||||
else:
|
||||
log.debug(u"repository data saved to {}".format(dest_path))
|
||||
hg_data = {'node_short': hg_node, 'distance': hg_distance}
|
||||
|
||||
repos_cache_dict = hg_data
|
||||
|
||||
if as_string:
|
||||
if not hg_data:
|
||||
return u'repository data unknown'
|
||||
strings = [u'rev', hg_data['node_short']]
|
||||
try:
|
||||
if hg_data['modified']:
|
||||
strings.append(u"[M]")
|
||||
except KeyError:
|
||||
pass
|
||||
try:
|
||||
strings.extend([u'({branch} {date})'.format(**hg_data)])
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
return u' '.join(strings)
|
||||
repos_cache = NO_REPOS_DATA
|
||||
else:
|
||||
strings = [u'rev', hg_data['node_short']]
|
||||
try:
|
||||
if hg_data['modified']:
|
||||
strings.append(u"[M]")
|
||||
except KeyError:
|
||||
pass
|
||||
try:
|
||||
strings.extend([u'({branch} {date})'.format(**hg_data)])
|
||||
except KeyError:
|
||||
pass
|
||||
try:
|
||||
strings.extend([u'+{distance}'.format(**hg_data)])
|
||||
except KeyError:
|
||||
pass
|
||||
repos_cache = u' '.join(strings)
|
||||
return repos_cache
|
||||
else:
|
||||
return hg_data
|
||||
|
|
|
|||
Loading…
Reference in a new issue