From b712be346c30403dc9148f001c5ad31996b7188f Mon Sep 17 00:00:00 2001 From: Matt Wright Date: Wed, 15 Aug 2012 15:38:04 -0400 Subject: [PATCH] Add release script --- scripts/release.py | 189 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100755 scripts/release.py diff --git a/scripts/release.py b/scripts/release.py new file mode 100755 index 0000000..b355b82 --- /dev/null +++ b/scripts/release.py @@ -0,0 +1,189 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" + make-release + ~~~~~~~~~~~~ + + Helper script that performs a release. Does pretty much everything + automatically for us. + + :copyright: (c) 2011 by Armin Ronacher. + :license: BSD, see LICENSE for more details. +""" +import sys +import os +import re +from datetime import datetime, date +from subprocess import Popen, PIPE + +_date_clean_re = re.compile(r'(\d+)(st|nd|rd|th)') + + +def installed_libraries(): + return set(Popen(['pip', 'freeze'], stdout=PIPE).communicate()[0].splitlines()) + + +def has_library_installed(library): + return library + '==' in installed_libraries() + + +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(line.strip()): + fail('Invalid hyphen count below version line: %s', line.strip()) + + while 1: + released = lineiter.next().strip() + if released: + break + + match = re.search(r'Released (\w+\s+\d+\w+\s+\d+)', released) + + if match is None: + fail('Could not find release date in version %s' % version) + + datestr = parse_date(match.group(1).strip()) + + return version, datestr + + +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)) + + +def parse_date(string): + string = _date_clean_re.sub(r'\1', string) + return datetime.strptime(string, '%B %d %Y') + + +def set_filename_version(filename, version_number, pattern): + changed = [] + + 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_security/__init__.py', version, '__version__') + + +def set_setup_version(version): + info('Setting setup.py version to %s', version) + set_filename_version('setup.py', version, 'version') + + +def set_docs_version(version): + info('Setting docs/conf.py version to %s', version) + set_filename_version('docs/conf.py', version, 'version') + + +def build_and_upload(): + Popen([sys.executable, 'setup.py', 'sdist', 'build_sphinx', 'upload', 'upload_sphinx']).wait() + + +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', '-a', tag, '-m', '%s release' % tag]).wait() + Popen(['git', 'push', '--tags']).wait() + + +def update_version(version): + for f in [set_init_version, set_setup_version, set_docs_version]: + f(version) + + +def get_branches(): + return set(Popen(['git', 'branch'], stdout=PIPE).communicate()[0].splitlines()) + + +def branch_is(branch): + return '* ' + branch in get_branches() + + +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 = rv + + tags = get_git_tags() + + if version in tags: + fail('Version "%s" is already tagged', version) + if release_date.date() != date.today(): + fail('Release date is not today') + + if not branch_is('master'): + fail('You are not on the master branch') + + if not git_is_clean(): + fail('You have uncommitted changes in git') + + for lib in ['Sphinx', 'Sphinx-PyPI-upload']: + if not has_library_installed(lib): + fail('Build requires that %s be installed', lib) + + info('Releasing %s (release date %s)', + version, release_date.strftime('%d/%m/%Y')) + + update_version(version) + make_git_commit('Bump version number to %s', version) + make_git_tag(version) + build_and_upload() + + +if __name__ == '__main__': + main()