Commits

Anonymous committed 75a6bdd

move stephen's stuff to subdirectory stephen

Comments (0)

Files changed (1)

xre.py

-# XEmacs Release Engineer module
-
-# Copyright (C) 2001 Free Software Foundation
-
-# Author: Stephen J. Turnbull <stephen@xemacs.org>
-# Created: 26 Sep 2001
-# Keywords: releases, maint
-
-# This program is part of XEmacs, which 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, 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 XEmacs; see the file COPYING.  If not, write to
-# the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-# Boston, MA 02111-1307, USA.
-
-#######################################################################
-# ALL REVIEWERS, FEEL FREE TO MODIFY THIS FILE.  I have my personal   #
-# version checked in on a CVS branch.  If it's a generic improvement, #
-# a courtesy notice to <stephen@xemacs.org> would be appreciated.     #
-#######################################################################
-
-# Usage:
-# Don't.
-# OK, if you must.  This code _requires_ Python 2.  I run the Python
-# interpreter from M-x shell mode.  Then I do "from xre import gamma",
-# followed by "gamma.doit()" which does most of what's necessary for a
-# release.  Note that by default doit() is pretty conservative about what
-# does.  You'll need to create a Developer object for yourself.  The
-# Project, Developer, and Release objects aren't very orthogonal, sorry!
-
-# Kudos:
-# This program is based on Martin Buchholz's excellent Perl program `xre'.
-
-# Apologia:
-# I rewrote it in Python because I understand Python better, and I realized
-# that it would take nearly as much effort to fully understand Martin's
-# program as a Perl program as to rewrite it in Python form, documenting
-# the parts I didn't get on first reading as I go along.
-
-# TODO
-#
-# 2.  Many methods refer to xemacs; this should be abstracted.
-# 4.  Martin's xre has a better way to do os.system()?
-# 5.  Use try ... finally etc to make sure chdirs etc are undone?
-# 6.  Add a dictionary of developer aliases to Project, remove projectAlias
-#     from Developer class.  See comment in Project.updateChangeLogs.
-# 7.  Release.upload now uploads to a ./pretest directory where
-#     pretests are held.
-#     The pretest needs to be announced, and build reports solicited.
-#     The pretest would also be available on a special branch,
-#     pretest-$major-$minor.  The branch would be deleted at commit
-#     time, then recreated with the new release as the node.  This
-#     needs to be implemented
-#     Then, after the results are in, the files would be moved to the
-#     parent directory.  A function to do this is implemented.
-#     Pretests could be announced on xemacs-beta, and progress tracked
-#     on the web site.
-# 8.  There should be a host class to hold info about directories and
-#     anonymous user authorization.
-# 9.  Move the object initializations out of this file.
-# 12. Need to deal with CHANGES-{beta,release} in validation procedures.
-# 13. Deal with all FIXMEs.
-# 14. 
-
-# DONE
-#
-# 10. Update methods to pass dryRun parameters to execAnd*() functions.
-# 11. Rename class Status as class Filter.
-# 1.  Many methods do os.chdir(self.project.stage).  Probably this should
-#     be done in initialization, once.  ## Not is better, more robust.
-# 3.  Change the insane __init__ arglists to use keywords.
-
-## imports
-
-import sys
-import os
-from string import split, strip
-from time import strftime, gmtime, time
-from re import match, search, IGNORECASE
-from os.path import dirname, isdir
-from types import *
-
-## main program
-
-## class definitions
-
-class Developer:
-    """Collects information about a developer.
-
-    Initializer arguments (keyword format recommended):
-        fullName         Full name of the developer for use in sigs, etc.
-        comment          Description of the developer's role.
-        projectAlias     Mail alias at the project, if available (user only).
-        mailingAddress   Non-project (full) mailing address.
-        cvsAuthUser      Login name on CVS host; for commits.
-        uploadAuthUser   Login name on distribution host; for uploads.
-    Other attributes:
-        valid            Has initialization been completed?"""
-    def __init__ (self,
-                  fullName = None,
-                  comment = None,
-                  projectAlias = None,
-                  mailingAddress = None,
-                  cvsAuthUser = None,
-                  uploadAuthUser = None):
-        self.valid = 0
-        self.fullName = fullName
-        self.comment = comment
-        self.projectAlias = projectAlias
-        self.mailingAddress = mailingAddress
-        self.cvsAuthUser = cvsAuthUser
-        self.uploadAuthUser = uploadAuthUser
-        self.validate(0)
-    def validate (self, verbose = 1):
-        """Check whether instance is ready for use.  If so, set valid true.
-
-        If verbose, warn about incomplete or invalid entries.
-        FIXME: currently does nothing."""
-        pass
-
-class Project:
-    """Collects information about a project.
-
-    Initializer arguments (keyword format recommended):
-        alias            Short name used to construct pathnames, lowercase.
-        fullName         Official name of the project.
-        mailDomain       Domain where developers receive mail.
-        homePage         URL of home page.
-        uploadHost       Destination for uploads for distribution.
-        uploadRoot       Root of hierarchy for uploads on uploadHost.
-        cvsAnonUser      Anonymous user for CVS pserver.
-        cvsAnonPasswd    Password for anonymous user of CVS pserver.
-        cvsHost          Host of CVS repository.
-        cvsRoot          Root of CVS repository tree on cvsHost.
-                         Not the same as CVSROOT!
-        cvsModule        CVS module to check out.
-        sourceSize       Approximate disk usage (MB) of distribution.
-        buildSize        Approximate disk usage (MB) of build-generated files.
-        installSize      Approximate disk usage (MB) of files installed.
-        stage            Staging area on local disk.
-        versionFile      Name of file containing version information.
-        versionFormat    Sequence of regexps matching versionFile line-by-line.
-    Attributes:
-        valid            Can the object be used?"""
-
-    def __init__ (self,
-                  fullName,
-                  alias,
-                  mailDomain,
-                  homePage,
-                  uploadHost,
-                  uploadRoot,
-                  cvsAnonUser,
-                  cvsAnonPasswd,
-                  cvsHost,
-                  cvsRoot,
-                  cvsModule,
-                  sourceSize,
-                  buildSize,
-                  installSize,
-                  versionFile,
-                  versionFormat,
-                  stage="/tmp/staging"):
-        self.valid = 0
-        self.alias = alias
-        self.fullName = fullName
-        self.homePage = homePage
-        self.mailDomain = mailDomain
-        self.uploadHost = uploadHost
-        self.uploadRoot = uploadRoot
-        self.cvsHost = cvsHost
-        self.cvsRoot = cvsRoot
-        self.cvsAnonUser = cvsAnonUser
-        self.cvsAnonPasswd = cvsAnonPasswd
-        self.cvsModule = cvsModule
-        self.sourceSize = sourceSize
-        self.installSize = installSize
-        self.buildSize = buildSize
-        self.versionFile = versionFile
-        self.versionFormat = versionFormat
-        self.stage = stage
-        self.validate()
-
-    def validate (self, verbose = 1):
-        """Check whether instance is ready for use.  If so, set valid true.
-
-        If verbose, warn about incomplete or invalid entries.
-        FIXME: currently does nothing."""
-        pass
-
-class Release:
-    """Represents a release of a software project.
-
-    Successful instantiation of a release creates an object with methods
-    for finalizing the release in CVS with appropriate ChangeLog entries,
-    tagging the tree, rolling distribution tarballs, uploading them, and
-    announcing the release.  FIXME: It should execute these, and also
-    produce a state object that will allow restarting the process if it
-    stalls in an idempotent way.
-
-    All paths below are relative to the project staging directory.
-
-    Initializer arguments:
-        cvsTag                 CVS branch tag for checkout and update.
-        versionString          String in dotted triple format (1.2.3),
-                               indicates current version.  #### advisory?
-        ancestorVersionString  String in dotted triple format (1.2.3),
-                               indicates version from which branch forked.
-        codenameFile           Path to file containing version/codename pairs.
-        project                Project object.
-        engineer               Developer object.
-        copySource             Source for copying in dry runs.
-        isBeta                 0 for public release, 1 for beta release.
-                               Defaulted according to odd/even rule, but can be
-                               overridden explicitly.
-        branchPointTag         Tag to branch from for initial public release.
-    Attributes:
-        majorVersion           Nonnegative integer.
-        minorVersion           Nonnegative integer.
-        releaseVersion         Nonnegative integer.  May be beta version.
-        stem                   Stem of names, eg, root of distribution tree.
-        versionDir             Directory on upload host for this series.
-        herald                 String announcing release in one line.
-        reallyCheckout         Kludge to substitute local cp for cvs checkout.
-    Public methods:
-        prepare                Create a clean source tree, updated to current
-                               state of CVS branch, verify consistency.
-        finalize               Finalize by adding appropriate ChangeLogs and
-                               incrementing version in version.sh.
-        abstract               Scarf ChangeLogs etc for use in announcements.
-        package                Make tarball(s), patches, PGP signatures, ready
-                               for upload.
-        check                  Do consistency testing.
-        tag                    Tag the CVS repository.  Expects to find a fresh
-                               branch for a .0 release.
-        upload                 Upload tarball(s), patch, and PGP signatures to
-                               distribution host.
-        announce               Post announcements to (eg) xemacs-announce,
-                               the home page, and FreshMeat."""
-
-    def __init__ (self,
-                  cvsTag,
-                  versionString,
-                  ancestorVersionString,
-                  codenameFile,
-                  project,
-                  engineer,
-                  copySource,
-                  isBeta = -1,
-                  branchPointTag = None
-                  ):
-        self.valid = 0
-        self.state = None
-        self.project = project
-        self.engineer = engineer
-        self.cvsTag = cvsTag
-        self.versionString = versionString
-        self.ancestorVersionString = ancestorVersionString
-        self.codenameFile = codenameFile
-        self.copySource = copySource
-        m = match(r"(\d+)\.(\d+)\.(\d+)", versionString)
-        if m:
-            self.majorVersion = int(m.group(1))
-            self.minorVersion = int(m.group(2))
-            self.releaseVersion = int(m.group(3)) # or beta version
-        else:
-            self.majorVersion = self.minorVersion = self.releaseVersion = None
-        self.stem = project.alias + "-" + self.versionString
-        if self.releaseVersion:
-            self.previousStem = "%s-%d.%d.%d" % (self.project.alias,
-                                                 self.majorVersion,
-                                                 self.minorVersion,
-                                                 self.releaseVersion-1)
-            self.versionDir = "%s-%d.%d" % (self.project.alias,
-                                            self.majorVersion,
-                                            self.minorVersion)
-        else:
-            self.previousStem = self.project.alias + "-" \
-                                + self.ancestorVersionString
-            # not great but should be safe ... error here?
-            self.versionDir = self.project.alias + "-" + self.versionString
-        if isBeta < 0:
-            self.isBeta = self.minorVersion % 2
-        else:
-            self.isBeta = isBeta
-        self.branchPointTag = branchPointTag
-        self.validate()
-
-    def validate (self, verbose = 1):
-        """Check whether instance is ready for use.  If so, set valid true.
-
-        If verbose, warn about incomplete or invalid entries.
-        FIXME: currently does almost nothing."""
-
-        self.valid = 1
-        if not self.engineer.valid:
-            print "Error: engineer of release invalid."
-            self.valid = 0
-        if not self.codenameFile:       # FIXME: validate file readability.
-            print "Error: no codename file."
-            self.valid = 0
-        # check only majorVersion because version string didn't match
-        # r"\d+\.\d+\.\d+"
-        if not self.majorVersion:
-            print "Error: version string invalid."
-            self.valid = 0
-        if not self.project.valid:
-            print "Error: project of release invalid."
-            self.valid = 0
-        else:
-            # FIXME: better to use statfs?
-            dfOut = os.popen('df -k ' + self.project.stage).readlines()
-            avail = int(split(dfOut[1])[3])
-
-            p = self.project
-            sNeeds = 18*p.sourceSize/5
-            sbiNeeds = 6*(2*p.sourceSize + p.buildSize + p.installSize)/5
-
-            format = "insufficient staging space (%d) for release (%s = %d)."
-            if (sNeeds > avail/1024):
-                raise ValueError, format % (avail/1024,"sNeeds",sNeeds)
-            if (sbiNeeds > avail/1024):
-                raise ValueError, format % (avail/1024,"sbiNeeds",sbiNeeds)
-
-    def doit (self, reallyCheckout=1, dontSign=0):
-        """Automated release procedure.
-
-        release is a Release object.  reallyCheckout is an integer:
-        0 -> check out from CVS repository,
-        1 -> reuse an existing tree verbatim,
-        2 -> copy a local tree.
-        dontSign is tri-state:
-        0 -> make tarballs and sign gzip files with GnuPG (ASCII armored),
-        1 -> fake the signatures (avoid repeated entry of the passphrase),
-        2 -> do not make tarballs or try to sign anything."""
-        # Most of these take some kind of dry run parameter, which
-        # defaults according to stability of implementation.  False (0)
-        # means "full steam ahead", execute a standard release.  However,
-        # there may be degrees of dry run, see the doc for each function.
-        self.prepare(reallyCheckout)
-        self.finalize()
-        self.abstract()
-        self.package(dontSign)
-        self.check()
-        print "The automated portion is done.  The rest is a dry run."
-        self.tag()
-        self.upload()
-        self.makePublic()
-        self.announce()
-        print """Now apply methods
-        tag()
-        upload()
-        makePublic()
-        announce()
-to the Release object in order."""
-
-    def prepare (self, reallyCheckout):
-        """Create clean source trees for current and previous release.
-
-        Update to current state of relevant CVS branch, and verify
-        consistency.  reallyCheckout is an integer:
-        0 -> check out from CVS repository,
-        1 -> reuse an existing tree verbatim,
-        2 -> copy a local tree.
-        0 and 2 remove an existing current tree, but leave the `previous'
-        tree alone."""
-
-        # Procedure
-        p = self.project
-        # 1. CD to an appropriate staging area.  Check space and modes.
-        print "Prepare a source tree:"
-        print "  Change to " + p.stage
-        os.chdir(p.stage)
-        # FIXME: better to use statfs?
-        print "  Checking space ...",
-        dfOut = os.popen('df -k ' + p.stage).readlines()
-        avail = int(split(dfOut[1])[3])
-        # FIXME: check math here!!
-        sNeeds = 18*p.sourceSize/5
-        sbiNeeds = 6*(2*p.sourceSize + p.buildSize + p.installSize)/5
-
-        format = "insufficient staging space (%d) for release (%s = %d)."
-        if (sNeeds > avail/1024):
-            raise ValueError, format % (avail/1024,"sNeeds",sNeeds)
-        if (sbiNeeds > avail/1024):
-            raise ValueError, format % (avail/1024,"sbiNeeds",sbiNeeds)
-        print " OK."
-        # 2. Checkout the current tip of the CVS branch.  (Theoretically
-        #    we could reuse a work area, but this is safest.)
-        #    FIXME: assumes pserver, and cvs login already done.
-        #    FIXME: probably should also clean out previousStem?
-        if reallyCheckout == 0 or reallyCheckout == 2:
-            execAndTrace("rm -rf " + self.stem)
-        if reallyCheckout == 0:
-            cmd = "cvs -d :pserver:" + p.cvsAnonUser + "@" + p.cvsHost \
-                  + ":" + p.cvsRoot + " checkout -d " \
-                  + self.stem
-            if self.cvsTag != "HEAD":
-                cmd += " -r " + self.cvsTag
-            cmd += " " + p.cvsModule
-            updateCarefully(cmd)
-        elif reallyCheckout == 1:
-            # FIXME: need to barf here if the checked-out tree doesn't exist.
-            pass
-        elif reallyCheckout == 2:
-            # The following probably doesn't really make sense; it's
-            # defensive for the case where stem is more than
-            # one level of subdirectory
-            execAndTrace("mkdir -p " + self.stem)
-            execAndTrace("rmdir " + self.stem)
-            execAndTrace("cp -pR " + self.copySource + " " \
-                      + self.stem)
-        else:
-            # Fake it.
-            # Actually, should probably cp over the munged files.
-            print "(cp -pR " + self.copySource + " " \
-                  + self.stem + ") ... done."
-
-        # make sure that configure is up to date
-        # NB: cvs will not commit unless it's different (according to diff)
-        os.chdir(self.stem)
-        execAndTrace("autoconf")
-        os.chdir("..")
-
-        # 3. Check that version.sh is consistent with (ie, immediate
-        #    predecessor of) the release in preparation.
-        #    FIXME: omitted.
-        # 4. Prepare the previous release tree.  (#### Not for x.y.0 releases?)
-        #    Local copy and CVS update in reverse needs care, because of
-        #    file add and removal.
-
-        if self.isBeta:
-            # Are we going to need to clean this up to get a sensible patch?
-            previousTarball = self.previousStem + ".tar.gz"
-        else:
-            previousTarball = self.previousStem + "-src.tar.gz"
-        # FIXME: need to clean up previous workspace
-        # FIXME: use the local copy, this should be if not exist tarball
-        # FIXME: if exists but not readable and not writable, this is going
-        # to fail.
-        if not os.access(previousTarball,os.R_OK) and self.releaseVersion:
-            # FIXME: this should not refer to xemacs explicitly
-            print "Can't find readable " + previousTarball + "."
-            cmd =  "wget --non-verbose --passive"
-            cmd += " ftp://ftp.xemacs.org/pub/xemacs/"
-            if self.isBeta:
-                cmd += "beta/"
-            cmd += "%s-%d.%d/%s" % (p.alias, self.majorVersion,
-                                    self.minorVersion, previousTarball)
-            execAndTrace(cmd)
-        if os.access(previousTarball,os.R_OK):
-            execAndTrace("rm -rf " + self.previousStem)
-            cmd = "tar xzvf " + previousTarball
-            execAndTrace(cmd)
-        else:
-            raise Error, "Couldn't explode previous tarball: " \
-                  + previousTarball
-
-    def finalize (self, dryRun=0):
-        """Add herald to ChangeLogs and increment version in version.sh.
-
-        We do _not_ commit or tag here because that is externally visible.
-        FIXME: this routine is a good place to make sure that autoconf is
-        run."""
-
-        print "Constructing herald:  ",
-        self.herald = self.codename = None
-        os.chdir(self.project.stage + "/" + self.stem)
-        infile = open(self.codenameFile,"r")
-        if infile:
-            if self.codenameFile == "etc/OXYMORONS":
-                for line in infile.readlines():
-                    m = match(self.versionString + ":\s*(\S.*)$",line)
-                    if m:
-                        self.codename = strip(m.group(1))
-                        self.herald = 'XEmacs %s "%s" is released.' \
-                                      % (self.versionString, self.codename)
-            # Sigh.  Steve is one-based ...
-            elif self.codenameFile == "../Codenames-21.2":
-                lines = infile.readlines()
-                if len(lines) >= self.releaseVersion:
-                    self.codename = strip(lines[self.releaseVersion-1])
-                    self.herald = 'XEmacs %s "%s" is released.' \
-                                  % (self.versionString, self.codename)
-            # ... and Martin is zero-based.
-            elif self.codenameFile == "etc/VEGETABLES":
-                lines = infile.readlines()
-                if len(lines) >= self.releaseVersion:
-                    self.codename = strip(lines[self.releaseVersion])
-                    self.herald = 'XEmacs %s "%s" is released.' \
-                                  % (self.versionString, self.codename)
-            infile.close()
-        if not self.herald:
-            print "\nError: couldn't determine codename."
-            self.herald = 'XEmacs %s is released.' % (self.versionString)
-        print self.herald
-
-        print "Updating ChangeLogs ..."
-        self.updateChangeLogs()
-        # version file (XEmacs specific)
-        lines = []
-        # ensure dirname() returns non-false
-        old = "./" + self.project.versionFile
-        print "Munge version file " + old + " ..."
-        infile = open(old,"r")
-        if not infile:
-            print "\nError: can't open version file."
-        else:
-            values = [None, None, None, None, None]
-            format = self.project.versionFormat
-            lines = infile.readlines()
-            infile.close()
-            if len(lines) != len(format):
-                print "\nWarning: file has %d lines, expected %d." \
-                      % (len(lines), len(format))
-            for i in range(len(format)):
-                m = match(format[i],strip(lines[i]))
-                if not m:
-                    print "\nWarning: unexpected format in line %d."
-                elif 0 < i < 6:
-                    values[i-1] = m.group(1)
-            if self.isBeta and values[0] != "t" \
-               or not self.isBeta and values[0] != "":
-                print "\nWarning: unexpected emacs_is_beta value."
-            # FIXME: bogus if releaseVersion same but others differ
-            if int(values[1]) != self.majorVersion \
-               or int(values[2]) != self.minorVersion \
-               or int(values[3]) != self.releaseVersion - 1:
-                if int(values[3]) == self.releaseVersion:
-                    print "\nWarning: version.sh seems to be finalized."
-                else:
-                    print "\nWarning: version not consecutive."
-        new = os.tempnam(dirname(old))
-        outfile = open(new,"w")
-        if self.isBeta:
-            ib = "t"
-        else:
-            ib = ""
-        outfile.write('''#!/bin/sh
-emacs_is_beta=%s
-emacs_major_version=%d
-emacs_minor_version=%d
-emacs_beta_version=%d
-xemacs_codename="%s"
-''' % (ib, self.majorVersion, self.minorVersion, self.releaseVersion,
-       self.codename))
-        for i in range(6,len(lines)):
-            outfile.write(lines[i])
-        outfile.close()
-        # FIXME: do some verification
-        os.unlink(old)
-        os.rename(new,old)
-        # Martin's xre also manipulates CHANGES-beta.
-        # I should do CHANGES-release.
-        print "done."
-
-
-    def abstract (self, verbose=0, dryRun=0):
-        """Extract recent logs and other info useful in announcements.
-
-        Write them to a file in the staging directory."""
-
-        re0 = r'XEmacs.* (\d+)\.(\d+)(\.|-beta)(\d+).*is released'
-        re1 = r'^to( XEmacs)? (\d+)\.(\d+)\.(\d+) "(.+)"$'
-        os.chdir(self.project.stage + "/" + self.stem)
-
-        # handle CHANGES-{beta,release}
-        lines = ['Brief summary of Changes to %s "%s"\n\n' %
-                 (self.versionString,self.codename)]
-        if self.isBeta:
-            changesFile = "CHANGES-beta"
-        else:
-            changesFile = "CHANGES-release"
-        # FIXME: handle missing or empty changesFile
-        f = open(changesFile,"r")
-        line = f.readline()
-        if verbose > 1:
-            print line
-        m = match(re1, line)
-        while line and not m:
-            line = f.readline()
-            if verbose > 1:
-                print line
-            m = match(re1, line)
-        if (m.group(5) != self.codename or
-            int(m.group(4)) != self.releaseVersion or
-            int(m.group(3)) != self.minorVersion or
-            int(m.group(2)) != self.majorVersion):
-            # FIXME: should be able to make this exception a warning
-            raise ValueError, "Incorrect version in " + changesFile
-        line = f.readline()
-        while line:
-            if verbose > 1:
-                print line
-            # FIXME: maybe this should be less fuzzy
-            m = match(re1, line)
-            if m:
-                break
-            else:
-                lines.append(line)
-                line = f.readline()
-        f.close()
-
-        # handle ChangeLogs
-        for log in findChangeLogs ("."):
-            if verbose > 0:
-                print log
-            # consistent with Martin's format
-            lines.append("-------- ChangeLog entries from " +
-                         log[2:] +
-                         " --------\n\n")
-            f = open(log,"r")
-            line = f.readline()
-            while line:
-                if verbose > 1:
-                    print line
-                # FIXME: maybe this should be less fuzzy
-                m = search(re0, line)
-                if (m and
-                    (int(m.group(4)) != self.releaseVersion or
-                     int(m.group(2)) != self.minorVersion or
-                     int(m.group(1)) != self.majorVersion)):
-                    lines = lines[:-2]
-                    break
-                else:
-                    lines.append(line)
-                    line = f.readline()
-            f.close()
-        f = open(self.project.stage + "/Abstract-" + self.stem, "w")
-        for line in lines:
-            f.write(line)
-        f.close()
-
-
-    def package (self, verbose=2, dryRun=0):
-        # FIXME: This function needs to be verbosified!!
-        """Create distribution ready for upload.
-
-        Distribution includes patch, tarball(s), and PGP signatures.
-
-        N.B. `dryRun' is _not_ a Boolean:  if > 1, do not make the patch
-        and tarball(s).  If > 0 fake the signature files (to avoid
-        repeated entry of the pass phrase), else sign gzip files with
-        GnuPG (ASCII armored).
-        FIXME: This use of dryRun is too tricky."""
-
-        if dryRun > 1:
-            print "Dry run, not making tarballs."
-            return
-        
-        # Procedure
-        # 1.  cd to staging directory.
-        os.chdir(self.project.stage)
-        # 2.  Make patch.
-        #     This should be done before making tarballs, as the tarball
-        #     creation process involves generation of .elcs and .infos.
-        newspace = self.stem
-        # by now this should be initialized
-        oldspace = self.previousStem
-        patchkit = self.previousStem + "-" + self.versionString + ".patch"
-        findcmd =  "find . -name CVS -prune -or -type f -print > MANIFEST"
-        os.chdir(self.previousStem)
-        print "cd to " + self.previousStem
-        execAndTrace(findcmd)
-        os.chdir("../" + self.stem)
-        print "cd to ../" + self.stem
-        execAndTrace(findcmd)
-        os.chdir("..")
-
-        # FIXME: excludes should be shared between makepatch and tar
-        # need to exclude images and compressed files
-        cmd = "makepatch --diff 'diff --text -u' --verbose " \
-              + "--exclude MANIFEST --exclude .precious " + oldspace + " " \
-              + newspace + " > " + patchkit
-        execAndTrace(cmd)
-        execAndTrace("gzip --best --force " + patchkit)
-        #     Here's Martin's Perl procedure:
-        #   sub make_patch {
-        #     die $usage unless @_ == 2;
-        #     my ($oldws, $newws) = @_;
-        #     die $usage unless -d $oldws && -d $newws;
-        #     my $oldmanifest = "$oldws.manifest";
-        #     my $newmanifest = "$newws.manifest";
-        #     
-        #     MakeManifest ($oldws, $oldmanifest);
-        #     MakeManifest ($newws, $newmanifest);
-        #     
-        #     my $makepatch =
-        #       InputPipe 'makepatch', '-verbose',
-        #       # Wise installer filename has embedded blank
-        #       '-exclude', 'display',
-        #       '-diff', 'diff --text -u',
-        #       '-oldmanifest', $oldmanifest,
-        #       '-newmanifest', $newmanifest,
-        #       $oldws, $newws;
-        #     
-        #     my $pid = open ((my $gzip = new FileHandle), "|-");
-        #     die "Can't fork: $!" unless defined $gzip && defined $pid;
-        #     if ($pid == 0) {
-        #       my $patch = patchfilename ($oldws, $newws);
-        #       open (STDOUT, "> $patch") or die "$patch: $!";
-        #       exec 'gzip', '--best', '--force' or die "Can't exec gzip: $!";
-        #     }
-        #     
-        #     while (<$makepatch>) { print $gzip $_; }
-        #     SafeClose $makepatch;
-        #     SafeClose $gzip;
-        #   }
-        # 3.  Make tarballs.
-        self.makeTarballs(self.stem,self.isBeta,verbose)
-        # 4.  PGP sign tarball(s) and patch.
-        # Unfortunately, it doesn't seem possible to batch this.
-        if dryRun == 0:
-            cmd = 'for i in *' + self.versionString + \
-                  '*.gz; do gpg --armor --detach-sign $i; done'
-        else:
-            cmd = 'for i in *' + self.versionString + \
-                  '*.gz; do echo "Faked signature" >$i.asc; done'
-        execAndTrace(cmd)
-
-    def check (self, dryRun=0):
-        """Test the distribution.
-
-        Unpack the tarball(s), apply the patch, recursively diff, build
-        current.  gunzip everything.  Verify the signatures.
-
-        `dryRun' argument is ignored."""
-        if 0:                           # not ready for prime time
-            # Procedure:
-            # 1. Download old full tarball.  Unpack.
-            # 2. Unpack new full tarball.
-            # 3. Patch old tree.  Diff against new.  Remove old tree.
-            # 4. Move new tree.  Unpack disaggregated tarballs.  Diff.
-            # 5. tar tf full.tar.gz | sort > full.manifest
-            #    (for f in pars; do tar tf $f; done) | sort > union.manifest
-            #    uniq -d union.manifest should be empty
-            #    diff full.manifest union.manifest should be empty
-            # 6. ./configure --prefix=$staging/install; make; make check;
-            #    make install
-            # 7. gzip -t *.gz
-            # 8. Verify signatures.
-            #    for i in *.gz; do gpg --verify $i.asc $i; done
-            # 9. print out versions and stuff for hand-checking.
-            pass
-        else:
-            os.chdir(self.project.stage)
-            print "You need to do release.check() by hand."
-
-    def tag (self, verbose=2, dryRun=1):
-        """Commit and tag the CVS repository.
-
-        NB: Expects to find a fresh branch already made for a .0 release."""
-
-        project = self.project
-        engineer = self.engineer
-	# FIXME: ensure that CVS_RSH is set and the ssh-agent is set up
-        cvsLogin = ":ext:%s@%s:%s" % (engineer.cvsAuthUser,
-                                      project.cvsHost,
-                                      project.cvsRoot)
-        stem = "r%d-%d" % (self.majorVersion,self.minorVersion)
-        if dryRun:                           # not ready for prime time
-            print \
-"""Dry run, not executing commit and tag operations.
-Commit changed files (should only be ChangeLogs and version.sh, maybe
-CHANGES-beta or CHANGES-release, maybe configure) and tag Repository:
-1. mv configure configure.save; autoconf; if diff configure configure.save;
-   then rm configure.save; cvs commit configure; else mv configure.save
-   configure; fi (FIXME: unimplemented
-   Should be able to do this with a cvs update after autoconf, or a
-   cp and diff strategy (this is better, can do without net).)
-2. for f in $changed_files; cvs -d $repository commit -m $herald $f; done
-   # necessary because of cvs.xemacs.org fuckage
-3. cvs update, checking for any changes or modification
-4. cvs -d %s tag %s-%d
-5. cvs -d %s tag -r %s-%d %s-current
-
-You can run the beta version of this function with `release.tag(0)'.
-""" \
-            % (cvsLogin, stem, self.releaseVersion, cvsLogin, stem, \
-               self.releaseVersion, stem)
-
-        os.chdir(project.stage + "/" + self.stem)
-        changedFiles = [ "version.sh", "configure" ]
-        changedFiles += findChangeLogs(".")
-        cmd = "cvs -d %s commit -m '%s'" % (cvsLogin,self.herald)
-        for f in changedFiles:
-            if f != "CVS":
-                cmd += " " + f
-        commitCarefully(cmd,verbose,dryRun)
-        cmd = "cvs -d %s tag -F %s-%d ." % (cvsLogin,stem,self.releaseVersion)
-        execAndTrace(cmd,verbose,dryRun)
-        if self.isBeta:
-            cmd = "cvs -d %s tag -r %s-%d -F %s-latest-beta ." \
-                  % (cvsLogin,stem,self.releaseVersion,stem)
-            execAndTrace(cmd,verbose,dryRun)
-
-    def upload (self, verbose=2, dryRun=1):
-        """Upload distribution to primary distribution host.
-
-        Distribution includes tarball(s), patch, and PGP signatures."""
-        if dryRun:                      # not ready for prime time
-            print """Dry run: not executing uploads.
-
-You can run the beta version of this function with `release.upload(0)'."""
-
-        # Procedure:
-        # 0. generic config
-        project = self.project
-        engineer = self.engineer
-        os.chdir(project.stage)
-
-        login = engineer.uploadAuthUser + "@" + project.uploadHost
-        if self.isBeta:
-            distdir = "%s/beta/%s" % (project.uploadRoot, self.versionDir)
-            tmpdir = distdir + "/tmp"
-        else:
-            distdir = "%s/%s" % (project.uploadRoot, self.versionDir)
-            tmpdir = distdir + "/pretest"
-        latest = "LATEST.IS." + self.versionString + " " \
-                 + "LATEST-IS-" + self.versionString + " "
-        patchkit = self.previousStem + "-" + self.versionString + ".patch.gz"
-        # 1. Construct list of uploads: tarball(s), patch, .sigs.
-        if self.isBeta:
-            uploadlist = [
-                self.stem + ".tar.gz",
-                patchkit
-                ]
-        else:
-            uploadlist = [
-                self.stem + ".tar.gz",
-                self.stem + "-src.tar.gz",
-                self.stem + "-elc.tar.gz",
-                self.stem + "-info.tar.gz",
-                patchkit
-                ]
-        tarballs = ""
-        pgpFiles = ""
-        for f in uploadlist[:]:
-            if not f == patchkit:       # treat specially in timestamping
-                tarballs += " " + f
-            pgpFiles += " " + f + ".asc"
-            uploadlist.append(f + ".asc")
-        tarballs = tarballs[1:]
-        # don't forget patchkit here
-        uploads = tarballs + " " + patchkit + " " + pgpFiles
-        pgpFiles = pgpFiles[1:]
-
-        # paranoia re premature access
-        for f in uploadlist:
-            if verbose > 0: print "chmod 0600 " + f
-            os.chmod(f,0600)
-
-        for cmd in [
-            "ssh %s mkdir -p %s" % (login,tmpdir),
-            "ssh %s 'cd %s && rm -f %s'" % (login,tmpdir,uploads),
-            "scp -q %s %s:%s/" % (uploads,login,tmpdir),
-            "ssh %s 'cd %s && rm -f %s'" % (login,distdir,uploads)
-            ]:
-            execAndTrace(cmd,verbose,dryRun)
-
-
-    def makePublic (self, verbose=2, dryRun=2):
-        """Maybe move distribution to public directory.  Always timestamp.
-
-        Distribution includes tarball(s), patch, and PGP signatures.
-
-        `dryRun'==0 means stamp and move.  `dryRun'==1 means stamp only.
-        `dryRun'==2 means do nothing.
-
-        FIXME: this overload of dryRun is too tricky."""
-
-        # abbreviations
-        project = self.project
-        engineer = self.engineer
-        login = engineer.uploadAuthUser + "@" + project.uploadHost
-        patchkit = self.previousStem + "-" + self.versionString + ".patch.gz"
-        # on upload host
-        if self.isBeta:
-            distdir = "%s/beta/%s/" % (project.uploadRoot,self.versionDir)
-            tmpdir = distdir + "tmp/"
-        else:
-            distdir = "%s/%s/" % (project.uploadRoot,self.versionDir)
-            tmpdir = distdir + "pretest/"
-        latest = "LATEST.IS." + self.versionString + " " \
-                 + "LATEST-IS-" + self.versionString + " "
-
-        os.chdir(project.stage)
-
-        # 1. Construct list of uploads: tarball(s), patch, .sigs.
-        if self.isBeta:
-            uploadlist = [
-                self.stem + ".tar.gz",
-                patchkit
-                ]
-        else:
-            uploadlist = [
-                self.stem + ".tar.gz",
-                self.stem + "-src.tar.gz",
-                self.stem + "-elc.tar.gz",
-                self.stem + "-info.tar.gz",
-                patchkit
-                ]
-        tarballs = ""
-        pgpFiles = ""
-        for f in uploadlist[:]:
-            if not f == patchkit:       # treat specially in timestamping
-                tarballs += " " + f
-            pgpFiles += " " + f + ".asc"
-            uploadlist.append(f + ".asc")
-        tarballs = tarballs[1:]
-        pgpFiles = pgpFiles[1:]
-
-        # don't forget patchkit here
-        uploads = tarballs + " " + patchkit + " " + pgpFiles
-
-        cmd = "ssh %s 'cd %s && chmod 0600 %s && mv %s %s'" \
-              % (login,tmpdir,uploads,uploads,distdir)
-        execAndTrace(cmd,verbose,dryRun)
-
-        # 7. Timestamps in the right order...
-        # Um, Martin, why is this right?
-        # build up a remote command
-        if dryRun == 1:
-            remotecmd = "cd " + tmpdir
-        else:
-            remotecmd =   "cd " + distdir \
-                        + " && rm -f LATEST?IS*" \
-                        + " && touch " + latest + " && sleep 1"
-        remotecmd +=   " && touch " + pgpFiles + " && sleep 1" \
-                     + " && touch " + patchkit + " && sleep 1" \
-                     + " && touch " + tarballs + " && sleep 1" \
-                     + " && chmod 444 " + uploads
-        if dryRun != 1:
-            remotecmd += " " + latest
-        # quote for the local shell and execute it
-        if dryRun > 0:
-            dryRun -= 1
-        cmd = "ssh %s '%s'" % (login,remotecmd)
-        execAndTrace(cmd,verbose,dryRun)
-
-    def announce (self, dryRun=1):
-        """Post announcements.
-
-        Relevant channels include xemacs-announce, the home page, and
-        FreshMeat."""
-        if dryRun:                      # not ready for prime time
-            print """Dry run: not posting announcements.
-You must implement each step of release.announce() by hand.
-Post the announcement to the home pages (Martin has this automated, how?),
-./index.content, ./Releases/index.content, and ./Releases/$VERSION.content.
-Update /pub/xemacs/{.message,README} on FTP site by ssh and sed.  Or see
-updateFtpREADME and updateFtpMessage below.
-Post Release Manager Activity Report to xemacs-beta.
-Post to xemacs-beta (for betas and prereleases) and to xemacs-announce
-(for public releases).  Also notify FreshMeat for public releases."""
-        else:
-            os.chdir(project.stage)
-            print \
-"""You must execute release.announce() by hand.
-Post the announcement to the home pages (Martin has this automated, how?),
-./index.content, ./Releases/index.content, and ./Releases/$VERSION.content.
-Update /pub/xemacs/{.message,README} on FTP site by ssh and sed.  Or see
-updateFtpREADME and updateFtpMessage below.
-Post Release Manager Activity Report to xemacs-beta.
-Post to xemacs-beta (for betas and prereleases) and to xemacs-announce
-(for public releases).  Also notify FreshMeat for public releases."""
-
-    ## Private methods
-    def updateFtpREADMEs (self, verbose=2, dryRun=1):
-        """Update ftp://ftp.xemacs.org/pub/xemacs/{README,.message}.
-
-        If dryRun < 2, write the local file.  If dryRun == 0, scp to the
-        remote site as well."""
-
-        # abbreviations
-        project = self.project
-        engineer = self.engineer
-        login = engineer.uploadAuthUser + "@" + project.uploadHost
-
-        # define and initialize a Filter
-        def filterFunction (line, filter):
-            m = match(filter.regexp,line)
-            if m:
-                filter.matchCount += 1
-                filter.text += "%s%d%s\n" % (m.group(1), \
-                                             self.releaseVersion, \
-                                             m.group(2)))
-            else:
-                filter.text += (line + "\n")
-            return filter
-
-        filter = Filter()
-        filter.regexp = r"^(.*%d\.%d\.)\d+(.*)$" % (self.majorVersion, \
-                                                    self.minorVersion)
-        filter.matchCount = 0
-        filter.filter = filterFunction
-        filter.text = ""
-
-        if verbose: print "cd " + project.stage
-        os.chdir(project.stage)
-
-        # FIXME: too much repeated code!
-        # process README
-        cmd = "ssh %s 'cat /ftp/pub/tux/xemacs/README'" % (login)
-        # cmd never changes anything, never dry run here.
-        filter = execAndFilter(cmd,filter,verbose,dryRun=0)
-        if filter.matchCount != 0:
-            print "Warning: unexpected number of matches in README: %d" \
-                  % (filter.matchCount)
-        fn = self.versionString+".README"
-        if dryRun < 2:
-            print "XRE> ",
-            file = open(fn,"w")
-            file.write(filter.text)
-            file.close()
-        else:
-            print "DRYRUN> ",
-        print "Wrote " + fn + "."
-        cmd = "scp %s %s:/ftp/pub/tux/xemacs/README'" % (fn,login)
-        execAndTrace(cmd,verbose,dryRun)
-
-        # reinitialize filter
-        filter.matchCount = 0
-        filter.text = ""
-
-        # process .message
-        cmd = "ssh %s 'cat /ftp/pub/tux/xemacs/.message'" % (login)
-        # cmd never changes anything, never dry run here.
-        filter = execAndFilter(cmd,filter,verbose,dryRun=0)
-        if filter.matchCount != 0:
-            print "Warning: unexpected number of matches in .message: %d" \
-                  % (filter.matchCount)
-        fn = self.versionString+"..message"
-        if dryRun < 2:
-            print "XRE> ",
-            file = open(fn,"w")
-            file.write(filter.text)
-            file.close()
-        else:
-            print "DRYRUN> ",
-        print "Wrote " + fn + "."
-        cmd = "scp %s %s:/ftp/pub/tux/xemacs/.message'" % (fn,login)
-        execAndTrace(cmd,verbose,dryRun)
-
-    def updateChangeLogs (self, herald=None, engineer=None, date=None):
-        """Update ChangeLogs.
-
-        Finds all ChangeLogs under current directory, adds a log item
-        containing the herald (string) by engineer (Developer object) on
-        date (string in format YYYY-MM-DD).  Herald and engineer default
-        to those of the current project, date defaults to today UTC."""
-        if not herald: herald = self.herald
-        if not engineer: engineer = self.engineer
-        if not date: date = strftime("%Y-%m-%d",gmtime(time()))
-        changelogs = findChangeLogs(".")
-        for log in changelogs:
-            print "  Adding herald to " + log + " ...",
-            new = os.tempnam(dirname(log))
-            infile = open(log,"r")
-            outfile = open(new,"w")
-            logmsg = \
-"""%s  %s  <%s@%s>
-
-	* %s
-
-"""                   % (date,
-                         engineer.fullName,
-                         # This shows that Developer and Project structures
-                         # are incorrect.
-                         engineer.projectAlias,
-                         self.project.mailDomain,
-                         herald)
-            if outfile:
-                outfile.write(logmsg)
-                if infile:
-                    outfile.write(infile.read())
-                    infile.close()
-                else:
-                    print "\nError: couldn't open old ChangeLog."
-                outfile.close()
-                # FIXME: do some verification
-                os.unlink(log)
-                os.rename(new,log)
-            else:
-                print "\nError: couldn't open new ChangeLog!"
-            print " done."
-
-    def revertChangeLogs (self, herald=None, engineer=None, date=None):
-        """Revert ChangeLogs and version.sh to current CVS state.
-
-        Finds all ChangeLogs under current directory."""
-
-        # This function used to try to unmunge.  This is simpler and more
-        # to the point.
-        # FIXME: Use execAndTrace()?
-    
-        changelogs = findChangeLogs(".")
-        targets = "version.sh"
-        for log in changelogs:
-            targets += " " + log
-        os.system("rm -f " + targets)
-        os.system("cvs update " + targets)
-
-    # This is very XEmacs-specific and probably should be moved out of here.
-    def makeTarballs (self, dir, fullOnly=0, verbose=2):
-        """Make distribution tarballs for XEmacs.
-
-        Always exclude the CVS, xemacs-packages, and mule-packages
-        subdirectories and files named ".precious", "TAGS", or "MANIFEST".
-        ("MANIFEST" is produced as a byproduct of makepatch.)
-        Always make the full source + compiled Lisp + Info tarball.
-        If fullOnly is false, also make the disaggregated source-only,
-        elc, and info tarballs.
-
-        Assumes cwd is the staging directory."""
-
-        if not isdir(dir):
-            raise TypeError, dir + " is not a directory."
-
-        def makeTarball (tarball, arguments, verbose = 1):
-            if os.access(tarball,os.F_OK):
-                os.unlink(tarball)
-            cmd = "tar --create --file " + tarball + arguments
-            execAndTrace(cmd)
-            if os.access(tarball+".gz",os.F_OK):
-                os.unlink(tarball+".gz")
-            execAndTrace("gzip --best --force " + tarball)
-
-        excludeAlways  = " --exclude CVS --exclude .precious --exclude TAGS" \
-                         + " --exclude MANIFEST --exclude xemacs-packages" \
-                         + " --exclude mule-packages"
-        excludeGenerated = " --exclude '*.info*' --exclude '*.elc'"
-
-        if not fullOnly:
-            # make src tarball first
-            tarball = dir + "-src.tar"
-            targets = " " + dir
-            makeTarball(tarball,excludeAlways+excludeGenerated+targets)
-
-        # build and clean leaving .elcs and .infos
-        # FIXME: *must* check error returns!!  system() is inappropriate!!
-        ret = execAndTrace("cd " + dir + " && ./configure --with-mule && make && make distclean")
-        if ret.code: print "Error return (%d) from build!" % (ret)
-
-        # make full tarball
-        tarball = dir + ".tar"
-        targets = " " + dir
-        makeTarball(tarball,excludeAlways+targets)
-
-        if not fullOnly:
-            # make elc and info tarballs
-            tarball = dir + "-elc.tar"
-            targets = " `find " + dir + " -name '*.elc'`"
-            makeTarball(tarball,excludeAlways+targets)
-
-            tarball = dir + "-info.tar"
-            targets = " " + dir + "/info"
-            makeTarball(tarball,excludeAlways+targets)
-
-#### Finalizing the CVS tree
-
-# Finalizing is different depending on whether the release is a beta
-# release or a public release.
-
-# In the case of a beta release, the tree remains open to commits, so
-# there is some possibility of a developer committing while the
-# release is in process.  In periods of active development, the
-# release engineer is fairly likely to be out of synch with other
-# developers.  For this reason, the CVS repository is considered
-# authoritative, and the procedure updateCarefully is used to ensure
-# that the repository is in a more or less consistent state before
-# constructing the distribution.  At that point the tree is tagged
-# based on the local workspace.
-
-# For a public release, things are simpler.  The release engineer is
-# assumed to maintain control of the tree, and thus the local
-# workspace is considered authoritative.  This is implemented by
-# committing the local state to the repository, and tagging.
-
-# A tricky case is where the public release is the initial release of
-# a version.  Then a branch must be created, rooted at a branch-point
-# tag.  The initial release need not (and probably will not) occur at
-# the branch point.
-
-# Finally, release markers are added to all ChangeLogs and the tags
-# are checked for consistency, and the ChangeLogs are committed and
-# retagged with the release tags.  N.B.  Martin uses a somewhat
-# different strategy here, it seems.
-
-def findChangeLogs(dir):
-    """Return list of ChangeLogs under `dir'.  strip() the strings."""
-
-    list = []
-    for c in os.popen("find " + dir + " -name ChangeLog").readlines():
-        list.append(strip(c))
-    return list
-
-class Filter:
-    """Implement filters.
-
-    There are two predefined attributes: `code', which is the status of the
-    filter, and `filter', which actually processes the stream.  Other
-    attributes may be defined for the use of the `filter'."""
-    def __init__ (self):
-        self.code = 0
-        self.filter = None
-
-
-def updateCarefully (command, verbose=2, dryRun=0):
-    """Repeatedly update (check out) the release until the tree is quiescent.
-
-    Return a Filter object.
-
-    Normally only two or three passes is necessary.  #### Fail ugly on
-    inconsistent status. (means what?)
-
-    `command' should be a CVS update or checkout command.  See execAndFilter
-    for the use of `verbose' and `dryRun', and for the interpretation of the
-    Filter object returned."""
-
-    def updateCarefullyFilter (line, filter):
-        #### Is this OK?  ACMR => commit needed, right?
-        if match("[UP] ",line):
-            filter.code = 1
-        if match("[ACMR] ",line):
-            filter.updateCarefullyInconsistencies.append(line)
-            filter.code = 1
-        return filter
-
-    needUpdate = Filter()
-    needUpdate.updateCarefullyInconsistencies = []
-    needUpdate.code = 1
-    needUpdate.filter = updateCarefullyFilter
-
-    while needUpdate.code and not needUpdate.updateCarefullyInconsistencies:
-        needUpdate.code = 0
-        needUpdate = execAndFilter(command,needUpdate,verbose,dryRun)
-
-    if needUpdate.updateCarefullyInconsistencies:
-        print "The repository and the workspace are inconsistent:"
-        for l in needUpdate.updateCarefullyInconsistencies:
-            print l
-        sys.exit(1)
-
-def commitCarefully (command, verbose=2, dryRun=0):
-    """Repeatedly commit the workspace until the tree is quiescent.
-
-    Return a Filter object.
-
-    Normally only two or three passes is necessary.  #### Fail ugly on
-    inconsistent status. (means what?)
-
-    `command' should be a CVS commit command.  See execAndFilter for the
-    use of `verbose' and `dryRun', and for the interpretation of the
-    Filter object returned."""
-
-    def commitCarefullyFilter (line, filter):
-        if match("Checking in ", line):
-            filter.code = 1
-            filter.commitCarefullyCommits.append(line)
-        return filter
-
-    needToCommit = Filter()
-    needToCommit.code = 1
-    needToCommit.commitCarefullyCommits = []
-    needToCommit.filter = commitCarefullyFilter
-
-    while needToCommit.code:
-        needToCommit.code = 0
-        needToCommit = execAndFilter(command,needToCommit,verbose,dryRun)
-
-
-def execAndFilter (command, filter=None, verbose=2, dryRun=0):
-    """Execute `command', filtering output with `filter'.filter.
-
-    `filter''s filter is a function taking two arguments: the current
-    line from `command''s output, and the current filter itself.  It
-    is applied to each line of output from `command' in turn, and its
-    return value is assigned to the current status.
-
-    The default if `filter' is None is a Filter with filter = None.
-    Effectively this just provides a status code.
-
-    Normally display a trace to standard output.  The `verbose' flag
-    determines the level of detail.  If positive, `command' is
-    printed.  If verbose > 1, print output lines from the command.
-    FIXME: should print filtered lines with verbose = 2, and raw
-    output with verbose > 2.  If the `dryRun' parameter is positive,
-    print but do not execute the command, regardless of the level of
-    `verbose'.
-
-    Return a Filter object.  The status code is in the `code' attribute.
-    0 means normal execution and exit.  Negative statuses are errors.
-    Positive values should not occur."""
-
-    #
-    if filter == None:
-        filter = Filter()
-
-    if dryRun > 0:
-        print "DRYRUN> " + command
-        return filter
-    if verbose > 0:
-        print "XRE> " + command
-    f = os.popen(command)
-    if not f:
-        print "Error: pipe(" + command + ") failed."
-        # not very graceful
-        sys.exit (1)
-
-    if type(filter) is InstanceType and filter.__class__ == Filter:
-        while 1:
-            l = f.readline()
-            if l: l = strip(l)
-            else: break
-            if filter and filter.filter: filter = filter.filter(l,filter)
-            if verbose > 1: print l
-
-        pipestatus = f.close()
-        if pipestatus:
-            if pipestatus > 0:
-                filter.code = -pipestatus
-            else:
-                # shouldn't happen?
-                filter.code = pipestatus
-        return filter
-    else:
-        raise TypeError, "the value of `filter' is not a Filter object."
-
-
-def execAndTrace (command, verbose=2, dryRun=0):
-    """Execute `command'.
-
-    Normally display a trace to standard output.  The `verbose' flag
-    determines the level of detail.  If the `dryRun' parameter is set,
-    print but do not execute the command.
-
-    Return a Filter object.  The status code is in the `code' attribute.
-    0 means normal execution and exit.  Negative statuses are errors.
-    Positive values should not occur."""
-
-    return execAndFilter (command,None,verbose,dryRun)
-
-
-def printObject (objectName):
-    print "Contents of", objectName
-    for att in dir(eval(objectName)):
-        print att, ':', eval(objectName + "." + att)
-
-
-## global data initialization
-
-martin = Developer(fullName        = "Martin R. Buchholz",
-                   comment         = "Beta Release Engineer, XEmacs 21.2",
-                   projectAlias    = "martin",
-                   mailingAddress  = "mrb@m17n.org",
-                   cvsAuthUser     = "martinb",
-                   uploadAuthUser  = "mrb"
-                   )
-martin.valid = 1
-
-steve = Developer(fullName        = "Stephen J. Turnbull",
-                  comment         = "Release Manager, XEmacs 21.4",
-                  projectAlias    = "stephen",
-                  mailingAddress  = "turnbull@sk.tsukuba.ac.jp",
-                  cvsAuthUser     = "stephent",
-                  uploadAuthUser  = "turnbull"
-                  )
-steve.valid = 1
-
-xemacs = Project(fullName      = "XEmacs",
-                 alias         = "xemacs",
-                 mailDomain    = "xemacs.org",
-                 homePage      = "http://www.xemacs.org/",
-                 uploadHost    = "ftp.xemacs.org",
-                 uploadRoot    = "/ftp/pub/xemacs",
-                 cvsHost       = "cvs.xemacs.org",
-                 cvsAnonUser   = "xemacs",
-                 cvsAnonPasswd = "zawinski",
-                 cvsRoot       = "/usr/CVSroot",
-                 cvsModule     = "xemacs",
-                 sourceSize    = 0,     # FIXME: should be 40!!
-                 buildSize     = 0,     # FIXME: should be 35!!
-                 installSize   = 0,     # FIXME: should be 50!!
-                 versionFile   = "version.sh",
-                 versionFormat = (r"#!/bin/sh",
-                                  r"emacs_is_beta=(t?)", # match empty string
-                                  r"emacs_major_version=(\d+)",
-                                  r"emacs_minor_version=(\d+)",
-                                  r"emacs_beta_version=(\d+)",
-                                  r'xemacs_codename="(.+)"',
-                                  r"infodock_major_version=\d+",
-                                  r"infodock_minor_version=\d+",
-                                  r"infodock_build_version=\d+"
-                                  ),
-                 stage          = "/playpen/mozilla/XEmacs"
-                 )
-xemacs.valid = 1
-
-#beta = Release(versionString = "21.2.47",
-#               ancestorVersionString = None, # unknown or new project
-#               codenameFile = "../Codenames-21.2",
-#               project = xemacs,
-#               engineer = steve,
-#               isBeta = 0               # force 0 for prerelease
-#               )
-#beta.valid = 1
-beta = Release(versionString = "21.5.3",
-               ancestorVersionString = "21.2.47", # unknown or new project
-               codenameFile = "etc/VEGETABLES",
-               project = xemacs,
-               engineer = steve,
-               cvsTag = "HEAD",
-               copySource = "/home/steve/Projects/XEmacs/xemacs-21.5",
-               isBeta = 1
-               )
-beta.valid = 1
-
-gamma = Release(versionString = "21.4.4",
-                ancestorVersionString = "21.2.47",
-                codenameFile = "etc/OXYMORONS",
-                project = xemacs,
-                cvsTag = "release-21-4",
-                copySource = "/home/steve/Projects/XEmacs/21.4-HEAD",
-                engineer= steve,
-                isBeta = 0
-                )
-gamma.valid = 1
-