| 
									
										
										
										
											2011-06-27 06:31:24 +08:00
										 |  |  | #!/usr/bin/env python | 
					
						
							|  |  |  | # -*- coding: utf-8 -*- | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  |     make-release | 
					
						
							|  |  |  |     ~~~~~~~~~~~~ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Helper script that performs a release.  Does pretty much everything | 
					
						
							|  |  |  |     automatically for us. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-02 10:35:00 +08:00
										 |  |  |     :copyright: (c) 2015 by Armin Ronacher. | 
					
						
							| 
									
										
										
										
											2011-06-27 06:31:24 +08:00
										 |  |  |     :license: BSD, see LICENSE for more details. | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  | import sys | 
					
						
							|  |  |  | import os | 
					
						
							|  |  |  | import re | 
					
						
							|  |  |  | from datetime import datetime, date | 
					
						
							|  |  |  | from subprocess import Popen, PIPE | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-08-08 07:26:39 +08:00
										 |  |  | _date_clean_re = re.compile(r'(\d+)(st|nd|rd|th)') | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-06-27 06:31:24 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | def parse_changelog(): | 
					
						
							|  |  |  |     with open('CHANGES') as f: | 
					
						
							|  |  |  |         lineiter = iter(f) | 
					
						
							|  |  |  |         for line in lineiter: | 
					
						
							|  |  |  |             match = re.search('^Version\s+(.*)', line.strip()) | 
					
						
							|  |  |  |             if match is None: | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |             version = match.group(1).strip() | 
					
						
							|  |  |  |             if lineiter.next().count('-') != len(match.group(0)): | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |             while 1: | 
					
						
							|  |  |  |                 change_info = lineiter.next().strip() | 
					
						
							|  |  |  |                 if change_info: | 
					
						
							|  |  |  |                     break | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-06-30 00:37:08 +08:00
										 |  |  |             match = re.search(r'released on (\w+\s+\d+\w+\s+\d+)' | 
					
						
							|  |  |  |                               r'(?:, codename (.*))?(?i)', change_info) | 
					
						
							| 
									
										
										
										
											2011-06-27 06:31:24 +08:00
										 |  |  |             if match is None: | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             datestr, codename = match.groups() | 
					
						
							|  |  |  |             return version, parse_date(datestr), codename | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-06-27 06:34:15 +08:00
										 |  |  | def bump_version(version): | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         parts = map(int, version.split('.')) | 
					
						
							|  |  |  |     except ValueError: | 
					
						
							|  |  |  |         fail('Current version is not numeric') | 
					
						
							|  |  |  |     parts[-1] += 1 | 
					
						
							|  |  |  |     return '.'.join(map(str, parts)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-06-27 06:31:24 +08:00
										 |  |  | def parse_date(string): | 
					
						
							| 
									
										
										
										
											2011-08-08 07:26:39 +08:00
										 |  |  |     string = _date_clean_re.sub(r'\1', string) | 
					
						
							| 
									
										
										
										
											2011-06-27 06:31:24 +08:00
										 |  |  |     return datetime.strptime(string, '%B %d %Y') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def set_filename_version(filename, version_number, pattern): | 
					
						
							|  |  |  |     changed = [] | 
					
						
							| 
									
										
										
										
											2014-04-05 17:38:28 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-06-27 06:31:24 +08:00
										 |  |  |     def inject_version(match): | 
					
						
							|  |  |  |         before, old, after = match.groups() | 
					
						
							|  |  |  |         changed.append(True) | 
					
						
							|  |  |  |         return before + version_number + after | 
					
						
							|  |  |  |     with open(filename) as f: | 
					
						
							|  |  |  |         contents = re.sub(r"^(\s*%s\s*=\s*')(.+?)(')(?sm)" % pattern, | 
					
						
							|  |  |  |                           inject_version, f.read()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if not changed: | 
					
						
							|  |  |  |         fail('Could not find %s in %s', pattern, filename) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     with open(filename, 'w') as f: | 
					
						
							|  |  |  |         f.write(contents) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def set_init_version(version): | 
					
						
							|  |  |  |     info('Setting __init__.py version to %s', version) | 
					
						
							|  |  |  |     set_filename_version('flask/__init__.py', version, '__version__') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def build_and_upload(): | 
					
						
							| 
									
										
										
										
											2016-03-28 01:36:57 +08:00
										 |  |  |     Popen([sys.executable, 'setup.py', 'release', 'sdist', 'bdist_wheel', 'upload']).wait() | 
					
						
							| 
									
										
										
										
											2011-06-27 06:31:24 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def fail(message, *args): | 
					
						
							|  |  |  |     print >> sys.stderr, 'Error:', message % args | 
					
						
							|  |  |  |     sys.exit(1) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def info(message, *args): | 
					
						
							|  |  |  |     print >> sys.stderr, message % args | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def get_git_tags(): | 
					
						
							|  |  |  |     return set(Popen(['git', 'tag'], stdout=PIPE).communicate()[0].splitlines()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def git_is_clean(): | 
					
						
							|  |  |  |     return Popen(['git', 'diff', '--quiet']).wait() == 0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def make_git_commit(message, *args): | 
					
						
							|  |  |  |     message = message % args | 
					
						
							|  |  |  |     Popen(['git', 'commit', '-am', message]).wait() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def make_git_tag(tag): | 
					
						
							|  |  |  |     info('Tagging "%s"', tag) | 
					
						
							|  |  |  |     Popen(['git', 'tag', tag]).wait() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def main(): | 
					
						
							|  |  |  |     os.chdir(os.path.join(os.path.dirname(__file__), '..')) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     rv = parse_changelog() | 
					
						
							|  |  |  |     if rv is None: | 
					
						
							|  |  |  |         fail('Could not parse changelog') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     version, release_date, codename = rv | 
					
						
							| 
									
										
										
										
											2011-06-27 06:34:15 +08:00
										 |  |  |     dev_version = bump_version(version) + '-dev' | 
					
						
							| 
									
										
										
										
											2011-06-27 06:31:24 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     info('Releasing %s (codename %s, release date %s)', | 
					
						
							|  |  |  |          version, codename, release_date.strftime('%d/%m/%Y')) | 
					
						
							|  |  |  |     tags = get_git_tags() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if version in tags: | 
					
						
							|  |  |  |         fail('Version "%s" is already tagged', version) | 
					
						
							|  |  |  |     if release_date.date() != date.today(): | 
					
						
							| 
									
										
										
										
											2014-04-05 17:38:28 +08:00
										 |  |  |         fail('Release date is not today (%s != %s)', | 
					
						
							|  |  |  |              release_date.date(), date.today()) | 
					
						
							| 
									
										
										
										
											2011-06-27 06:31:24 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if not git_is_clean(): | 
					
						
							|  |  |  |         fail('You have uncommitted changes in git') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     set_init_version(version) | 
					
						
							|  |  |  |     make_git_commit('Bump version number to %s', version) | 
					
						
							|  |  |  |     make_git_tag(version) | 
					
						
							|  |  |  |     build_and_upload() | 
					
						
							| 
									
										
										
										
											2011-06-27 06:34:15 +08:00
										 |  |  |     set_init_version(dev_version) | 
					
						
							| 
									
										
										
										
											2011-06-27 06:31:24 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if __name__ == '__main__': | 
					
						
							|  |  |  |     main() |