1#!/usr/bin/env python2 2 3import argparse 4import datetime 5import inspect 6import logging 7import os 8import re 9import subprocess 10import sys 11import time 12 13 14ansi_brown = "\x1b[0;33;40m" 15ansi_yello_bold = "\x1b[1;33;40m" 16ansi_lblue = "\x1b[0;36;40m" 17ansi_pinky = "\x1b[0;35;40m" 18ansi_reset = "\x1b[0m" 19 20 21log_ts = None 22def log_time(): 23 global log_ts 24 if log_ts == None: 25 log_ts = time.time() 26 ts = time.time() 27 delta_ms = int((ts - log_ts) * 1000) 28 log_ts = ts 29 return "{0: >6}".format(delta_ms) 30 31 32def log_prefix(): 33 line = inspect.stack()[2][2] 34 function = inspect.stack()[2][3] 35 return ansi_lblue + "{0} {1}:{2}".format(log_time(), function, line) + ansi_reset 36 37 38def debug(*args): 39 parts = [log_prefix()] 40 for s in args: 41 parts.append(str(s)) 42 logging.debug(" ".join(parts)) 43 44 45def info(*args): 46 parts = [log_prefix()] 47 for s in args: 48 parts.append(str(s)) 49 logging.info(" ".join(parts)) 50 51 52def cmd(s): 53 logging.debug(log_prefix() + " Executing: " + ansi_brown + s + ansi_reset) 54 return s 55 56 57def run(s): 58 proc = subprocess.Popen(cmd(s), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True) 59 ret = proc.wait() 60 out, err = proc.communicate() 61 for line in out.split("\n"): 62 debug(ansi_pinky + line + ansi_reset) 63 debug(ansi_pinky + "Exit code: " + str(ret)) 64 if ret != 0: 65 print(err) 66 raise Exception("Command failed!") 67 return out 68 69 70def natsorted(ls): 71 dre = re.compile(r'(\d+)') 72 return sorted(ls, key=lambda l: [int(s) if s.isdigit() else s.lower() for s in re.split(dre, l)]) 73 74 75def get_last_version(): 76 tags = natsorted(run("git tag -l 'v*'").split("\n")) 77 return tags[-1] 78 79 80def inc_version(v, feature=False, tiny=False): 81 if v.startswith("v0."): 82 assert v == "v0.14.6" 83 return "v15.0" 84 # v4.11 -> v4.12 or v5.0 or v4.11.1 85 parts = v.split(".") 86 while len(parts) < 3: 87 parts.append("0") 88 assert len(parts) == 3 89 if feature: 90 del parts[-1] 91 parts[-2] = "v" + str(int(parts[-2].replace("v", "")) + 1) 92 parts[-1] = "0" 93 elif tiny: 94 parts[-1] = str(int(parts[-1]) + 1) 95 else: 96 del parts[-1] 97 parts[-1] = str(int(parts[-1]) + 1) 98 return ".".join([s for s in parts if s]) 99 100 101def assert_equal(a, b): 102 if a != b: 103 info(a, "!=", b) 104 assert(False) 105 106 107def test_inc_version(): 108 # auto 109 assert_equal(inc_version("v0.14.6"), "v15.0") 110 assert_equal(inc_version("v15"), "v15.1") 111 assert_equal(inc_version("v15.0"), "v15.1") 112 assert_equal(inc_version("v16.1"), "v16.2") 113 assert_equal(inc_version("v16.10"), "v16.11") 114 # fix 115 assert_equal(inc_version("v0.14.6", False), "v15.0") 116 assert_equal(inc_version("v15", False), "v15.1") 117 assert_equal(inc_version("v15.0", False), "v15.1") 118 assert_equal(inc_version("v16.1", False), "v16.2") 119 assert_equal(inc_version("v16.10", False), "v16.11") 120 # feature 121 assert_equal(inc_version("v15", True), "v16.0") 122 assert_equal(inc_version("v15.0", True), "v16.0") 123 assert_equal(inc_version("v15.1", True), "v16.0") 124 assert_equal(inc_version("v15.2", True), "v16.0") 125 assert_equal(inc_version("v15.10", True), "v16.0") 126 127 128def replace_in_file(path, before, after): 129 with open(path, "r+") as f: 130 old = f.read() 131 new = old.replace(before, after) 132 f.seek(0) 133 f.write(new) 134 135 136def update_man(path, version, date): 137 with open(path, "r+") as f: 138 lines = f.read().split("\n") 139 # # TINT2 1 "2017-03-26" 0.14.1 140 parts = lines[0].split() 141 parts[-2] = '"' + date + '"' 142 parts[-1] = version 143 lines[0] = " ".join(parts) 144 f.seek(0) 145 f.write("\n".join(lines)) 146 run("cd doc ; ./generate-doc.sh") 147 148 149def update_log(path, version, date): 150 with open(path, "r+") as f: 151 lines = f.read().split("\n") 152 f.seek(0) 153 assert lines[0].endswith("master") 154 lines[0] = date + " " + version 155 f.write("\n".join(lines)) 156 157 158if __name__ == '__main__': 159 parser = argparse.ArgumentParser() 160 parser.add_argument("--feature", action="store_true") 161 parser.add_argument("--tiny", action="store_true") 162 args = parser.parse_args() 163 logging.basicConfig(format=ansi_lblue + "%(asctime)s %(pathname)s %(levelname)s" + ansi_reset + " %(message)s", level=logging.DEBUG) 164 test_inc_version() 165 # Read version from last tag and increment 166 old_version = get_last_version() 167 info("Old version:", old_version) 168 version = inc_version(old_version, args.feature, args.tiny) 169 readable_version = version.replace("v", "") 170 date = datetime.datetime.now().strftime("%Y-%m-%d") 171 info("New version:", readable_version, version, date) 172 # Disallow unstaged changes in the working tree 173 run("git diff-files --quiet --ignore-submodules --") 174 # Disallow uncommitted changes in the index 175 run("git diff-index --cached --quiet HEAD --ignore-submodules --") 176 # Update version string 177 replace_in_file("README.md", old_version.replace("v", ""), readable_version) 178 update_man("doc/tint2.md", readable_version, date) 179 update_log("ChangeLog", readable_version, date) 180 run("git commit -am 'Release %s'" % readable_version) 181 run("git tag -a %s -m 'version %s'" % (version, readable_version)) 182 run("git tag -a %s -m 'version %s'" % (readable_version, readable_version)) 183 run("rm -rf tint2-%s* || true" % readable_version) 184 run("./make_release.sh") 185 run("tar -xzf tint2-%s.tar.gz" % readable_version) 186 run("cd tint2-%s ; mkdir build ; cd build ; cmake .. ; make" % readable_version) 187 assert_equal(run("./tint2-%s/build/tint2 -v" % readable_version).strip(), "tint2 version %s" % readable_version) 188 os.system("git log -p -1 --word-diff") 189 print "Does this look correct? [y/n]" 190 choice = raw_input().lower() 191 if choice != "y": 192 run("git reset --hard HEAD~ ; git tag -d %s ; git tag -d %s" % (version, readable_version)) 193 sys.exit(1) 194 print "Publish? [y/n]" 195 choice = raw_input().lower() 196 if choice != "y": 197 sys.exit(1) 198 run("git push origin master && git push --tags origin master") 199