#!/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, see .
import os
import posixpath
import re
import sys
import commands
import time
import commands
def report_error(message):
print message
sys.exit(255)
def get_up_to_date():
"First step is always to get up to date."
os.system("svn up")
# yes, I know this is MY username. I will come back and fix it
# later, but for now there is a lot else to do. FIXME
your_username = 'Thomas Thurman '
def changelog_and_checkin(filename, message):
changelog = open('ChangeLog.tmp', 'w')
changelog.write('%s %s\n\n * %s: %s\n\n' % (
time.strftime('%Y-%m-%d',time.gmtime()),
your_username,
filename,
message))
for line in open('ChangeLog').readlines():
changelog.write(line)
changelog.close()
os.rename('ChangeLog.tmp', 'ChangeLog')
if os.system('svn commit -m "%s"' % (message.replace('"','\\"')))!=0:
report_error("Could not commit; bailing.")
def check_we_are_up_to_date():
changed = []
for line in commands.getoutput('/usr/bin/svn status').split('\n'):
if line!='' and (line[0]=='C' or line[0]=='M'):
if line.find('release-wrangler.py')==-1 and line.find('ChangeLog')==-1:
# we should be insensitive to changes in this script itself
# to avoid chicken-and-egg problems
changed.append(line[1:].lstrip())
if changed:
report_error('These files are out of date; I can\'t continue until you fix them: ' + \
', '.join(changed))
def version_numbers():
# 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_index = versions.index(version_value)+1
if versions[version_index] == version['micro']:
# work around metacity giving "1" twice
version_index += 1
version['micro_next'] = versions[version_index]
except:
report_error("You gave a list of micro version numbers, but we've used them up!")
else:
report_error("You gave a list of micro version numbers, but the current one wasn't in it! Current is %s and your list is %s" % (
`version_value`, `versions`))
previous_line = line
if not 'micro_next' in version:
version['micro_next'] = version['micro']+1
version['string'] = '%(major)s.%(minor)s.%(micro)s' % (version)
version['filename'] = '%(name)s-%(string)s.tar.gz' % (version)
return version
def check_file_does_not_exist(version):
if os.access(version['filename'], os.F_OK):
report_error("Sorry, you already have a file called %s! Please delete it or move it first." % (version['filename']))
def is_date(str):
return len(str)>4 and str[4]=='-'
def scan_changelog(version):
changelog = file("ChangeLog").readlines()
# Find the most recent release.
release_date = None
for line in changelog:
if is_date(line):
release_date = line[:10]
if "Post-release bump to" in line:
changelog = changelog[:changelog.index(line)+1]
break
contributors = {}
thanks = ''
entries = []
def assumed_surname(name):
if name=='': return ''
# might get more complicated later, but for now...
return name.split()[-1]
def assumed_forename(name):
if name=='': return ''
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))
# FIXME: getting complex enough we should be returning a dictionary
return (contributors, changelog, entries, release_date)
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('(',' (')
def favourite_editor():
e = os.environ
if e.has_key('VISUAL'): return e['VISUAL']
if e.has_key('EDITOR'): return e['EDITOR']
if os.access('/usr/bin/nano', os.F_OK):
return '/usr/bin/nano'
report_error("I can't find an editor for you!")
def edit_news_entry(version):
# FIXME: still needs a lot of tidying up. Translator stuff especially needs to be
# factored out into a separate function.
(contributors, changelog, entries, release_date) = scan_changelog(version)
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]])
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]