From 7f5806d78aa50f316234200284658c41a30e71c3 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Wed, 31 Jul 2013 23:08:26 +0200 Subject: [PATCH] Updated release and development flow to be automated --- .travis.yml | 5 +- CHANGES | 31 ++++--- Makefile | 23 +++++- flask_sqlalchemy/__init__.py | 3 + scripts/make-release.py | 154 +++++++++++++++++++++++++++++++++++ setup.cfg | 13 +-- setup.py | 2 +- 7 files changed, 210 insertions(+), 21 deletions(-) create mode 100755 scripts/make-release.py diff --git a/.travis.yml b/.travis.yml index 9992aa01..ecd6f90e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,10 @@ python: - "pypy" - "3.3" -script: python setup.py test +install: + - pip install --editable . + +script: make test notifications: email: false diff --git a/CHANGES b/CHANGES index a9dbfa2d..eb905d76 100644 --- a/CHANGES +++ b/CHANGES @@ -1,11 +1,18 @@ Changelog ---------- +========= Here you can see the full list of changes between each Flask-SQLAlchemy release. +Version 1.1 +----------- + +(Release date to be decided, codename to be selected) + Version 1.0 -``````````` +----------- + +(Released on July 20th 2013, no codename) - Added Python 3.3 support. - Dropped 2.5 compatibility. @@ -13,28 +20,28 @@ Version 1.0 - Changed versioning format to do major releases for each update now. Version 0.16 -```````````` +------------ - New distribution format (flask_sqlalchemy) - Added support for Flask 0.9 specifics. Version 0.15 -```````````` +------------ - Added session support for multiple databases Version 0.14 -```````````` +------------ - Make relative sqlite paths relative to the application root. Version 0.13 -```````````` +------------ - Fixed an issue with Flask-SQLAlchemy not selecting the correct binds. Version 0.12 -```````````` +------------ - Added support for multiple databases. - Expose Flask-SQLAlchemy's BaseQuery as `db.Query`. - Set default query_class for `db.relation`, `db.relationship`, and @@ -42,12 +49,12 @@ Version 0.12 - Improved compatibility with Flask 0.7. Version 0.11 -```````````` +------------ - Fixed a bug introduced in 0.10 with alternative table constructors. Version 0.10 -```````````` +------------ - Added support for signals. - Table names are now automatically set from the class name unless @@ -61,18 +68,18 @@ Version 0.10 This makes it possible to omit the metadata. Version 0.9 -``````````` +----------- - applied changes to pass the Flask extension approval process. Version 0.8 -``````````` +----------- - added a few configuration keys for creating connections. - automatically activate connection recycling for MySQL connections. - added support for the Flask testing mode. Version 0.7 -``````````` +----------- - Initial public release diff --git a/Makefile b/Makefile index c5637dcc..ee785cb7 100644 --- a/Makefile +++ b/Makefile @@ -3,4 +3,25 @@ all: test test: - python setup.py test + @python test_sqlalchemy.py + +clean: clean-pyc + +clean-pyc: + @find . -name '*.pyc' -exec rm {} \; + @find . -name '__pycache__' -type d | xargs rm -rf + +develop: + @pip install --editable . + +release: + @python scripts/make-release.py + +tox-test: + @tox + +upload-docs: + $(MAKE) -C docs html + python setup.py upload-docs + +.PHONY: test release all clean clean-pyc develop tox-test upload-docs diff --git a/flask_sqlalchemy/__init__.py b/flask_sqlalchemy/__init__.py index 93092447..b949b90f 100644 --- a/flask_sqlalchemy/__init__.py +++ b/flask_sqlalchemy/__init__.py @@ -42,6 +42,9 @@ _app_ctx_stack = None +__version__ = '1.1-dev' + + # Which stack should we use? _app_ctx_stack is new in 0.9 connection_stack = _app_ctx_stack or _request_ctx_stack diff --git a/scripts/make-release.py b/scripts/make-release.py new file mode 100755 index 00000000..92caf8d5 --- /dev/null +++ b/scripts/make-release.py @@ -0,0 +1,154 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" + make-release + ~~~~~~~~~~~~ + + Helper script that performs a release. Does pretty much everything + automatically for us. + + :copyright: (c) 2013 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 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 + length = len(match.group(1)) + 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 + + match = re.search(r'released on (\w+\s+\d+\w+\s+\d+)' + r'(?:, codename (.*))?(?i)', change_info) + if match is None: + continue + + datestr, codename = match.groups() + return version, parse_date(datestr), codename + + +def bump_version(version): + try: + parts = map(int, version.split('.')) + except ValueError: + fail('Current version is not numeric') + if parts[-1] != 0: + parts[-1] += 1 + else: + parts[0] += 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_sqlalchemy/__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 build_and_upload(): + Popen([sys.executable, 'setup.py', 'release', 'sdist', 'upload']).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', 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 + dev_version = bump_version(version) + '-dev' + + 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(): + fail('Release date is not today (%s != %s)') + + if not git_is_clean(): + fail('You have uncommitted changes in git') + + set_init_version(version) + set_setup_version(version) + make_git_commit('Bump version number to %s', version) + make_git_tag(version) + build_and_upload() + set_init_version(dev_version) + set_setup_version(dev_version) + + +if __name__ == '__main__': + main() diff --git a/setup.cfg b/setup.cfg index 60070cd2..ffe7c8f9 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,7 +1,8 @@ -[build_sphinx] -source-dir = docs/ -build-dir = docs/_build -all_files = 1 +[egg_info] +tag_date = true -[upload_docs] -upload-dir = docs/_build/html +[aliases] +release = egg_info -RDb '' + +[pytest] +norecursedirs = .* _* scripts {args} diff --git a/setup.py b/setup.py index 014c3d90..0c632714 100644 --- a/setup.py +++ b/setup.py @@ -17,7 +17,7 @@ setup( name='Flask-SQLAlchemy', - version='1.0', + version='1.1-dev', url='http://github.com/mitsuhiko/flask-sqlalchemy', license='BSD', author='Armin Ronacher',