From e51d9439c53a50ac7e11780a391d52c17dceb6ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Tue, 2 Sep 2014 11:35:42 +0200 Subject: [PATCH] Modified versioneer to allow specification of manual versions based on branches You can now define a lookup file explicitely mapping virtual version tags to branches via regular expressions and a reference commit from which the commit distance will be calculated. Format of the file is The file is processed from top to bottom, the first matching line wins. If or are left out, the lookup table does not apply to the matched branches and the regular versioneer resolution (via git describe) takes place. Current configuration makes "master", "staging" and any branch starting with "fix/" make use the default behaviour, all other branches (so basically all development branches) are defined as "1.2.0-dev" (cherry picked from commit 212f40c) --- .versioneer-lookup | 18 ++++ setup.py | 1 + src/octoprint/_version.py | 101 ++++++++++++++++-- versioneer.py | 208 +++++++++++++++++++++++++++++++++++--- 4 files changed, 304 insertions(+), 24 deletions(-) create mode 100644 .versioneer-lookup diff --git a/.versioneer-lookup b/.versioneer-lookup new file mode 100644 index 0000000000..8e1218d797 --- /dev/null +++ b/.versioneer-lookup @@ -0,0 +1,18 @@ +# Configuration file for the versioneer lookup, manually mapping tags based on branches +# +# Format is +# +# +# +# The file is processed from top to bottom, the first matching line wins. If or are left out, +# the lookup table does not apply to the matched branches + +# master and staging shall not use the lookup table +master +staging + +# fix/ branches are fixes for master, so we don't handle those either +fix/.* + +# every other branch is a development branch and thus gets resolved to 1.2.0-dev for now +.* 1.2.0-dev 50cf776e70b9 diff --git a/setup.py b/setup.py index 471e546f35..d06296ea24 100644 --- a/setup.py +++ b/setup.py @@ -7,6 +7,7 @@ versioneer.versionfile_build = 'octoprint/_version.py' versioneer.tag_prefix = '' versioneer.parentdir_prefix = '' +versioneer.lookupfile = '.versioneer-lookup' from setuptools import setup, find_packages, Command import os diff --git a/src/octoprint/_version.py b/src/octoprint/_version.py index f9a41364b8..52aefd4943 100644 --- a/src/octoprint/_version.py +++ b/src/octoprint/_version.py @@ -54,6 +54,18 @@ def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False): import re import os.path +def get_gits(root, verbose=False): + if not os.path.exists(os.path.join(root, ".git")): + if verbose: + print("no .git in %s" % root) + return None + + GITS = ["git"] + if sys.platform == "win32": + GITS = ["git.cmd", "git.exe"] + return GITS + + def get_expanded_variables(versionfile_abs): # the code embedded in _version.py can just fetch the value of these # variables. When used from setup.py, we don't want to import @@ -114,20 +126,60 @@ def versions_from_expanded_variables(variables, tag_prefix, verbose=False): return { "version": variables["full"].strip(), "full": variables["full"].strip() } +def versions_from_lookup(lookup, root, verbose=False): + GITS = get_gits(root, verbose=verbose) + if GITS is None: + return {} + + stdout = run_command(GITS, ["rev-parse", "--abbrev-ref", "HEAD"], + cwd=root) + if stdout is None: + return {} + + current_branch = stdout.strip() + for matcher, tag, ref_commit in lookup: + if matcher.match(current_branch): + if tag is None or ref_commit is None: + return {} + + stdout = run_command(GITS, ["rev-list", "%s..HEAD" % ref_commit, "--count"], cwd=root) + if stdout is None: + return {} + num_commits = stdout.strip() + + stdout =run_command(GITS, ["rev-parse", "--short", "HEAD"], cwd=root) + if stdout is None: + return {} + short_hash = stdout.strip() + + stdout = run_command(GITS, ["describe", "--tags", "--dirty", "--always"], cwd=root) + if stdout is None: + return {} + dirty = stdout.strip().endswith("-dirty") + + stdout = run_command(GITS, ["rev-parse", "HEAD"], cwd=root) + if stdout is None: + return {} + full = stdout.strip() + + version = "%s-%s-g%s" % (tag, num_commits, short_hash) + if dirty: + version += "-dirty" + full += "-dirty" + return {"version": version, "full": full} + + return {} + def versions_from_vcs(tag_prefix, root, verbose=False): # this runs 'git' from the root of the source tree. This only gets called # if the git-archive 'subst' variables were *not* expanded, and # _version.py hasn't already been rewritten with a short version string, # meaning we're inside a checked out source tree. - if not os.path.exists(os.path.join(root, ".git")): - if verbose: - print("no .git in %s" % root) + GITS = get_gits(root, verbose=verbose) + if GITS is None: return {} - GITS = ["git"] - if sys.platform == "win32": - GITS = ["git.cmd", "git.exe"] stdout = run_command(GITS, ["describe", "--tags", "--dirty", "--always"], cwd=root) if stdout is None: @@ -158,10 +210,39 @@ def versions_from_parentdir(parentdir_prefix, root, verbose=False): return {"version": dirname[len(parentdir_prefix):], "full": ""} tag_prefix = "" -parentdir_prefix = "octoprint-" +parentdir_prefix = "" versionfile_source = "src/octoprint/_version.py" +lookupfile = ".versioneer-lookup" + +def parse_lookup_file(root, lookup_path=None): + if not lookup_path: + lookup_path = lookupfile + if not lookup_path: + return [] + + path = os.path.join(root, lookup_path) + if not os.path.exists(path): + return [] + + import re + lookup = [] + with open(os.path.join(root, lookup_path), "r") as f: + for line in f: + if '#' in line: + line = line[:line.rindex('#')] + line = line.strip() + try: + split_line = line.split() + if len(split_line) == 3: + pattern, tag, ref_commit = split_line + lookup.append([re.compile(pattern), tag, ref_commit]) + elif len(split_line) >= 1: + lookup.append([re.compile(split_line[0]), None, None]) + except: + break + return lookup -def get_versions(default={"version": "unknown", "full": ""}, verbose=False): +def get_versions(default={"version": "unknown", "full": ""}, lookup_path=None, verbose=False): # I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have # __file__, we can work backwards from there to the root. Some # py2exe/bbfreeze/non-CPython implementations don't do __file__, in which @@ -182,7 +263,9 @@ def get_versions(default={"version": "unknown", "full": ""}, verbose=False): except NameError: return default - return (versions_from_vcs(tag_prefix, root, verbose) + lookup = parse_lookup_file(root, lookup_path=lookup_path) + return (versions_from_lookup(lookup, root, verbose) + or versions_from_vcs(tag_prefix, root, verbose) or versions_from_parentdir(parentdir_prefix, root, verbose) or default) diff --git a/versioneer.py b/versioneer.py index a01ab1cefe..237968f19d 100644 --- a/versioneer.py +++ b/versioneer.py @@ -252,6 +252,7 @@ versionfile_build = None tag_prefix = None parentdir_prefix = None +lookupfile = None VCS = "git" @@ -312,6 +313,18 @@ def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False): import re import os.path +def get_gits(root, verbose=False): + if not os.path.exists(os.path.join(root, ".git")): + if verbose: + print("no .git in %%s" %% root) + return None + + GITS = ["git"] + if sys.platform == "win32": + GITS = ["git.cmd", "git.exe"] + return GITS + + def get_expanded_variables(versionfile_abs): # the code embedded in _version.py can just fetch the value of these # variables. When used from setup.py, we don't want to import @@ -372,20 +385,60 @@ def versions_from_expanded_variables(variables, tag_prefix, verbose=False): return { "version": variables["full"].strip(), "full": variables["full"].strip() } +def versions_from_lookup(lookup, root, verbose=False): + GITS = get_gits(root, verbose=verbose) + if GITS is None: + return {} + + stdout = run_command(GITS, ["rev-parse", "--abbrev-ref", "HEAD"], + cwd=root) + if stdout is None: + return {} + + current_branch = stdout.strip() + for matcher, tag, ref_commit in lookup: + if matcher.match(current_branch): + if tag is None or ref_commit is None: + return {} + + stdout = run_command(GITS, ["rev-list", "%%s..HEAD" %% ref_commit, "--count"], cwd=root) + if stdout is None: + return {} + num_commits = stdout.strip() + + stdout =run_command(GITS, ["rev-parse", "--short", "HEAD"], cwd=root) + if stdout is None: + return {} + short_hash = stdout.strip() + + stdout = run_command(GITS, ["describe", "--tags", "--dirty", "--always"], cwd=root) + if stdout is None: + return {} + dirty = stdout.strip().endswith("-dirty") + + stdout = run_command(GITS, ["rev-parse", "HEAD"], cwd=root) + if stdout is None: + return {} + full = stdout.strip() + + version = "%%s-%%s-g%%s" %% (tag, num_commits, short_hash) + if dirty: + version += "-dirty" + full += "-dirty" + return {"version": version, "full": full} + + return {} + def versions_from_vcs(tag_prefix, root, verbose=False): # this runs 'git' from the root of the source tree. This only gets called # if the git-archive 'subst' variables were *not* expanded, and # _version.py hasn't already been rewritten with a short version string, # meaning we're inside a checked out source tree. - if not os.path.exists(os.path.join(root, ".git")): - if verbose: - print("no .git in %%s" %% root) + GITS = get_gits(root, verbose=verbose) + if GITS is None: return {} - GITS = ["git"] - if sys.platform == "win32": - GITS = ["git.cmd", "git.exe"] stdout = run_command(GITS, ["describe", "--tags", "--dirty", "--always"], cwd=root) if stdout is None: @@ -418,8 +471,37 @@ def versions_from_parentdir(parentdir_prefix, root, verbose=False): tag_prefix = "%(TAG_PREFIX)s" parentdir_prefix = "%(PARENTDIR_PREFIX)s" versionfile_source = "%(VERSIONFILE_SOURCE)s" - -def get_versions(default={"version": "unknown", "full": ""}, verbose=False): +lookupfile = %(LOOKUPFILE)s + +def parse_lookup_file(root, lookup_path=None): + if not lookup_path: + lookup_path = lookupfile + if not lookup_path: + return [] + + path = os.path.join(root, lookup_path) + if not os.path.exists(path): + return [] + + import re + lookup = [] + with open(os.path.join(root, lookup_path), "r") as f: + for line in f: + if '#' in line: + line = line[:line.rindex('#')] + line = line.strip() + try: + split_line = line.split() + if len(split_line) == 3: + pattern, tag, ref_commit = split_line + lookup.append([re.compile(pattern), tag, ref_commit]) + elif len(split_line) >= 1: + lookup.append([re.compile(split_line[0]), None, None]) + except: + break + return lookup + +def get_versions(default={"version": "unknown", "full": ""}, lookup_path=None, verbose=False): # I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have # __file__, we can work backwards from there to the root. Some # py2exe/bbfreeze/non-CPython implementations don't do __file__, in which @@ -440,7 +522,9 @@ def get_versions(default={"version": "unknown", "full": ""}, verbose=False): except NameError: return default - return (versions_from_vcs(tag_prefix, root, verbose) + lookup = parse_lookup_file(root, lookup_path=lookup_path) + return (versions_from_lookup(lookup, root, verbose) + or versions_from_vcs(tag_prefix, root, verbose) or versions_from_parentdir(parentdir_prefix, root, verbose) or default) @@ -488,6 +572,18 @@ def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False): import re import os.path +def get_gits(root, verbose=False): + if not os.path.exists(os.path.join(root, ".git")): + if verbose: + print("no .git in %s" % root) + return None + + GITS = ["git"] + if sys.platform == "win32": + GITS = ["git.cmd", "git.exe"] + return GITS + + def get_expanded_variables(versionfile_abs): # the code embedded in _version.py can just fetch the value of these # variables. When used from setup.py, we don't want to import @@ -548,20 +644,62 @@ def versions_from_expanded_variables(variables, tag_prefix, verbose=False): return { "version": variables["full"].strip(), "full": variables["full"].strip() } + +def versions_from_lookup(lookup, root, verbose=False): + GITS = get_gits(root, verbose=verbose) + if GITS is None: + return {} + + stdout = run_command(GITS, ["rev-parse", "--abbrev-ref", "HEAD"], + cwd=root) + if stdout is None: + return {} + + current_branch = stdout.strip() + for matcher, tag, ref_commit in lookup: + if matcher.match(current_branch): + if tag is None or ref_commit is None: + return {} + + stdout = run_command(GITS, ["rev-list", "%s..HEAD" % ref_commit, "--count"], cwd=root) + if stdout is None: + return {} + num_commits = stdout.strip() + + stdout =run_command(GITS, ["rev-parse", "--short", "HEAD"], cwd=root) + if stdout is None: + return {} + short_hash = stdout.strip() + + stdout = run_command(GITS, ["describe", "--tags", "--dirty", "--always"], cwd=root) + if stdout is None: + return {} + dirty = stdout.strip().endswith("-dirty") + + stdout = run_command(GITS, ["rev-parse", "HEAD"], cwd=root) + if stdout is None: + return {} + full = stdout.strip() + + version = "%s-%s-g%s" % (tag, num_commits, short_hash) + if dirty: + version += "-dirty" + full += "-dirty" + return {"version": version, "full": full} + + return {} + + def versions_from_vcs(tag_prefix, root, verbose=False): # this runs 'git' from the root of the source tree. This only gets called # if the git-archive 'subst' variables were *not* expanded, and # _version.py hasn't already been rewritten with a short version string, # meaning we're inside a checked out source tree. - if not os.path.exists(os.path.join(root, ".git")): - if verbose: - print("no .git in %s" % root) + GITS = get_gits(root, verbose=verbose) + if GITS is None: return {} - GITS = ["git"] - if sys.platform == "win32": - GITS = ["git.cmd", "git.exe"] stdout = run_command(GITS, ["describe", "--tags", "--dirty", "--always"], cwd=root) if stdout is None: @@ -684,11 +822,38 @@ def get_root(): except NameError: return os.path.dirname(os.path.abspath(sys.argv[0])) +def parse_lookup_file(root, lookup_path=None): + if not lookup_path: + lookup_path = lookupfile + + path = os.path.join(root, lookup_path) + if not os.path.exists(path): + return [] + + import re + lookup = [] + with open(os.path.join(root, lookup_path), "r") as f: + for line in f: + if '#' in line: + line = line[:line.rindex("#")] + line = line.strip() + try: + split_line = line.split() + if len(split_line) == 3: + pattern, tag, ref_commit = split_line + lookup.append([re.compile(pattern), tag, ref_commit]) + elif len(split_line) >= 1: + lookup.append([re.compile(split_line[0]), None, None]) + except: + break + return lookup + def get_versions(default=DEFAULT, verbose=False): # returns dict with two keys: 'version' and 'full' assert versionfile_source is not None, "please set versioneer.versionfile_source" assert tag_prefix is not None, "please set versioneer.tag_prefix" assert parentdir_prefix is not None, "please set versioneer.parentdir_prefix" + # I am in versioneer.py, which must live at the top of the source tree, # which we use to compute the root directory. py2exe/bbfreeze/non-CPython # don't have __file__, in which case we fall back to sys.argv[0] (which @@ -697,6 +862,11 @@ def get_versions(default=DEFAULT, verbose=False): root = get_root() versionfile_abs = os.path.join(root, versionfile_source) + if lookupfile: + lookup = parse_lookup_file(root, lookup_path = lookupfile) + else: + lookup = None + # extract version from first of _version.py, 'git describe', parentdir. # This is meant to work for developers using a source checkout, for users # of a tarball created by 'setup.py sdist', and for users of a @@ -715,6 +885,12 @@ def get_versions(default=DEFAULT, verbose=False): if verbose: print("got version from file %s %s" % (versionfile_abs,ver)) return ver + if lookup: + ver = versions_from_lookup(lookup, root, verbose=verbose) + if ver: + if verbose: print("got version from lookup %s" % ver) + return ver + ver = versions_from_vcs(tag_prefix, root, verbose) if ver: if verbose: print("got version from git %s" % ver) @@ -792,6 +968,7 @@ def run(self): "TAG_PREFIX": tag_prefix, "PARENTDIR_PREFIX": parentdir_prefix, "VERSIONFILE_SOURCE": versionfile_source, + "LOOKUPFILE": '"%s"' % lookupfile if lookupfile is not None else "None", }) f.close() @@ -835,6 +1012,7 @@ def run(self): "TAG_PREFIX": tag_prefix, "PARENTDIR_PREFIX": parentdir_prefix, "VERSIONFILE_SOURCE": versionfile_source, + "LOOKUPFILE": '"%s"' % lookupfile if lookupfile is not None else "None", }) f.close()