basic release script; needs work, but probably good enough for the current

2008-02-03  Thomas Thurman  <tthurman@gnome.org>

        * tools/release-wrangler.py: basic release script; needs work,
        but probably good enough for the current unstable release


svn path=/trunk/; revision=3541
This commit is contained in:
Thomas Thurman 2008-02-03 21:47:09 +00:00 committed by Thomas James Alexander Thurman
parent a660fd3805
commit 836a1f7b08
2 changed files with 230 additions and 161 deletions

View File

@ -1,3 +1,8 @@
2008-02-03 Thomas Thurman <tthurman@gnome.org>
* tools/release-wrangler.py: basic release script; needs work,
but probably good enough for the current unstable release
2008-02-02 Thomas Thurman <tthurman@gnome.org> 2008-02-02 Thomas Thurman <tthurman@gnome.org>
* src/Makefile.am: core.h is in include, not core. (Last one, I * src/Makefile.am: core.h is in include, not core. (Last one, I

View File

@ -21,136 +21,148 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
# 02111-1307, USA. # 02111-1307, USA.
# This script doesn't do all the work yet, but it will soon.
import os import os
import posixpath import posixpath
import re import re
import sys import sys
import commands import commands
import time
# First step is always to get up to date. def report_error(message):
os.system("svn up") print message
################################################################
# 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) sys.exit(255)
################################################################ def get_up_to_date():
"First step is always to get up to date."
os.system("svn up")
# FIXME: This is all very metacity-specific. Compare fusa, etc # 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
# Okay, read through configure.in and find who and where we are. your_username = 'Thomas Thurman <tthurman@gnome.org>'
#
# 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) 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))
if version_number: for line in open('ChangeLog').readlines():
version_type = version_number.group(1) changelog.write(line)
version_value = int(version_number.group(2))
version[version_type] = version_value changelog.close()
os.rename('ChangeLog.tmp', 'ChangeLog')
if version_type == 'micro': os.system('svn commit -m \\"%s\\"' % (message.replace('"','\\"')))
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: def check_we_are_up_to_date():
try: changed = []
version['micro_next'] = versions[versions.index(version_value)+1] for line in commands.getoutput('/usr/bin/svn status').split('\n'):
except: if line!='' and (line[0]=='C' or line[0]=='M'):
print "You gave a list of micro version numbers, but we've used them up!" changed.append(line[1:].lstrip())
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 changed:
report_error('These files are out of date; I can\'t continue until you fix them: ' + \
', '.join(changed))
if not 'micro_next' in version: def version_numbers():
version['micro_next'] = version['micro']+1 # 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."""
archive_filename = '%(name)s-%(major)s.%(minor)s.%(micro)s.tar.gz' % (version) version = {}
if os.access(archive_filename, os.F_OK): previous_line = ''
print "Sorry, you already have a file called %s! Please delete it or move it first." % (archive_filename) for line in file("configure.in").readlines():
sys.exit(255) 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)
changelog = file("ChangeLog").readlines() if version_number:
version_type = version_number.group(1)
version_value = int(version_number.group(2))
# Find the most recent release. 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:
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): def is_date(str):
return len(str)>3 and str[4]=='-' return len(str)>3 and str[4]=='-'
release_date = None def scan_changelog(version):
changelog = file("ChangeLog").readlines()
for line in changelog: # Find the most recent release.
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 = {} release_date = None
thanks = ''
entries = []
def assumed_surname(name): for line in changelog:
# might get more complicated later, but for now... if is_date(line):
return name.split()[-1] 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
def assumed_forename(name): contributors = {}
return name.split()[0] thanks = ''
entries = []
bug_re = re.compile('bug \#?(\d+)', re.IGNORECASE) def assumed_surname(name):
hash_re = re.compile('\#(\d+)') # might get more complicated later, but for now...
return name.split()[-1]
for line in changelog: def assumed_forename(name):
if is_date(line): return name.split()[0]
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() bug_re = re.compile('bug \#?(\d+)', re.IGNORECASE)
contributors_list.sort() hash_re = re.compile('\#(\d+)')
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) 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=''): def wordwrap(str, prefix=''):
"Really simple wordwrap" "Really simple wordwrap"
@ -175,103 +187,155 @@ def wordwrap(str, prefix=''):
return '\n'.join(result).replace('(',' (') return '\n'.join(result).replace('(',' (')
thanks = '%s\n%s\n\n' % (version_string, '='*len(version_string)) def favourite_editor():
thanks += wordwrap('Thanks to %s for improvements in this version.' % (thanksline)) e = os.environ
thanks += '\n\n' if e.has_key('VISUAL'): return e['VISUAL']
for line in entries: if e.has_key('EDITOR'): return e['EDITOR']
thanks += ' - xxx %s\n' % (line) if os.access('/usr/bin/nano', os.F_OK):
return '/usr/bin/nano'
report_error("I can't find an editor for you!")
# and now pick up the translations. def edit_news_entry(version):
translations = {} # FIXME: still needs a lot of tidying up. Translator stuff especially needs to be
language_re = re.compile('\*\s*(.+)\.po') # factored out into a separate function.
for line in file("po/ChangeLog").readlines(): (contributors, changelog, entries, release_date) = scan_changelog(version)
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() contributors_list = contributors.keys()
translator_list.sort() contributors_list.sort()
thanksline = ', '.join([contributors[x] for x in contributors_list])
thanksline = thanksline.replace(contributors[contributors_list[-1]], 'and '+contributors[contributors_list[-1]])
last_translator_re = re.compile('Last-Translator:([^<"]*)', re.IGNORECASE) 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)
def translator_name(language): # and now pick up the translations.
name = 'unknown'
for line in file('po/%s.po' % (language)).readlines(): translations = {}
match = last_translator_re.search(line) language_re = re.compile('\*\s*(.+)\.po')
for line in file("po/ChangeLog").readlines():
match = language_re.search(line)
if match: if match:
name = match.group(1).rstrip().lstrip() translations[match.group(1)] = 1
if is_date(line) and line[:10]<release_date:
break break
return "%s (%s)" % (name, language)
thanks += '\nTranslations\n' translator_list = translations.keys()
thanks += wordwrap(', '.join([translator_name(x) for x in translator_list]), ' ') translator_list.sort()
thanks += '\n\n'
changes = '## '+ ' '.join(changelog).replace('\n', '\n## ') last_translator_re = re.compile('Last-Translator:([^<"]*)', re.IGNORECASE)
filename = posixpath.expanduser("~/.release-wrangler-%(name)s-%(major)s-%(minor)s-%(micro)s.txt" % version) def translator_name(language):
tmp = open(filename, 'w') name = 'unknown'
tmp.write('## You are releasing %(name)s, version %(major)s.%(minor)s.%(micro)s.\n' % version) for line in file('po/%s.po' % (language)).readlines():
tmp.write('## The text at the foot of the page is the part of the ChangeLog which\n') match = last_translator_re.search(line)
tmp.write('## has changed since the last release. Please summarise it.\n') if match:
tmp.write('## Anything preceded by a # is ignored.\n') name = match.group(1).rstrip().lstrip()
tmp.write(thanks) break
tmp.write(changes) return "%s (%s)" % (name, language)
tmp.close()
os.spawnl(os.P_WAIT, '/bin/nano', 'nano', '+6', filename) thanks += '\nTranslations\n'
thanks += wordwrap(', '.join([translator_name(x) for x in translator_list]), ' ')
thanks += '\n\n'
################################################################ changes = '## '+ ' '.join(changelog).replace('\n', '\n## ')
# Write it out to NEWS filename = posixpath.expanduser("~/.release-wrangler-%(name)s-%(string)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()
news_tmp = open('NEWS.tmp', 'a') os.system(favourite_editor()+' +6 %s ' % (filename))
for line in open(filename, 'r').readlines():
if line=='' or line[0]!='#': # 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.write(line)
for line in open('NEWS').readlines(): news_tmp.close()
news_tmp.write(line)
news_tmp.close() os.rename('NEWS.tmp', 'NEWS')
changelog_and_checkin('NEWS', '%(major)s.%(minor)s.%(micro)s release.' % (version))
os.rename('NEWS.tmp', 'NEWS') def build_it_all(version):
"Now build the thing."
autogen_prefix= '/prefix' # FIXME: this is specific to tthurman's laptop!
################################################################ # FIXME: These should use os.system
# Now build the thing. if os.spawnl(os.P_WAIT, './autogen.sh', './autogen.sh', '--prefix', autogen_prefix) != 0:
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' print 'autogen failed'
sys.exit(255) sys.exit(255)
if os.spawnl(os.P_WAIT, '/usr/bin/make', '/usr/bin/make') != 0: if os.spawnl(os.P_WAIT, '/usr/bin/make', '/usr/bin/make') != 0:
print 'make failed' print 'make failed'
sys.exit(255) sys.exit(255)
if os.spawnl(os.P_WAIT, '/usr/bin/make', '/usr/bin/make', 'install') != 0: if os.spawnl(os.P_WAIT, '/usr/bin/make', '/usr/bin/make', 'install') != 0:
print 'install failed' print 'install failed'
sys.exit(255) sys.exit(255)
if os.spawnl(os.P_WAIT, '/usr/bin/make', '/usr/bin/make', 'distcheck') != 0: if os.spawnl(os.P_WAIT, '/usr/bin/make', '/usr/bin/make', 'distcheck') != 0:
print 'distcheck failed' print 'distcheck failed'
sys.exit(255) sys.exit(255)
if not os.access(archive_filename, os.F_OK): if not os.access(version['filename'], os.F_OK):
print "Sorry, we don't appear to have a file called %s!" % (archive_filename) print "Sorry, we don't appear to have a file called %s!" % (archive_filename)
sys.exit(255) sys.exit(255)
# No, we won't have a configuration option to set your name on svn.g.o; that's def upload(version):
# what ~/.ssh/config is for. # No, we won't have a configuration option to set your name on master.g.o; that's
# what ~/.ssh/config is for.
print "Uploading..." print "Uploading..."
upload_result = commands.getstatusoutput('scp %s master.gnome.org:' % (archive_filename)) upload_result = commands.getstatusoutput('scp %s master.gnome.org:' % (version['filename']))
if upload_result[0]!=0:
report_error("There appears to have been an uploading problem: %d\n%s\n" % (upload_result[0], upload_result[1]))
def increment_version(version):
configure_in = file('configure.in.tmp', 'w')
for line in file('configure.in'):
if re.search("^m4_define\(\[.*_micro_version\], \[(\d+)\]", line):
line = line.replace('[%(micro)s]' % version, '[%(micro_next)s]' % version)
configure_in.write(line)
configure_in.close()
os.rename('configure.in.tmp', 'configure.in')
changelog_and_checkin('configure.in', 'Post-release bump to %(major)s.%(minor)s.%(micro_next)s.' % version)
def tag_the_release(version):
version['ucname'] = name.upper()
os.system("svn cp -m release . svn+ssh://svn.gnome.org/svn/%(name)s/tags/%(ucname)s_%(major)s_%(minor)_%(micro)" % (version))
def main():
get_up_to_date()
check_we_are_up_to_date()
version = version_numbers()
check_file_does_not_exist(version)
edit_news_entry(version)
build_it_all(version)
tag_the_release(version)
increment_version(version)
upload(version)
print "-- Done --"
if __name__=='__main__':
main()
if upload_result[0]!=0:
print "There appears to have been an uploading problem: %d\n%s\n" % (upload_result[0], upload_result[1])