Added release.py script automating release process
authorW. Trevor King <wking@drexel.edu>
Fri, 20 Nov 2009 12:28:20 +0000 (07:28 -0500)
committerW. Trevor King <wking@drexel.edu>
Fri, 20 Nov 2009 12:28:20 +0000 (07:28 -0500)
release.py [new file with mode: 0755]

diff --git a/release.py b/release.py
new file mode 100755 (executable)
index 0000000..d064b11
--- /dev/null
@@ -0,0 +1,159 @@
+#!/usr/bin/python
+#
+# Copyright (C) 2009 W. Trevor King <wking@drexel.edu>
+#
+# 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.
+
+import os
+import os.path
+import shutil
+import string
+from subprocess import Popen
+import sys
+
+from update_copyright import Pipe
+
+def validate_tag(tag):
+    """
+    >>> validate_tag('1.0.0')
+    >>> validate_tag('A.B.C-r7')
+    >>> validate_tag('A.B.C r7')
+    Traceback (most recent call last):
+      ...
+    Exception: Invalid character ' ' in tag 'A.B.C r7'
+    >>> validate_tag('"')
+    Traceback (most recent call last):
+      ...
+    Exception: Invalid character '"' in tag '"'
+    >>> validate_tag("'")
+    Traceback (most recent call last):
+      ...
+    Exception: Invalid character ''' in tag '''
+    """
+    for char in tag:
+        if char in string.digits:
+            continue
+        elif char in string.letters:
+            continue
+        elif char in ['.','-']:
+            continue
+        raise Exception("Invalid character '%s' in tag '%s'" % (char, tag))
+
+def bzr_pending_changes():
+    """Use `bzr diff`s exit status to detect change:
+    1 - changed
+    2 - unrepresentable changes
+    3 - error
+    0 - no change
+    """
+    p = Pipe([['bzr', 'diff']])
+    if p.status == 0:
+        return False
+    elif p.status in [1,2]:
+        return True
+    raise Exception("Error in bzr diff %d\n%s" % (p.status, p.stderrs[-1]))
+
+def set_release_version(tag):
+    print "set libbe.version._VERSION = '%s'" % tag
+    p = Pipe([['sed', '-i', "s/^# *_VERSION *=.*/_VERSION = '%s'/" % tag,
+               os.path.join('libbe', 'version.py')]])
+    assert p.status == 0, p.statuses
+
+def bzr_commit(commit_message):
+    print 'commit current status:', commit_message
+    p = Pipe([['bzr', 'commit', '-m', commit_message]])
+    assert p.status == 0, p.statuses
+
+def bzr_tag(tag):
+    print 'tag current revision', tag
+    p = Pipe([['bzr', 'tag', tag]])
+    assert p.status == 0, p.statuses
+
+def bzr_export(target_dir):
+    print 'export current revision to', target_dir
+    p = Pipe([['bzr', 'export', target_dir]])
+    assert p.status == 0, p.statuses
+
+def make_version():
+    print 'generate libbe/_version.py'
+    p = Pipe([['make', os.path.join('libbe', '_version.py')]])
+    assert p.status == 0, p.statuses
+
+def make_changelog(filename, tag):
+    print 'generate ChangeLog file', filename, 'up to tag', tag
+    p = Popen(['bzr', 'log', '--gnu-changelog', '-n1', '-r',
+               '..tag:%s' % tag], stdout=file(filename, 'w'))
+    status = p.wait()
+    assert status == 0, status
+
+def set_vcs_name(filename, vcs_name='None'):
+    """Exported directory is not a bzr repository, so set vcs_name to
+    something that will work.
+      vcs_name: new_vcs_name
+    """
+    print 'set_vcs_name in', filename, 'to', vcs_name
+    p = Pipe([['sed', '-i', "s/^vcs_name:.*/vcs_name: %s/" % vcs_name,
+               filename]])
+    assert p.status == 0, p.statuses
+
+def create_tarball(tag):
+    release_name='be-%s' % tag
+    export_dir = release_name
+    bzr_export(export_dir)
+    make_version()
+    print 'copy libbe/_version.py to %s/libbe/_version.py' % export_dir
+    shutil.copy(os.path.join('libbe', '_version.py'),
+                os.path.join(export_dir, 'libbe', '_version.py'))
+    make_changelog(os.path.join(export_dir, 'ChangeLog'), tag)
+    set_vcs_name(os.path.join(export_dir, '.be', 'settings'))
+    tarball_file = '%s.tar.gz' % release_name
+    print 'create tarball', tarball_file
+    p = Pipe([['tar', '-czf', tarball_file, export_dir]])
+    assert p.status == 0, p.statuses
+    print 'remove', export_dir
+    shutil.rmtree(export_dir)
+
+def test():
+    import doctest
+    doctest.testmod() 
+
+if __name__ == '__main__':
+    import optparse
+    usage = """%prog [options] TAG
+
+Create a bzr tag and a release tarball from the current revision.
+For example
+  %prog 1.0.0
+"""
+    p = optparse.OptionParser(usage)
+    p.add_option('--test', dest='test', default=False,
+                 action='store_true', help='Run internal tests and exit')
+    options,args = p.parse_args()
+
+    if options.test == True:
+        test()
+        sys.exit(0)
+
+    assert len(args) == 1, '%d (!= 1) arguments: %s' % (len(args), args)
+    tag = args[0]
+    validate_tag(tag)
+
+    if bzr_pending_changes() == True:
+        print "Handle pending changes before releasing."
+        sys.exit(1)
+    set_release_version(tag)
+    bzr_commit("Bumped to version %s" % tag)
+    bzr_tag(tag)
+    create_tarball(tag)