diff options
Diffstat (limited to 'extra/release/bump_version.py')
-rwxr-xr-x | extra/release/bump_version.py | 166 |
1 files changed, 166 insertions, 0 deletions
diff --git a/extra/release/bump_version.py b/extra/release/bump_version.py new file mode 100755 index 00000000..02d8727c --- /dev/null +++ b/extra/release/bump_version.py @@ -0,0 +1,166 @@ +#!/usr/bin/env python + +from __future__ import absolute_import +from __future__ import with_statement + +import errno +import os +import re +import sys +import subprocess + +from contextlib import contextmanager +from tempfile import NamedTemporaryFile + +rq = lambda s: s.strip("\"'") + + +def cmd(*args): + return subprocess.Popen(args, stdout=subprocess.PIPE).communicate()[0] + + +@contextmanager +def no_enoent(): + try: + yield + except OSError, exc: + if exc.errno != errno.ENOENT: + raise + + +class StringVersion(object): + + def decode(self, s): + s = rq(s) + text = "" + major, minor, release = s.split(".") + if not release.isdigit(): + pos = release.index(re.split("\d+", release)[1][0]) + release, text = release[:pos], release[pos:] + return int(major), int(minor), int(release), text + + def encode(self, v): + return ".".join(map(str, v[:3])) + v[3] +to_str = StringVersion().encode +from_str = StringVersion().decode + + +class TupleVersion(object): + + def decode(self, s): + v = list(map(rq, s.split(", "))) + return (tuple(map(int, v[0:3])) + + tuple(["".join(v[3:])])) + + def encode(self, v): + v = list(v) + + def quote(lit): + if isinstance(lit, basestring): + return '"%s"' % (lit, ) + return str(lit) + + if not v[-1]: + v.pop() + return ", ".join(map(quote, v)) + + +class VersionFile(object): + + def __init__(self, filename): + self.filename = filename + self._kept = None + + def _as_orig(self, version): + return self.wb % {"version": self.type.encode(version), + "kept": self._kept} + + def write(self, version): + pattern = self.regex + with no_enoent(): + with NamedTemporaryFile() as dest: + with open(self.filename) as orig: + for line in orig: + if pattern.match(line): + dest.write(self._as_orig(version)) + else: + dest.write(line) + os.rename(dest.name, self.filename) + + def parse(self): + pattern = self.regex + gpos = 0 + with open(self.filename) as fh: + for line in fh: + m = pattern.match(line) + if m: + if "?P<keep>" in pattern.pattern: + self._kept, gpos = m.groupdict()["keep"], 1 + return self.type.decode(m.groups()[gpos]) + + +class PyVersion(VersionFile): + regex = re.compile(r'^VERSION\s*=\s*\((.+?)\)') + wb = "VERSION = (%(version)s)\n" + type = TupleVersion() + + +class SphinxVersion(VersionFile): + regex = re.compile(r'^:[Vv]ersion:\s*(.+?)$') + wb = ':Version: %(version)s\n' + type = StringVersion() + + +class CPPVersion(VersionFile): + regex = re.compile(r'^\#\s*define\s*(?P<keep>\w*)VERSION\s+(.+)') + wb = '#define %(kept)sVERSION "%(version)s"\n' + type = StringVersion() + + +_filetype_to_type = {"py": PyVersion, + "rst": SphinxVersion, + "c": CPPVersion, + "h": CPPVersion} + +def filetype_to_type(filename): + _, _, suffix = filename.rpartition(".") + return _filetype_to_type[suffix](filename) + + +def bump(*files, **kwargs): + version = kwargs.get("version") + files = [filetype_to_type(f) for f in files] + versions = [v.parse() for v in files] + current = list(reversed(sorted(versions)))[0] # find highest + + if version: + next = from_str(version) + else: + major, minor, release, text = current + if text: + raise Exception("Can't bump alpha releases") + next = (major, minor, release + 1, text) + + print("Bump version from %s -> %s" % (to_str(current), to_str(next))) + + for v in files: + print(" writing %r..." % (v.filename, )) + v.write(next) + + print(cmd("git", "commit", "-m", "Bumps version to %s" % (to_str(next), ), + *[f.filename for f in files])) + print(cmd("git", "tag", "v%s" % (to_str(next), ))) + + +def main(argv=sys.argv, version=None): + if not len(argv) > 1: + print("Usage: distdir [docfile] -- <custom version>") + sys.exit(0) + if "--" in argv: + c = argv.index('--') + version = argv[c + 1] + argv = argv[:c] + bump(*argv[1:], version=version) + +if __name__ == "__main__": + main() |