Completed
Push — develop ( bd0cae...c96210 )
by Kale
01:40 queued 44s
created

auxlib.SDistCommand   A

Complexity

Total Complexity 3

Size/Duplication

Total Lines 10
Duplicated Lines 0 %
Metric Value
dl 0
loc 10
rs 10
wmc 3

2 Methods

Rating   Name   Duplication   Size   Complexity  
A make_release_tree() 0 6 2
A run() 0 2 1
1
# -*- coding: utf-8 -*-
2
from __future__ import print_function, division, absolute_import
3
from logging import getLogger
4
from os.path import basename, dirname, join, isdir
5
from re import match
6
from setuptools.command.build_py import build_py
7
from setuptools.command.sdist import sdist
8
from setuptools.command.test import test as TestCommand
9
from subprocess import CalledProcessError, check_call, check_output
10
import sys
11
12
from .path import absdirname, PackageFile, ROOT_PATH
13
14
log = getLogger(__name__)
15
16
17
def _get_version_from_pkg_info(package_name):
18
    with PackageFile('.version', package_name) as fh:
19
        return fh.read()
20
21
22
def _is_git_dirty():
23
    try:
24
        check_call(('git', 'diff', '--quiet'))
25
        check_call(('git', 'diff', '--cached', '--quiet'))
26
        return False
27
    except CalledProcessError:
28
        return True
29
30
31
def _get_most_recent_git_tag():
32
    try:
33
        return check_output(["git", "describe", "--tags"]).strip()
34
    except CalledProcessError as e:
35
        if e.returncode == 128:
36
            return "0.0.0.0"
37
        else:
38
            raise  # pragma: no cover
39
40
41
def _get_git_hash():
42
    try:
43
        return check_output(["git", "rev-parse", "HEAD"]).strip()[:7]
44
    except CalledProcessError:
45
        return 0
46
47
48
def _get_version_from_git_tag():
49
    """Return a PEP440-compliant version derived from the git status.
50
    If that fails for any reason, return the first 7 chars of the changeset hash.
51
    """
52
    tag = _get_most_recent_git_tag()
53
    m = match(b"(?P<xyz>\d+\.\d+\.\d+)(?:-(?P<dev>\d+)-(?P<hash>.+))?", tag)
54
    version = m.group('xyz').decode('utf-8')
55
    if m.group('dev') or _is_git_dirty():
56
        dev = (m.group('dev') or 0).decode('utf-8')
57
        hash_ = (m.group('hash') or _get_git_hash()).decode('utf-8')
58
        version += ".dev{dev}.{hash_}".format(dev=dev, hash_=hash_)
59
    return version
60
61
62
def is_git_repo(path, package):
63
    if path == ROOT_PATH or dirname(basename(path)) == package:
64
        return False
65
    else:
66
        return isdir(join(path, '.git')) or is_git_repo(dirname(path), package)
67
68
69
def get_version(file, package):
70
    """Returns a version string for the current package, derived
71
    either from git or from a .version file.
72
73
    This function is expected to run in two contexts. In a development
74
    context, where .git/ exists, the version is pulled from git tags.
75
    Using the BuildPyCommand and SDistCommand classes for cmdclass in
76
    setup.py will write a .version file into any dist.
77
78
    In an installed context, the .version file written at dist build
79
    time is the source of version information.
80
81
    """
82
    here = absdirname(file)
83
    if is_git_repo(here, package):
84
        return _get_version_from_git_tag()
85
86
    # fall back to .version file
87
    version_from_pkg = _get_version_from_pkg_info(package)
88
    if version_from_pkg:
89
        return version_from_pkg.decode('utf-8')
90
91
    raise RuntimeError("Could not get package version (no .git or .version file)")
92
93
94
class BuildPyCommand(build_py):
95
    def run(self):
96
        build_py.run(self)
97
        # locate .version in the new build/ directory and replace it with an updated value
98
        target_version_file = join(self.build_lib, self.distribution.metadata.name, ".version")
99
        print("UPDATING {0}".format(target_version_file))
100
        with open(target_version_file, 'w') as f:
101
            f.write(self.distribution.metadata.version)
102
103
104
class SDistCommand(sdist):
105
    def run(self):
106
        return sdist.run(self)
107
108
    def make_release_tree(self, base_dir, files):
109
        sdist.make_release_tree(self, base_dir, files)
110
        target_version_file = join(base_dir, self.distribution.metadata.name, ".version")
111
        print("UPDATING {0}".format(target_version_file))
112
        with open(target_version_file, 'w') as f:
113
            f.write(self.distribution.metadata.version)
114
115
116
class Tox(TestCommand):
117
    user_options = [('tox-args=', 'a', "Arguments to pass to tox")]
118
119
    def initialize_options(self):
120
        TestCommand.initialize_options(self)
121
        self.tox_args = None
122
123
    def finalize_options(self):
124
        TestCommand.finalize_options(self)
125
        self.test_args = []
126
        self.test_suite = True
127
128
    def run_tests(self):
129
        # import here, cause outside the eggs aren't loaded
130
        import tox
131
        import shlex
132
        args = self.tox_args
133
        if args:
134
            args = shlex.split(self.tox_args)
135
        else:
136
            args = ''
137
        errno = tox.cmdline(args=args)
138
        sys.exit(errno)
139