# HG changeset patch # User Goffi # Date 1522934618 -7200 # Node ID 18e2ca5f798e684c24b5f214005c0f28ede2998b # Parent 7e7f4e344a963f81c3139f315284a7111653eace 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 diff -r 7e7f4e344a96 -r 18e2ca5f798e sat/tools/utils.py --- a/sat/tools/utils.py Thu Apr 05 12:49:25 2018 +0200 +++ b/sat/tools/utils.py Thu Apr 05 15:23:38 2018 +0200 @@ -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 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 @@ 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 @@ - 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 @@ 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: @@ -158,67 +183,62 @@ 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