#! /usr/bin/env python # # Copyright (C) 1998-2006 by the Free Software Foundation, Inc. # # 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, # USA. # XXX This file does not need to be compatible with Python 2.1. It is only # used by the release manager. import os import re import sys import time import errno import optparse import tempfile from subprocess import Popen, PIPE from urlparse import urlparse __revision__ = '$Revision: 8006 $' parts = __revision__.split() if len(parts) == 3: __version__ = parts[1] else: __version__ = parts[0] SLASH = '/' SPACE = ' ' def calculate_urls(relname): srcurl = None stdout, stderr = do('svn info') for line in stdout.splitlines(): key, val = line.split(':', 1) if key.lower() == 'url': srcurl = val break else: print >> sys.stderr, 'No source url found' sys.exit(1) scheme, netloc, path, params, query, frag = urlparse(srcurl) if params or query or frag: print >> sys.stderr, 'src url has params, query and/or frag' sys.exit(1) parts = path.split(SLASH) # XXX Fix this to work on the trunk too for i, part in enumerate(parts): if part == 'branches': break else: print >> sys.stderr, 'No branches directory found in src url' sys.exit(1) del parts[i:] parts.extend(['tags', relname]) dsturl = SLASH.join(parts) return srcurl, dsturl def do(cmd): proc = Popen(cmd.split(), stdout=PIPE, stderr=PIPE) stdout, stderr = proc.communicate() return stdout, stderr def now(): return time.strftime('%d-%b-%Y', time.localtime(time.time())) def releasedir(tagname=None): tmpdir = tempfile.gettempdir() return os.path.join(tmpdir, 'mailman-' + tagname) def tag2rel(tagname): return 'Release_' + tagname.replace('.', '_') def tag_release(tagname): # Convert dots in tagname to underscores relname = tag2rel(tagname) # Calculate the 'tags' directory, which should be a sibling of the # 'branches' directory. srcurl, dsturl = calculate_urls(relname) print 'Tag url:', dsturl fd, msgfile = tempfile.mkstemp(text=True) try: os.close(fd) fp = open(msgfile, 'w') try: print >> fp, 'Tagging release', tagname finally: fp.close() do('svn cp %s %s -F %s' % (srcurl, dsturl, msgfile)) finally: os.remove(msgfile) def make_pkg(tagname, sign): reldir = releasedir(tagname) if os.path.exists(reldir): print >> sys.stderr, 'Release directory already exists:', reldir sys.exit(1) relname = tag2rel(tagname) srcurl, dsturl = calculate_urls(relname) print 'Exporting to release dir', reldir, '...' do('svn export %s %s' % (dsturl, reldir)) if not os.path.exists(reldir): print >> sys.stderr, 'svn export failed:', dsturl sys.exit(1) curdir = os.getcwd() try: os.chdir(os.path.dirname(reldir)) print 'Making tarball...' relname = 'mailman-' + tagname tarfile = relname + '.tgz' do('tar cvzf %s --exclude .svn %s' % (tarfile, relname)) do('tar cvzf mailman-doc.tgz --exclude .svn mailman-doc') if sign: do('gpg -bas %s' % tarfile) finally: os.chdir(curdir) VERSIONMARK = '' DATEMARK = '' def do_bump(newvers): print 'Doing bump...', for file in ('index.ht', 'version.ht'): print '\t%s...' % file, fp = open(os.path.join('admin', 'www', file), 'r+') text = fp.read() parts = text.split(VERSIONMARK) parts[1] = newvers text = VERSIONMARK.join(parts) parts = text.split(DATEMARK) parts[1] = now() text = DATEMARK.join(parts) fp.seek(0) fp.write(text) fp.close() # hack the configure.in file print 'Version.py...', infp = open('Mailman/Version.py') outfp = open('Mailman/Version.py.new', 'w') matched = False cre = re.compile(r'^VERSION(?P[ \t]*)=') for line in infp: mo = cre.search(line) if matched or not mo: outfp.write(line) else: outfp.write('VERSION%s= "%s"\n' % (mo.group('ws'), newvers)) matched = True if not matched: print >> sys.stderr, 'Error! VERSION line not found' infp.close() outfp.close() os.rename('Mailman/Version.py.new', 'Mailman/Version.py') def parseargs(): parser = optparse.OptionParser(version=__version__, usage="""\ %prog [options] tagname Manage releases of Mailman. tagname is used in the various commands above. It should essentially be the version number for the release, and is required.""") parser.add_option('-t', '--tag', default=False, action='store_true', help="""\ Tag all release files with tagname.""") parser.add_option('-p', '--package', default=False, action='store_true', help='Create the distribution package.') parser.add_option('-b', '--bump', default=False, action='store_true', help="""\ Bump the revision number in key files to tagname. This is done by textual substitution.""") parser.add_option('-s', '--sign', default=False, action='store_true', help="""\ Sign the release. gpg will prompt you for your passphrase.""") opts, args = parser.parse_args() if len(args) > 1: print >> sys.stderr, 'Unexpected arguments:', SPACE(args[1:]) sys.exit(1) if len(args) < 1: print >> sys.stderr, 'Required tagname argument is missing' sys.exit(1) return parser, opts, args[0] def main(): parser, opts, tagname = parseargs() # Very important!!! omask = os.umask(0) try: if opts.bump: do_bump(tagname) if opts.tag: tag_release(tagname) if opts.package: make_pkg(tagname, opts.sign) finally: os.umask(omask) if __name__ == '__main__': main()