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