[Python-checkins] r61259 - sandbox/trunk/release sandbox/trunk/release/release.py

barry.warsaw python-checkins at python.org
Wed Mar 5 23:13:12 CET 2008


Author: barry.warsaw
Date: Wed Mar  5 23:13:11 2008
New Revision: 61259

Added:
   sandbox/trunk/release/
   sandbox/trunk/release/release.py
Log:
Benjamin Peterson's release script, contributed to the cause.


Added: sandbox/trunk/release/release.py
==============================================================================
--- (empty file)
+++ sandbox/trunk/release/release.py	Wed Mar  5 23:13:11 2008
@@ -0,0 +1,237 @@
+"An assistant for making Python releases by Benjamin Peterson"
+#!/usr/bin/env python
+from __future__ import with_statement
+
+import sys
+import os
+import optparse
+import re
+import subprocess
+import shutil
+import tempfile
+
+# Ideas stolen from Mailman's release script, Lib/tokens.py and welease
+
+def error(*msgs):
+    print >> sys.stderr, "**ERROR**"
+    for msg in msgs:
+        print >> sys.stderr, msg
+    sys.exit(1)
+
+def run_cmd(args, silent=False):
+    cmd = " ".join(args)
+    if not silent:
+        print "Executing %s" % cmd
+    try:
+        if silent:
+            code = subprocess.call(cmd, shell=True, stdout=PIPE)
+        else:
+            code = subprocess.call(cmd, shell=True)
+    except OSError:
+        error("%s failed" % cmd)
+
+def check_env():
+    if "EDITOR" not in os.environ:
+        error("editor not detected.",
+            "Please set your EDITOR enviroment variable")
+    if not os.path.exists(".svn"):
+        error("CWD is not a Subversion checkout")
+        
+def get_arg_parser():
+    usage = "%prog [options] tagname"
+    p = optparse.OptionParser(usage=usage)
+    p.add_option("-b", "--bump",
+        default=False, action="store_true",
+        help="bump the revision number in important files")
+    p.add_option("-e", "--export",
+        default=False, action="store_true",
+        help="Export the SVN tag to a tarball")
+    p.add_option("-m", "--branch",
+        default=False, action="store_true",
+        help="create a maintance branch to go along with the release")
+    p.add_option("-t", "--tag",
+        default=False, action="store_true",
+        help="Tag the release in Subversion")
+    return p
+
+def constant_replace(fn, updated_constants, comment_start="/*", comment_end="*/"):
+    "Inserts in between --start constant-- and --end constant-- in a file"
+    start_tag = comment_start + "--start constants--" + comment_end
+    end_tag = comment_start + "--end constants--" + comment_end
+    with open(fn) as fp:
+        lines = fp.read().splitlines()
+    try:
+        start = lines.index(start_tag) + 1
+        end = lines.index(end_tag)
+    except ValueError:
+        error("%s doesn't have constant tags" % fn)
+    lines[start:end] = [updated_constants]
+    with open(fn, "w") as fp:
+        fp.write("\n".join(lines))
+
+def bump(tag):
+    print "Bumping version to %s" % tag
+    
+    wanted_file = "Misc/RPM/python-%s.spec" % tag.basic_version
+    print "Updating %s" % wanted_file,
+    if not os.path.exists(wanted_file):
+        specs = os.listdir("Misc/RPM/")
+        for file in specs:
+            if file.startswith("python-"):
+                break
+        full_path = os.path.join("Misc/RPM/", file)
+        print "\nrenaming %s to %s" % (full_path, wanted_file)
+        run_cmd(["svn", "rename", "--force", full_path, wanted_file])
+        print "File was renamed; please commit"
+        run_cmd(["svn", "commit"])
+    new = "%define version " + tag.text + \
+        "\n%define libver " + tag.basic_version
+    constant_replace(wanted_file, new, "#", "")
+    print "done"
+    
+    print "Updating Include/patchlevel.h...",
+    template = """#define PY_MAJOR_VERSION	[major]
+#define PY_MINOR_VERSION	[minor]
+#define PY_MICRO_VERSION	[patch]
+#define PY_RELEASE_LEVEL    [level]
+#define PY_RELEASE_SERIAL	[serial]
+#define PY_VERSION  \"[text]\""""
+    for what in ("major", "minor", "patch", "serial", "text"):
+        template = template.replace("[" + what + "]", str(getattr(tag, what)))
+    level_defines = {"a" : "PY_RELEASE_LEVEL_ALPHA",
+    "b" : "PY_RELEASE_LEVEL_BETA",
+    "c" : "PY_RELEASE_LEVEL_GAMMA",
+    "f" : "PY_RELEASE_LEVEL_FINAL"}
+    template = template.replace("[level]", level_defines[tag.level])
+    constant_replace("Include/patchlevel.h", template)
+    print "done"
+    
+    print "Updating Lib/idlelib/idlever.py...",
+    with open("Lib/idlelib/idlever.py", "w") as fp:
+        new = "IDLE_VERSION = \"%s\"\n" % tag.next_text
+        fp.write(new)
+    print "done"
+    
+    print "Updating Lib/distutils/__init__.py...",
+    new = "__version__ = \"%s\"" % tag.text
+    constant_replace("Lib/distutils/__init__.py", new, "#", "")
+    print "done"
+    
+    other_files = ["README"]
+    if tag.patch == 0 and tag.level == "a" and tag.serial == 0:
+        other_files += ["Doc/tutorial/interpreter.rst",
+            "Doc/tutorial/stdlib.rst", "Doc/tutorial/stdlib2.rst"]
+    print "\nManual editing time..."
+    for fn in other_files:
+        print "Edit %s" % fn
+        manual_edit(fn)
+    
+    print "Bumped revision"
+    print "Please commit and use --tag"
+
+def manual_edit(fn):
+    run_cmd([os.environ["EDITOR"], fn])
+
+def export(tag):
+    temp_dir = tempfile.mkdtemp("pyrelease")
+    if not os.path.exists("dist") and not os.path.isdir("dist"):
+        print "creating dist directory"
+        os.mkdir("dist")
+    tgz = "dist/Python-%s.tgz" % tag.text
+    bz = "dist/Python-%s.tar.bz2" % tag.text
+    old_cur = os.getcwd()
+    os.chdir(temp_dir)
+    try:
+        try:
+            print "Exporting tag"
+            run_cmd(["svn", "export",
+                "http://svn.python.org/projects/python/tags/r%s"
+                % tag.text.replace(".", ""), "release"])
+            print "Making .tgz"
+            run_cmd(["tar cf - release | gzip -9 > release.tgz"])
+            print "Making .tar.bz2"
+            run_cmd(["tar cf - release "
+                "| bzip2 -9 > release.tar.bz2"])
+        finally:
+            os.chdir(old_cur)
+        print "Moving files to dist"
+        os.rename(os.path.join(temp_dir, "release.tgz"), tgz)
+        os.rename(os.path.join(temp_dir, "release.tar.bz2"), bz)
+    finally:
+        print "Cleaning up"
+        shutil.rmtree(temp_dir)
+    print "Calculating md5sums"
+    run_cmd(["md5sum", tgz, ">", tgz + ".md5"])
+    run_cmd(["md5sum", bz, ">", bz + ".md5"])
+    print "**Now extract the archives and run the tests**"
+
+class Tag:
+    def __init__(self, text, major, minor, patch, level, serial):
+        self.text = text
+        self.next_text = self.text
+        self.major = major
+        self.minor = minor
+        self.patch = patch
+        self.level = level
+        self.serial = serial
+        self.basic_version = major + "." + minor
+    
+    def __str__(self):
+        return self.text
+
+def break_up_tag(tag):
+    exp = re.compile(r"(\d+)(?:\.(\d+)(?:\.(\d+))?)?(?:([abc])(\d+))?")
+    result = exp.search(tag)
+    if result is None:
+        error("tag %s is not valid" % tag)
+    data = list(result.groups())
+    # fix None level
+    if data[3] is None:
+        data[3] = "f"
+    # None Everythign else should be 0
+    for i, thing in enumerate(data):
+        if thing is None:
+            data[i] = 0
+    return Tag(tag, *data)
+
+def branch(tag):
+    if tag.minor > 0 or tag.patch > 0 or tag.level != "f":
+        print "It doesn't look like your making a final release."
+        if raw_input("Are you sure you want to branch?") != "y":
+            return
+    run_cmd(["svn", "copy", get_current_location(),
+        "svn+ssh://svn.python.org/projects/python/branches/" 
+            "release%s-maint" % (tag.major + tag.minor)])
+
+def get_current_location():
+    data = subprocess.Popen("svn info", shell=True,
+        stdout=subprocess.PIPE).stdout.read().splitlines()
+    for line in data:
+        if line.startswith("URL: "):
+            return line.lstrip("URL: ")
+
+def make_tag(tag):
+    run_cmd(["svn", "copy", get_current_location(),
+        "svn+ssh://svn.python.org/projects/python/tags/r"
+        + tag.text.replace(".", "")])
+
+def main(argv):
+    parser = get_arg_parser()
+    options, args = parser.parse_args(argv)
+    if len(args) != 2:
+        parser.print_usage()
+        sys.exit(1)
+    tag = break_up_tag(args[1])
+    if not options.export:
+        check_env()
+    if options.bump:
+        bump(tag)
+    elif options.tag:
+        make_tag(tag)
+    elif options.branch:
+        branch(tag)
+    elif options.export:
+        export(tag)
+
+if __name__ == "__main__":
+    main(sys.argv)


More information about the Python-checkins mailing list