make-release.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. """
  4. make-release
  5. ~~~~~~~~~~~~
  6. Helper script that performs a release. Does pretty much everything
  7. automatically for us.
  8. :copyright: (c) 2013 by Armin Ronacher.
  9. :license: BSD, see LICENSE for more details.
  10. """
  11. import sys
  12. import os
  13. import re
  14. from datetime import datetime, date
  15. from subprocess import Popen, PIPE
  16. _date_clean_re = re.compile(r'(\d+)(st|nd|rd|th)')
  17. def parse_changelog():
  18. with open('CHANGES') as f:
  19. lineiter = iter(f)
  20. for line in lineiter:
  21. match = re.search('^Version\s+(.*)', line.strip())
  22. if match is None:
  23. continue
  24. version = match.group(1).strip()
  25. if lineiter.next().count('-') != len(match.group(0)):
  26. continue
  27. while 1:
  28. change_info = lineiter.next().strip()
  29. if change_info:
  30. break
  31. match = re.search(r'released on (\w+\s+\d+\w+\s+\d+)'
  32. r'(?:, codename (.*))?', change_info,
  33. flags=re.IGNORECASE)
  34. if match is None:
  35. continue
  36. datestr, codename = match.groups()
  37. return version, parse_date(datestr), codename
  38. def bump_version(version):
  39. try:
  40. parts = map(int, version.split('.'))
  41. except ValueError:
  42. fail('Current version is not numeric')
  43. if parts[-1] != 0:
  44. parts[-1] += 1
  45. else:
  46. parts[0] += 1
  47. return '.'.join(map(str, parts))
  48. def parse_date(string):
  49. string = _date_clean_re.sub(r'\1', string)
  50. return datetime.strptime(string, '%B %d %Y')
  51. def set_filename_version(filename, version_number, pattern):
  52. changed = []
  53. def inject_version(match):
  54. before, old, after = match.groups()
  55. changed.append(True)
  56. return before + version_number + after
  57. with open(filename) as f:
  58. contents = re.sub(r"^(\s*%s\s*=\s*')(.+?)(')" % pattern,
  59. inject_version, f.read(),
  60. flags=re.DOTALL | re.MULTILINE)
  61. if not changed:
  62. fail('Could not find %s in %s', pattern, filename)
  63. with open(filename, 'w') as f:
  64. f.write(contents)
  65. def set_init_version(version):
  66. info('Setting __init__.py version to %s', version)
  67. set_filename_version('babel/__init__.py', version, '__version__')
  68. def set_setup_version(version):
  69. info('Setting setup.py version to %s', version)
  70. set_filename_version('setup.py', version, 'version')
  71. def build_and_upload():
  72. Popen([sys.executable, 'setup.py', 'release', 'sdist', 'upload']).wait()
  73. def fail(message, *args):
  74. print >> sys.stderr, 'Error:', message % args
  75. sys.exit(1)
  76. def info(message, *args):
  77. print >> sys.stderr, message % args
  78. def get_git_tags():
  79. return set(Popen(['git', 'tag'], stdout=PIPE).communicate()[0].splitlines())
  80. def git_is_clean():
  81. return Popen(['git', 'diff', '--quiet']).wait() == 0
  82. def make_git_commit(message, *args):
  83. message = message % args
  84. Popen(['git', 'commit', '-am', message]).wait()
  85. def make_git_tag(tag):
  86. info('Tagging "%s"', tag)
  87. Popen(['git', 'tag', tag]).wait()
  88. def main():
  89. os.chdir(os.path.join(os.path.dirname(__file__), '..'))
  90. rv = parse_changelog()
  91. if rv is None:
  92. fail('Could not parse changelog')
  93. version, release_date, codename = rv
  94. dev_version = bump_version(version) + '-dev'
  95. info('Releasing %s (codename %s, release date %s)',
  96. version, codename, release_date.strftime('%d/%m/%Y'))
  97. tags = get_git_tags()
  98. if version in tags:
  99. fail('Version "%s" is already tagged', version)
  100. if release_date.date() != date.today():
  101. fail('Release date is not today (%s != %s)')
  102. if not git_is_clean():
  103. fail('You have uncommitted changes in git')
  104. set_init_version(version)
  105. set_setup_version(version)
  106. make_git_commit('Bump version number to %s', version)
  107. make_git_tag(version)
  108. build_and_upload()
  109. set_init_version(dev_version)
  110. set_setup_version(dev_version)
  111. if __name__ == '__main__':
  112. main()