diff options
author | Thomas James Alexander Thurman <tthurman@src.gnome.org> | 2008-02-03 06:02:52 +0000 |
---|---|---|
committer | Thomas James Alexander Thurman <tthurman@src.gnome.org> | 2008-02-03 06:02:52 +0000 |
commit | 0bd1727eab1416bdafb9b3f32dc87029a41bef14 (patch) | |
tree | 2e5decd2bc4551d913a16145c31e259679c407a8 | |
parent | 8bd2822e695daaf3e8201be76c9d5e6e41b1d2f6 (diff) | |
download | metacity-0bd1727eab1416bdafb9b3f32dc87029a41bef14.tar.gz |
First draft of the release script; needs post-release bump adding
svn path=/trunk/; revision=3538
-rw-r--r-- | tools/release-wrangler.py | 277 |
1 files changed, 277 insertions, 0 deletions
diff --git a/tools/release-wrangler.py b/tools/release-wrangler.py new file mode 100644 index 00000000..818179c6 --- /dev/null +++ b/tools/release-wrangler.py @@ -0,0 +1,277 @@ +#!/usr/bin/python +# +# release-wrangler.py - very basic release system, primarily for +# Metacity, might be useful for others. In very early stages of +# development. +# +# Copyright (C) 2008 Thomas Thurman +# +# 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 2 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# This script doesn't do all the work yet, but it will soon. + +import os +import posixpath +import re +import sys +import commands + +# First step is always to get up to date. +os.system("svn up") + +################################################################ + +# Are we up to date now? + +changed = [] +for line in commands.getoutput('/usr/bin/svn status').split('\n'): + if line!='' and (line[0]=='C' or line[0]=='M'): + changed.append(line[1:].lstrip()) + +if changed: + print 'These files are out of date; I can\'t continue until you fix them.' + print ', '.join(changed) + sys.exit(255) + +################################################################ + +# FIXME: This is all very metacity-specific. Compare fusa, etc +# +# Okay, read through configure.in and find who and where we are. +# +# We also try to figure out where the next micro version number +# will be; some programs (e.g. Metacity) use a custom numbering +# scheme, and if they have a list of numbers on the line before the +# micro version then that will be used. Otherwise we will just +# increment. +version = {} +previous_line = '' +for line in file("configure.in").readlines(): + product_name = re.search("^AC_INIT\(\[([^\]]*)\]", line) + if product_name: + version['name'] = product_name.group(1) + + version_number = re.search("^m4_define\(\[.*_(.*)_version\], \[(\d+)\]", line) + + if version_number: + version_type = version_number.group(1) + version_value = int(version_number.group(2)) + + version[version_type] = version_value + + if version_type == 'micro': + group_of_digits = re.search("^\#\s*([0-9, ]+)\n$", previous_line) + if group_of_digits: + versions = [int(x) for x in group_of_digits.group(1).split(',')] + + if version_value in versions: + try: + version['micro_next'] = versions[versions.index(version_value)+1] + except: + print "You gave a list of micro version numbers, but we've used them up!" + sys.exit(255) + else: + print "You gave a list of micro version numbers, but the current one wasn't in it!" + print "Current is ",version_value + print "Your list is ",versions + sys.exit(255) + + previous_line = line + +if not 'micro_next' in version: + version['micro_next'] = version['micro']+1 + +################################################################ + +archive_filename = '%(name)s-%(major)s.%(minor)s.%(micro)s.tar.gz' % (version) +if os.access(archive_filename, os.F_OK): + print "Sorry, you already have a file called %s! Please delete it or move it first." % (archive_filename) + sys.exit(255) + +################################################################ + +changelog = file("ChangeLog").readlines() + +# Find the most recent release. + +def is_date(str): + return len(str)>3 and str[4]=='-' + +release_date = None + +for line in changelog: + if is_date(line): + release_date = line[:10] + if "Post-release bump to %s.%s.%s." % (version['major'], version['minor'], version['micro']) in line: + changelog = changelog[:changelog.index(line)+1] + break + +contributors = {} +thanks = '' +entries = [] + +def assumed_surname(name): + # might get more complicated later, but for now... + return name.split()[-1] + +def assumed_forename(name): + return name.split()[0] + +bug_re = re.compile('bug \#?(\d+)', re.IGNORECASE) +hash_re = re.compile('\#(\d+)') + +for line in changelog: + if is_date(line): + line = line[10:].lstrip() + line = line[:line.find('<')].rstrip() + contributors[assumed_surname(line)] = line + entries.append('(%s)' % (assumed_forename(line))) + else: + match = bug_re.search(line) + if not match: match = hash_re.search(line) + if match: + entries[-1] += ' (#%s)' % (match.group(1)) + +contributors_list = contributors.keys() +contributors_list.sort() +thanksline = ', '.join([contributors[x] for x in contributors_list]) +thanksline = thanksline.replace(contributors[contributors_list[-1]], 'and '+contributors[contributors_list[-1]]) + +version_string = '%(major)s.%(minor)s.%(micro)s' % (version) + +def wordwrap(str, prefix=''): + "Really simple wordwrap" + + # Ugly hack: + # We know that all open brackets are preceded by spaces. + # We don't want to split on these spaces. Therefore: + str = str.replace(' (','(') + + result = [''] + for word in str.split(): + + if result[-1]=='': + candidate = prefix + word + else: + candidate = '%s %s' % (result[-1], word) + + if len(candidate)>80: + result.append(prefix+word) + else: + result[-1] = candidate + + return '\n'.join(result).replace('(',' (') + +thanks = '%s\n%s\n\n' % (version_string, '='*len(version_string)) +thanks += wordwrap('Thanks to %s for improvements in this version.' % (thanksline)) +thanks += '\n\n' +for line in entries: + thanks += ' - xxx %s\n' % (line) + +# and now pick up the translations. + +translations = {} +language_re = re.compile('\*\s*(.+)\.po') + +for line in file("po/ChangeLog").readlines(): + match = language_re.search(line) + if match: + translations[match.group(1)] = 1 + if is_date(line) and line[:10]<release_date: + break + +translator_list = translations.keys() +translator_list.sort() + +last_translator_re = re.compile('Last-Translator:([^<"]*)', re.IGNORECASE) + +def translator_name(language): + name = 'unknown' + for line in file('po/%s.po' % (language)).readlines(): + match = last_translator_re.search(line) + if match: + name = match.group(1).rstrip().lstrip() + break + return "%s (%s)" % (name, language) + +thanks += '\nTranslations\n' +thanks += wordwrap(', '.join([translator_name(x) for x in translator_list]), ' ') +thanks += '\n\n' + +changes = '## '+ ' '.join(changelog).replace('\n', '\n## ') + +filename = posixpath.expanduser("~/.release-wrangler-%(name)s-%(major)s-%(minor)s-%(micro)s.txt" % version) +tmp = open(filename, 'w') +tmp.write('## You are releasing %(name)s, version %(major)s.%(minor)s.%(micro)s.\n' % version) +tmp.write('## The text at the foot of the page is the part of the ChangeLog which\n') +tmp.write('## has changed since the last release. Please summarise it.\n') +tmp.write('## Anything preceded by a # is ignored.\n') +tmp.write(thanks) +tmp.write(changes) +tmp.close() + +os.spawnl(os.P_WAIT, '/bin/nano', 'nano', '+6', filename) + +################################################################ + +# Write it out to NEWS + +news_tmp = open('NEWS.tmp', 'a') +for line in open(filename, 'r').readlines(): + if line=='' or line[0]!='#': + news_tmp.write(line) + +for line in open('NEWS').readlines(): + news_tmp.write(line) + +news_tmp.close() + +os.rename('NEWS.tmp', 'NEWS') + +################################################################ + +# Now build the thing. + +autogen_prefix= '/prefix' # FIXME: this is specific to tthurman's laptop! + +if os.spawnl(os.P_WAIT, './autogen.sh', './autogen.sh', '--prefix', autogen_prefix) != 0: + print 'autogen failed' + sys.exit(255) + +if os.spawnl(os.P_WAIT, '/usr/bin/make', '/usr/bin/make') != 0: + print 'make failed' + sys.exit(255) + +if os.spawnl(os.P_WAIT, '/usr/bin/make', '/usr/bin/make', 'install') != 0: + print 'install failed' + sys.exit(255) + +if os.spawnl(os.P_WAIT, '/usr/bin/make', '/usr/bin/make', 'distcheck') != 0: + print 'distcheck failed' + sys.exit(255) + +if not os.access(archive_filename, os.F_OK): + print "Sorry, we don't appear to have a file called %s!" % (archive_filename) + sys.exit(255) + +# No, we won't have a configuration option to set your name on svn.g.o; that's +# what ~/.ssh/config is for. + +print "Uploading..." +upload_result = commands.getstatusoutput('scp %s master.gnome.org:' % (archive_filename)) + +if upload_result[0]!=0: + print "There appears to have been an uploading problem: %d\n%s\n" % (upload_result[0], upload_result[1]) |