#!/usr/bin/env python2 # Copyright 2016 Lars Wirzenius # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # =*= License: GPL-3+ =*= import re import time import cliapp import bumperlib class Bumper(cliapp.Application): def process_args(self, args): project_name = self.get_project_name() version = self.get_version(args) print 'Releasing {} version {}'.format(project_name, version) current_version = self.get_current_version() if not self.version_is_newer(version, current_version): raise cliapp.AppException( 'New version {} is older than current version {}'.format( version, current_version)) version_py = self.find_version_py() print '... {}'.format(version_py) self.write_version_py(version_py, version, '') print '... debian/changelog' self.update_debian_changelog(version, 'New upstream version.') self.release_debian_changelog() print '... NEWS' self.update_NEWS_for_release(version) print '... git commit' self.commit(version, 'Prepare to release version {}'.format(version)) print '... git tag' self.make_release_tag(version) gitversion = version + '+git' print 'Updating in-development version to', gitversion print '... {}'.format(version_py) self.write_version_py(version_py, version, '+git') print '... debian/changelog' self.update_debian_changelog(gitversion, '') print '... NEWS' self.update_NEWS_for_git(gitversion) print '... git commit' self.commit( version, 'Bump version number post-release to {}'.format(gitversion)) def get_version(self, args): if len(args) != 1: raise cliapp.AppException('Need exactly one argument, the version') return args[0] def find_version_py(self): output = cliapp.runcmd(['git', 'ls-files']) filenames = [x.strip() for x in output.splitlines()] version_pys = [x for x in filenames if x.endswith('/version.py')] if len(version_pys) == 0: raise cliapp.AppException('No version.py in project') elif len(version_pys) > 1: raise cliapp.AppException('Too many version.py in project') return version_pys[0] def get_current_version(self): return cliapp.runcmd(['python', 'setup.py', '--version']).strip() def version_is_newer(self, v1, v2): '''Is v1 newer than v2?''' vi1 = self.parse_version_info(v1, None) vi2 = self.parse_version_info(v2, None) returncode, out, err = cliapp.runcmd_unchecked( ['dpkg', '--compare-versions', v1, 'gt', v2]) return returncode == 0 def update_debian_changelog(self, version, msg): debian_version = '{}-1'.format(version) cliapp.runcmd(['dch', '-v', debian_version, msg]) def release_debian_changelog(self): cliapp.runcmd(['dch', '-r', '']) def update_NEWS_for_release(self, version): with open('NEWS') as f: text = f.read() date = time.strftime('%Y-%m-%d') pattern = r'^Version \d+(\.\d+)*(\+git)?, not yet released$' replacement = 'Version {}, released {}'.format(version, date) updated = re.sub(pattern, replacement, text, count=1, flags=re.M) with open('NEWS', 'w') as f: f.write(updated) def update_NEWS_for_git(self, version): with open('NEWS') as f: text = f.read() pattern = r'^Version \d+(\.\d+)*, released \d\d\d\d-\d\d-\d\d$' replacement = 'Version {}, not yet released'.format(version) match = re.search(pattern, text, flags=re.M) if not match: raise cliapp.AppException('No place to insert new entry in NEWS') before, after = text[:match.start()], text[match.start():] with open('NEWS', 'w') as f: f.write(before) f.write(replacement + '\n') f.write('-' * len(replacement)) f.write('\n\n\n') f.write(after) def commit(self, version, msg): cliapp.runcmd(['git', 'commit', '-am', msg]) def make_release_tag(self, version): name = self.get_project_name() tag_name = '{}-{}'.format(name, version) msg = 'Release version {}'.format(version) cliapp.runcmd(['git', 'tag', '-sam', msg, tag_name]) def get_project_name(self): output = cliapp.runcmd(['python', 'setup.py', '--name']) return output.strip() def write_version_py(self, filename, version, suffix): version_info = self.parse_version_info(version, suffix) with open(filename, 'w') as f: f.write('__version__ = "{}"\n'.format(version + suffix)) f.write('__version_info__ = {!r}\n'.format(version_info)) def parse_version_info(self, version, suffix): parts = version.split('.') result = [] for part in parts: try: result.append(int(part)) except ValueError: result.append(part) if suffix: result.append(suffix) return tuple(result) Bumper(version=bumperlib.__version__).run()