Index: third_party/buildbot_7_12/buildbot/scripts/runner.py
|
diff --git a/third_party/buildbot_7_12/buildbot/scripts/runner.py b/third_party/buildbot_7_12/buildbot/scripts/runner.py
|
deleted file mode 100644
|
index 246fe098d4756a8e42693c71c184edb7675f1aaf..0000000000000000000000000000000000000000
|
--- a/third_party/buildbot_7_12/buildbot/scripts/runner.py
|
+++ /dev/null
|
@@ -1,1105 +0,0 @@
|
-# -*- test-case-name: buildbot.test.test_runner -*-
|
-
|
-# N.B.: don't import anything that might pull in a reactor yet. Some of our
|
-# subcommands want to load modules that need the gtk reactor.
|
-import os, sys, stat, re, time
|
-import traceback
|
-from twisted.python import usage, util, runtime
|
-
|
-from buildbot.interfaces import BuildbotNotRunningError
|
-
|
-# the create/start/stop commands should all be run as the same user,
|
-# preferably a separate 'buildbot' account.
|
-
|
-# Note that the terms 'options' and 'config' are used intechangeably here - in
|
-# fact, they are intercanged several times. Caveat legator.
|
-
|
-class OptionsWithOptionsFile(usage.Options):
|
- # subclasses should set this to a list-of-lists in order to source the
|
- # .buildbot/options file.
|
- # buildbotOptions = [ [ 'optfile-name', 'option-name' ], .. ]
|
- buildbotOptions = None
|
-
|
- def __init__(self, *args):
|
- # for options in self.buildbotOptions, optParameters, and the options
|
- # file, change the default in optParameters *before* calling through
|
- # to the parent constructor
|
-
|
- if self.buildbotOptions:
|
- optfile = loadOptionsFile()
|
- for optfile_name, option_name in self.buildbotOptions:
|
- for i in range(len(self.optParameters)):
|
- if self.optParameters[i][0] == option_name and optfile_name in optfile:
|
- self.optParameters[i][2] = optfile[optfile_name]
|
- usage.Options.__init__(self, *args)
|
-
|
-def loadOptionsFile(filename="options", here=None, home=None):
|
- """Find the .buildbot/FILENAME file. Crawl from the current directory up
|
- towards the root, and also look in ~/.buildbot . The first directory
|
- that's owned by the user and has the file we're looking for wins. Windows
|
- skips the owned-by-user test.
|
-
|
- @rtype: dict
|
- @return: a dictionary of names defined in the options file. If no options
|
- file was found, return an empty dict.
|
- """
|
-
|
- if here is None:
|
- here = os.getcwd()
|
- here = os.path.abspath(here)
|
-
|
- if home is None:
|
- if runtime.platformType == 'win32':
|
- home = os.path.join(os.environ['APPDATA'], "buildbot")
|
- else:
|
- home = os.path.expanduser("~/.buildbot")
|
-
|
- searchpath = []
|
- toomany = 20
|
- while True:
|
- searchpath.append(os.path.join(here, ".buildbot"))
|
- next = os.path.dirname(here)
|
- if next == here:
|
- break # we've hit the root
|
- here = next
|
- toomany -= 1 # just in case
|
- if toomany == 0:
|
- raise ValueError("Hey, I seem to have wandered up into the "
|
- "infinite glories of the heavens. Oops.")
|
- searchpath.append(home)
|
-
|
- localDict = {}
|
-
|
- for d in searchpath:
|
- if os.path.isdir(d):
|
- if runtime.platformType != 'win32':
|
- if os.stat(d)[stat.ST_UID] != os.getuid():
|
- print "skipping %s because you don't own it" % d
|
- continue # security, skip other people's directories
|
- optfile = os.path.join(d, filename)
|
- if os.path.exists(optfile):
|
- try:
|
- f = open(optfile, "r")
|
- options = f.read()
|
- exec options in localDict
|
- except:
|
- print "error while reading %s" % optfile
|
- raise
|
- break
|
-
|
- for k in localDict.keys():
|
- if k.startswith("__"):
|
- del localDict[k]
|
- return localDict
|
-
|
-class MakerBase(OptionsWithOptionsFile):
|
- optFlags = [
|
- ['help', 'h', "Display this message"],
|
- ["quiet", "q", "Do not emit the commands being run"],
|
- ]
|
-
|
- longdesc = """
|
- Operates upon the specified <basedir> (or the current directory, if not
|
- specified).
|
- """
|
-
|
- opt_h = usage.Options.opt_help
|
-
|
- def parseArgs(self, *args):
|
- if len(args) > 0:
|
- self['basedir'] = args[0]
|
- else:
|
- # Use the current directory if no basedir was specified.
|
- self['basedir'] = os.getcwd()
|
- if len(args) > 1:
|
- raise usage.UsageError("I wasn't expecting so many arguments")
|
-
|
- def postOptions(self):
|
- self['basedir'] = os.path.abspath(self['basedir'])
|
-
|
-makefile_sample = """# -*- makefile -*-
|
-
|
-# This is a simple makefile which lives in a buildmaster/buildslave
|
-# directory (next to the buildbot.tac file). It allows you to start/stop the
|
-# master or slave by doing 'make start' or 'make stop'.
|
-
|
-# The 'reconfig' target will tell a buildmaster to reload its config file.
|
-
|
-start:
|
- twistd --no_save -y buildbot.tac
|
-
|
-stop:
|
- kill `cat twistd.pid`
|
-
|
-reconfig:
|
- kill -HUP `cat twistd.pid`
|
-
|
-log:
|
- tail -f twistd.log
|
-"""
|
-
|
-class Maker:
|
- def __init__(self, config):
|
- self.config = config
|
- self.basedir = config['basedir']
|
- self.force = config.get('force', False)
|
- self.quiet = config['quiet']
|
-
|
- def mkdir(self):
|
- if os.path.exists(self.basedir):
|
- if not self.quiet:
|
- print "updating existing installation"
|
- return
|
- if not self.quiet: print "mkdir", self.basedir
|
- os.mkdir(self.basedir)
|
-
|
- def mkinfo(self):
|
- path = os.path.join(self.basedir, "info")
|
- if not os.path.exists(path):
|
- if not self.quiet: print "mkdir", path
|
- os.mkdir(path)
|
- created = False
|
- admin = os.path.join(path, "admin")
|
- if not os.path.exists(admin):
|
- if not self.quiet:
|
- print "Creating info/admin, you need to edit it appropriately"
|
- f = open(admin, "wt")
|
- f.write("Your Name Here <admin@youraddress.invalid>\n")
|
- f.close()
|
- created = True
|
- host = os.path.join(path, "host")
|
- if not os.path.exists(host):
|
- if not self.quiet:
|
- print "Creating info/host, you need to edit it appropriately"
|
- f = open(host, "wt")
|
- f.write("Please put a description of this build host here\n")
|
- f.close()
|
- created = True
|
- access_uri = os.path.join(path, "access_uri")
|
- if not os.path.exists(access_uri):
|
- if not self.quiet:
|
- print "Not creating info/access_uri - add it if you wish"
|
- if created and not self.quiet:
|
- print "Please edit the files in %s appropriately." % path
|
-
|
- def chdir(self):
|
- if not self.quiet: print "chdir", self.basedir
|
- os.chdir(self.basedir)
|
-
|
- def makeTAC(self, contents, secret=False):
|
- tacfile = "buildbot.tac"
|
- if os.path.exists(tacfile):
|
- oldcontents = open(tacfile, "rt").read()
|
- if oldcontents == contents:
|
- if not self.quiet:
|
- print "buildbot.tac already exists and is correct"
|
- return
|
- if not self.quiet:
|
- print "not touching existing buildbot.tac"
|
- print "creating buildbot.tac.new instead"
|
- tacfile = "buildbot.tac.new"
|
- f = open(tacfile, "wt")
|
- f.write(contents)
|
- f.close()
|
- if secret:
|
- os.chmod(tacfile, 0600)
|
-
|
- def makefile(self):
|
- target = "Makefile.sample"
|
- if os.path.exists(target):
|
- oldcontents = open(target, "rt").read()
|
- if oldcontents == makefile_sample:
|
- if not self.quiet:
|
- print "Makefile.sample already exists and is correct"
|
- return
|
- if not self.quiet:
|
- print "replacing Makefile.sample"
|
- else:
|
- if not self.quiet:
|
- print "creating Makefile.sample"
|
- f = open(target, "wt")
|
- f.write(makefile_sample)
|
- f.close()
|
-
|
- def sampleconfig(self, source):
|
- target = "master.cfg.sample"
|
- config_sample = open(source, "rt").read()
|
- if os.path.exists(target):
|
- oldcontents = open(target, "rt").read()
|
- if oldcontents == config_sample:
|
- if not self.quiet:
|
- print "master.cfg.sample already exists and is up-to-date"
|
- return
|
- if not self.quiet:
|
- print "replacing master.cfg.sample"
|
- else:
|
- if not self.quiet:
|
- print "creating master.cfg.sample"
|
- f = open(target, "wt")
|
- f.write(config_sample)
|
- f.close()
|
- os.chmod(target, 0600)
|
-
|
- def public_html(self, files):
|
- webdir = os.path.join(self.basedir, "public_html")
|
- if os.path.exists(webdir):
|
- if not self.quiet:
|
- print "public_html/ already exists: not replacing"
|
- return
|
- else:
|
- os.mkdir(webdir)
|
- if not self.quiet:
|
- print "populating public_html/"
|
- for target, source in files.iteritems():
|
- target = os.path.join(webdir, target)
|
- f = open(target, "wt")
|
- f.write(open(source, "rt").read())
|
- f.close()
|
-
|
- def populate_if_missing(self, target, source, overwrite=False):
|
- new_contents = open(source, "rt").read()
|
- if os.path.exists(target):
|
- old_contents = open(target, "rt").read()
|
- if old_contents != new_contents:
|
- if overwrite:
|
- if not self.quiet:
|
- print "%s has old/modified contents" % target
|
- print " overwriting it with new contents"
|
- open(target, "wt").write(new_contents)
|
- else:
|
- if not self.quiet:
|
- print "%s has old/modified contents" % target
|
- print " writing new contents to %s.new" % target
|
- open(target + ".new", "wt").write(new_contents)
|
- # otherwise, it's up to date
|
- else:
|
- if not self.quiet:
|
- print "populating %s" % target
|
- open(target, "wt").write(new_contents)
|
-
|
- def upgrade_public_html(self, files):
|
- webdir = os.path.join(self.basedir, "public_html")
|
- if not os.path.exists(webdir):
|
- if not self.quiet:
|
- print "populating public_html/"
|
- os.mkdir(webdir)
|
- for target, source in files.iteritems():
|
- self.populate_if_missing(os.path.join(webdir, target),
|
- source)
|
-
|
- def check_master_cfg(self):
|
- from buildbot.master import BuildMaster
|
- from twisted.python import log, failure
|
-
|
- master_cfg = os.path.join(self.basedir, "master.cfg")
|
- if not os.path.exists(master_cfg):
|
- if not self.quiet:
|
- print "No master.cfg found"
|
- return 1
|
-
|
- # side-effects of loading the config file:
|
-
|
- # for each Builder defined in c['builders'], if the status directory
|
- # didn't already exist, it will be created, and the
|
- # $BUILDERNAME/builder pickle might be created (with a single
|
- # "builder created" event).
|
-
|
- # we put basedir in front of sys.path, because that's how the
|
- # buildmaster itself will run, and it is quite common to have the
|
- # buildmaster import helper classes from other .py files in its
|
- # basedir.
|
-
|
- if sys.path[0] != self.basedir:
|
- sys.path.insert(0, self.basedir)
|
-
|
- m = BuildMaster(self.basedir)
|
- # we need to route log.msg to stdout, so any problems can be seen
|
- # there. But if everything goes well, I'd rather not clutter stdout
|
- # with log messages. So instead we add a logObserver which gathers
|
- # messages and only displays them if something goes wrong.
|
- messages = []
|
- log.addObserver(messages.append)
|
- try:
|
- # this will raise an exception if there's something wrong with
|
- # the config file. Note that this BuildMaster instance is never
|
- # started, so it won't actually do anything with the
|
- # configuration.
|
- m.loadConfig(open(master_cfg, "r"))
|
- except:
|
- f = failure.Failure()
|
- if not self.quiet:
|
- print
|
- for m in messages:
|
- print "".join(m['message'])
|
- print f
|
- print
|
- print "An error was detected in the master.cfg file."
|
- print "Please correct the problem and run 'buildbot upgrade-master' again."
|
- print
|
- return 1
|
- return 0
|
-
|
-class UpgradeMasterOptions(MakerBase):
|
- optFlags = [
|
- ["replace", "r", "Replace any modified files without confirmation."],
|
- ]
|
-
|
- def getSynopsis(self):
|
- return "Usage: buildbot upgrade-master [options] [<basedir>]"
|
-
|
- longdesc = """
|
- This command takes an existing buildmaster working directory and
|
- adds/modifies the files there to work with the current version of
|
- buildbot. When this command is finished, the buildmaster directory should
|
- look much like a brand-new one created by the 'create-master' command.
|
-
|
- Use this after you've upgraded your buildbot installation and before you
|
- restart the buildmaster to use the new version.
|
-
|
- If you have modified the files in your working directory, this command
|
- will leave them untouched, but will put the new recommended contents in a
|
- .new file (for example, if index.html has been modified, this command
|
- will create index.html.new). You can then look at the new version and
|
- decide how to merge its contents into your modified file.
|
- """
|
-
|
-def upgradeMaster(config):
|
- basedir = config['basedir']
|
- m = Maker(config)
|
- # TODO: check Makefile
|
- # TODO: check TAC file
|
- # check web files: index.html, default.css, robots.txt
|
- webdir = os.path.join(basedir, "public_html")
|
- m.upgrade_public_html({
|
- 'index.html' : util.sibpath(__file__, "../status/web/index.html"),
|
- 'bg_gradient.jpg' : util.sibpath(__file__, "../status/web/bg_gradient.jpg"),
|
- 'buildbot.css' : util.sibpath(__file__, "../status/web/default.css"),
|
- 'robots.txt' : util.sibpath(__file__, "../status/web/robots.txt"),
|
- })
|
- m.populate_if_missing(os.path.join(basedir, "master.cfg.sample"),
|
- util.sibpath(__file__, "sample.cfg"),
|
- overwrite=True)
|
- rc = m.check_master_cfg()
|
- if rc:
|
- return rc
|
- if not config['quiet']:
|
- print "upgrade complete"
|
-
|
-
|
-class MasterOptions(MakerBase):
|
- optFlags = [
|
- ["force", "f",
|
- "Re-use an existing directory (will not overwrite master.cfg file)"],
|
- ]
|
- optParameters = [
|
- ["config", "c", "master.cfg", "name of the buildmaster config file"],
|
- ["log-size", "s", "1000000",
|
- "size at which to rotate twisted log files"],
|
- ["log-count", "l", "None",
|
- "limit the number of kept old twisted log files"],
|
- ]
|
- def getSynopsis(self):
|
- return "Usage: buildbot create-master [options] [<basedir>]"
|
-
|
- longdesc = """
|
- This command creates a buildmaster working directory and buildbot.tac
|
- file. The master will live in <dir> and create various files there.
|
-
|
- At runtime, the master will read a configuration file (named
|
- 'master.cfg' by default) in its basedir. This file should contain python
|
- code which eventually defines a dictionary named 'BuildmasterConfig'.
|
- The elements of this dictionary are used to configure the Buildmaster.
|
- See doc/config.xhtml for details about what can be controlled through
|
- this interface."""
|
-
|
- def postOptions(self):
|
- MakerBase.postOptions(self)
|
- if not re.match('^\d+$', self['log-size']):
|
- raise usage.UsageError("log-size parameter needs to be an int")
|
- if not re.match('^\d+$', self['log-count']) and \
|
- self['log-count'] != 'None':
|
- raise usage.UsageError("log-count parameter needs to be an int "+
|
- " or None")
|
-
|
-
|
-masterTAC = """
|
-from twisted.application import service
|
-from buildbot.master import BuildMaster
|
-
|
-basedir = r'%(basedir)s'
|
-configfile = r'%(config)s'
|
-rotateLength = %(log-size)s
|
-maxRotatedFiles = %(log-count)s
|
-
|
-application = service.Application('buildmaster')
|
-try:
|
- from twisted.python.logfile import LogFile
|
- from twisted.python.log import ILogObserver, FileLogObserver
|
- logfile = LogFile.fromFullPath("twistd.log", rotateLength=rotateLength,
|
- maxRotatedFiles=maxRotatedFiles)
|
- application.setComponent(ILogObserver, FileLogObserver(logfile).emit)
|
-except ImportError:
|
- # probably not yet twisted 8.2.0 and beyond, can't set log yet
|
- pass
|
-BuildMaster(basedir, configfile).setServiceParent(application)
|
-
|
-"""
|
-
|
-def createMaster(config):
|
- m = Maker(config)
|
- m.mkdir()
|
- m.chdir()
|
- contents = masterTAC % config
|
- m.makeTAC(contents)
|
- m.sampleconfig(util.sibpath(__file__, "sample.cfg"))
|
- m.public_html({
|
- 'index.html' : util.sibpath(__file__, "../status/web/index.html"),
|
- 'bg_gradient.jpg' : util.sibpath(__file__, "../status/web/bg_gradient.jpg"),
|
- 'buildbot.css' : util.sibpath(__file__, "../status/web/default.css"),
|
- 'robots.txt' : util.sibpath(__file__, "../status/web/robots.txt"),
|
- })
|
- m.makefile()
|
-
|
- if not m.quiet: print "buildmaster configured in %s" % m.basedir
|
-
|
-class SlaveOptions(MakerBase):
|
- optFlags = [
|
- ["force", "f", "Re-use an existing directory"],
|
- ]
|
- optParameters = [
|
-# ["name", "n", None, "Name for this build slave"],
|
-# ["passwd", "p", None, "Password for this build slave"],
|
-# ["basedir", "d", ".", "Base directory to use"],
|
-# ["master", "m", "localhost:8007",
|
-# "Location of the buildmaster (host:port)"],
|
-
|
- ["keepalive", "k", 600,
|
- "Interval at which keepalives should be sent (in seconds)"],
|
- ["usepty", None, 0,
|
- "(1 or 0) child processes should be run in a pty (default 0)"],
|
- ["umask", None, "None",
|
- "controls permissions of generated files. Use --umask=022 to be world-readable"],
|
- ["maxdelay", None, 300,
|
- "Maximum time between connection attempts"],
|
- ["log-size", "s", "1000000",
|
- "size at which to rotate twisted log files"],
|
- ["log-count", "l", "None",
|
- "limit the number of kept old twisted log files"],
|
- ]
|
-
|
- longdesc = """
|
- This command creates a buildslave working directory and buildbot.tac
|
- file. The bot will use the <name> and <passwd> arguments to authenticate
|
- itself when connecting to the master. All commands are run in a
|
- build-specific subdirectory of <basedir>. <master> is a string of the
|
- form 'hostname:port', and specifies where the buildmaster can be reached.
|
-
|
- <name>, <passwd>, and <master> will be provided by the buildmaster
|
- administrator for your bot. You must choose <basedir> yourself.
|
- """
|
-
|
- def getSynopsis(self):
|
- return "Usage: buildbot create-slave [options] <basedir> <master> <name> <passwd>"
|
-
|
- def parseArgs(self, *args):
|
- if len(args) < 4:
|
- raise usage.UsageError("command needs more arguments")
|
- basedir, master, name, passwd = args
|
- if master[:5] == "http:":
|
- raise usage.UsageError("<master> is not a URL - do not use URL")
|
- self['basedir'] = basedir
|
- self['master'] = master
|
- self['name'] = name
|
- self['passwd'] = passwd
|
-
|
- def postOptions(self):
|
- MakerBase.postOptions(self)
|
- self['usepty'] = int(self['usepty'])
|
- self['keepalive'] = int(self['keepalive'])
|
- self['maxdelay'] = int(self['maxdelay'])
|
- if self['master'].find(":") == -1:
|
- raise usage.UsageError("--master must be in the form host:portnum")
|
- if not re.match('^\d+$', self['log-size']):
|
- raise usage.UsageError("log-size parameter needs to be an int")
|
- if not re.match('^\d+$', self['log-count']) and \
|
- self['log-count'] != 'None':
|
- raise usage.UsageError("log-count parameter needs to be an int "+
|
- " or None")
|
-
|
-slaveTAC = """
|
-from twisted.application import service
|
-from buildbot.slave.bot import BuildSlave
|
-
|
-basedir = r'%(basedir)s'
|
-buildmaster_host = '%(host)s'
|
-port = %(port)d
|
-slavename = '%(name)s'
|
-passwd = '%(passwd)s'
|
-keepalive = %(keepalive)d
|
-usepty = %(usepty)d
|
-umask = %(umask)s
|
-maxdelay = %(maxdelay)d
|
-rotateLength = %(log-size)s
|
-maxRotatedFiles = %(log-count)s
|
-
|
-application = service.Application('buildslave')
|
-try:
|
- from twisted.python.logfile import LogFile
|
- from twisted.python.log import ILogObserver, FileLogObserver
|
- logfile = LogFile.fromFullPath("twistd.log", rotateLength=rotateLength,
|
- maxRotatedFiles=maxRotatedFiles)
|
- application.setComponent(ILogObserver, FileLogObserver(logfile).emit)
|
-except ImportError:
|
- # probably not yet twisted 8.2.0 and beyond, can't set log yet
|
- pass
|
-s = BuildSlave(buildmaster_host, port, slavename, passwd, basedir,
|
- keepalive, usepty, umask=umask, maxdelay=maxdelay)
|
-s.setServiceParent(application)
|
-
|
-"""
|
-
|
-def createSlave(config):
|
- m = Maker(config)
|
- m.mkdir()
|
- m.chdir()
|
- try:
|
- master = config['master']
|
- host, port = re.search(r'(.+):(\d+)', master).groups()
|
- config['host'] = host
|
- config['port'] = int(port)
|
- except:
|
- print "unparseable master location '%s'" % master
|
- print " expecting something more like localhost:8007"
|
- raise
|
- contents = slaveTAC % config
|
-
|
- m.makeTAC(contents, secret=True)
|
-
|
- m.makefile()
|
- m.mkinfo()
|
-
|
- if not m.quiet: print "buildslave configured in %s" % m.basedir
|
-
|
-
|
-
|
-def stop(config, signame="TERM", wait=False):
|
- import signal
|
- basedir = config['basedir']
|
- quiet = config['quiet']
|
- os.chdir(basedir)
|
- try:
|
- f = open("twistd.pid", "rt")
|
- except:
|
- raise BuildbotNotRunningError
|
- pid = int(f.read().strip())
|
- signum = getattr(signal, "SIG"+signame)
|
- timer = 0
|
- try:
|
- os.kill(pid, signum)
|
- except OSError, e:
|
- if e.errno != 3:
|
- raise
|
-
|
- if not wait:
|
- if not quiet:
|
- print "sent SIG%s to process" % signame
|
- return
|
- time.sleep(0.1)
|
- while timer < 10:
|
- # poll once per second until twistd.pid goes away, up to 10 seconds
|
- try:
|
- os.kill(pid, 0)
|
- except OSError:
|
- if not quiet:
|
- print "buildbot process %d is dead" % pid
|
- return
|
- timer += 1
|
- time.sleep(1)
|
- if not quiet:
|
- print "never saw process go away"
|
-
|
-def restart(config):
|
- quiet = config['quiet']
|
- from buildbot.scripts.startup import start
|
- try:
|
- stop(config, wait=True)
|
- except BuildbotNotRunningError:
|
- pass
|
- if not quiet:
|
- print "now restarting buildbot process.."
|
- start(config)
|
-
|
-
|
-class StartOptions(MakerBase):
|
- optFlags = [
|
- ['quiet', 'q', "Don't display startup log messages"],
|
- ]
|
- def getSynopsis(self):
|
- return "Usage: buildbot start [<basedir>]"
|
-
|
-class StopOptions(MakerBase):
|
- def getSynopsis(self):
|
- return "Usage: buildbot stop [<basedir>]"
|
-
|
-class ReconfigOptions(MakerBase):
|
- optFlags = [
|
- ['quiet', 'q', "Don't display log messages about reconfiguration"],
|
- ]
|
- def getSynopsis(self):
|
- return "Usage: buildbot reconfig [<basedir>]"
|
-
|
-
|
-
|
-class RestartOptions(MakerBase):
|
- optFlags = [
|
- ['quiet', 'q', "Don't display startup log messages"],
|
- ]
|
- def getSynopsis(self):
|
- return "Usage: buildbot restart [<basedir>]"
|
-
|
-class DebugClientOptions(OptionsWithOptionsFile):
|
- optFlags = [
|
- ['help', 'h', "Display this message"],
|
- ]
|
- optParameters = [
|
- ["master", "m", None,
|
- "Location of the buildmaster's slaveport (host:port)"],
|
- ["passwd", "p", None, "Debug password to use"],
|
- ["myoption", "O", "DEF", "My Option!"],
|
- ]
|
- buildbotOptions = [
|
- [ 'debugMaster', 'passwd' ],
|
- [ 'master', 'master' ],
|
- ]
|
-
|
- def parseArgs(self, *args):
|
- if len(args) > 0:
|
- self['master'] = args[0]
|
- if len(args) > 1:
|
- self['passwd'] = args[1]
|
- if len(args) > 2:
|
- raise usage.UsageError("I wasn't expecting so many arguments")
|
-
|
- def postOptions(self):
|
- print self['myoption']
|
- sys.exit(1)
|
-
|
-def debugclient(config):
|
- from buildbot.clients import debug
|
-
|
- master = config.get('master')
|
- if master is None:
|
- raise usage.UsageError("master must be specified: on the command "
|
- "line or in ~/.buildbot/options")
|
-
|
- passwd = config.get('passwd')
|
- if passwd is None:
|
- raise usage.UsageError("passwd must be specified: on the command "
|
- "line or in ~/.buildbot/options")
|
-
|
- d = debug.DebugWidget(master, passwd)
|
- d.run()
|
-
|
-class StatusClientOptions(OptionsWithOptionsFile):
|
- optFlags = [
|
- ['help', 'h', "Display this message"],
|
- ]
|
- optParameters = [
|
- ["master", "m", None,
|
- "Location of the buildmaster's status port (host:port)"],
|
- ]
|
- buildbotOptions = [
|
- [ 'masterstatus', 'master' ],
|
- ]
|
-
|
- def parseArgs(self, *args):
|
- if len(args) > 0:
|
- self['master'] = args[0]
|
- if len(args) > 1:
|
- raise usage.UsageError("I wasn't expecting so many arguments")
|
-
|
-def statuslog(config):
|
- from buildbot.clients import base
|
- master = config.get('master')
|
- if master is None:
|
- raise usage.UsageError("master must be specified: on the command "
|
- "line or in ~/.buildbot/options")
|
- c = base.TextClient(master)
|
- c.run()
|
-
|
-def statusgui(config):
|
- from buildbot.clients import gtkPanes
|
- master = config.get('master')
|
- if master is None:
|
- raise usage.UsageError("master must be specified: on the command "
|
- "line or in ~/.buildbot/options")
|
- c = gtkPanes.GtkClient(master)
|
- c.run()
|
-
|
-class SendChangeOptions(OptionsWithOptionsFile):
|
- def __init__(self):
|
- OptionsWithOptionsFile.__init__(self)
|
- self['properties'] = {}
|
-
|
- optParameters = [
|
- ("master", "m", None,
|
- "Location of the buildmaster's PBListener (host:port)"),
|
- ("username", "u", None, "Username performing the commit"),
|
- ("branch", "b", None, "Branch specifier"),
|
- ("category", "c", None, "Category of repository"),
|
- ("revision", "r", None, "Revision specifier (string)"),
|
- ("revision_number", "n", None, "Revision specifier (integer)"),
|
- ("revision_file", None, None, "Filename containing revision spec"),
|
- ("property", "p", None,
|
- "A property for the change, in the format: name:value"),
|
- ("comments", "m", None, "log message"),
|
- ("logfile", "F", None,
|
- "Read the log messages from this file (- for stdin)"),
|
- ("when", "w", None, "timestamp to use as the change time"),
|
- ]
|
-
|
- buildbotOptions = [
|
- [ 'master', 'master' ],
|
- [ 'username', 'username' ],
|
- [ 'branch', 'branch' ],
|
- [ 'category', 'category' ],
|
- ]
|
-
|
- def getSynopsis(self):
|
- return "Usage: buildbot sendchange [options] filenames.."
|
- def parseArgs(self, *args):
|
- self['files'] = args
|
- def opt_property(self, property):
|
- name,value = property.split(':')
|
- self['properties'][name] = value
|
-
|
-
|
-def sendchange(config, runReactor=False):
|
- """Send a single change to the buildmaster's PBChangeSource. The
|
- connection will be drpoped as soon as the Change has been sent."""
|
- from buildbot.clients.sendchange import Sender
|
-
|
- user = config.get('username')
|
- master = config.get('master')
|
- branch = config.get('branch')
|
- category = config.get('category')
|
- revision = config.get('revision')
|
- properties = config.get('properties', {})
|
- if config.get('when'):
|
- when = float(config.get('when'))
|
- else:
|
- when = None
|
- # SVN and P4 use numeric revisions
|
- if config.get("revision_number"):
|
- revision = int(config['revision_number'])
|
- if config.get("revision_file"):
|
- revision = open(config["revision_file"],"r").read()
|
-
|
- comments = config.get('comments')
|
- if not comments and config.get('logfile'):
|
- if config['logfile'] == "-":
|
- f = sys.stdin
|
- else:
|
- f = open(config['logfile'], "rt")
|
- comments = f.read()
|
- if comments is None:
|
- comments = ""
|
-
|
- files = config.get('files', [])
|
-
|
- assert user, "you must provide a username"
|
- assert master, "you must provide the master location"
|
-
|
- s = Sender(master, user)
|
- d = s.send(branch, revision, comments, files, category=category, when=when,
|
- properties=properties)
|
- if runReactor:
|
- d.addCallbacks(s.printSuccess, s.printFailure)
|
- d.addBoth(s.stop)
|
- s.run()
|
- return d
|
-
|
-
|
-class ForceOptions(OptionsWithOptionsFile):
|
- optParameters = [
|
- ["builder", None, None, "which Builder to start"],
|
- ["branch", None, None, "which branch to build"],
|
- ["revision", None, None, "which revision to build"],
|
- ["reason", None, None, "the reason for starting the build"],
|
- ]
|
-
|
- def parseArgs(self, *args):
|
- args = list(args)
|
- if len(args) > 0:
|
- if self['builder'] is not None:
|
- raise usage.UsageError("--builder provided in two ways")
|
- self['builder'] = args.pop(0)
|
- if len(args) > 0:
|
- if self['reason'] is not None:
|
- raise usage.UsageError("--reason provided in two ways")
|
- self['reason'] = " ".join(args)
|
-
|
-
|
-class TryOptions(OptionsWithOptionsFile):
|
- optParameters = [
|
- ["connect", "c", None,
|
- "how to reach the buildmaster, either 'ssh' or 'pb'"],
|
- # for ssh, use --tryhost, --username, and --trydir
|
- ["tryhost", None, None,
|
- "the hostname (used by ssh) for the buildmaster"],
|
- ["trydir", None, None,
|
- "the directory (on the tryhost) where tryjobs are deposited"],
|
- ["username", "u", None, "Username performing the trial build"],
|
- # for PB, use --master, --username, and --passwd
|
- ["master", "m", None,
|
- "Location of the buildmaster's PBListener (host:port)"],
|
- ["passwd", None, None, "password for PB authentication"],
|
-
|
- ["diff", None, None,
|
- "Filename of a patch to use instead of scanning a local tree. Use '-' for stdin."],
|
- ["patchlevel", "p", 0,
|
- "Number of slashes to remove from patch pathnames, like the -p option to 'patch'"],
|
-
|
- ["baserev", None, None,
|
- "Base revision to use instead of scanning a local tree."],
|
-
|
- ["vc", None, None,
|
- "The VC system in use, one of: cvs,svn,tla,baz,darcs"],
|
- ["branch", None, None,
|
- "The branch in use, for VC systems that can't figure it out"
|
- " themselves"],
|
-
|
- ["builder", "b", None,
|
- "Run the trial build on this Builder. Can be used multiple times."],
|
- ["properties", None, None,
|
- "A set of properties made available in the build environment, format:prop=value,propb=valueb..."],
|
-
|
- ["try-topfile", None, None,
|
- "Name of a file at the top of the tree, used to find the top. Only needed for SVN and CVS."],
|
- ["try-topdir", None, None,
|
- "Path to the top of the working copy. Only needed for SVN and CVS."],
|
-
|
- ]
|
-
|
- optFlags = [
|
- ["wait", None, "wait until the builds have finished"],
|
- ["dryrun", 'n', "Gather info, but don't actually submit."],
|
- ]
|
-
|
- # here it is, the definitive, quirky mapping of .buildbot/options names to
|
- # command-line options. Design by committee, anyone?
|
- buildbotOptions = [
|
- [ 'try_connect', 'connect' ],
|
- #[ 'try_builders', 'builders' ], <-- handled in postOptions
|
- [ 'try_vc', 'vc' ],
|
- [ 'try_branch', 'branch' ],
|
- [ 'try_topdir', 'try-topdir' ],
|
- [ 'try_topfile', 'try-topfile' ],
|
- [ 'try_host', 'tryhost' ],
|
- [ 'try_username', 'username' ],
|
- [ 'try_dir', 'trydir' ],
|
- [ 'try_password', 'passwd' ],
|
- [ 'try_master', 'master' ],
|
- #[ 'try_wait', 'wait' ], <-- handled in postOptions
|
- [ 'masterstatus', 'master' ],
|
- ]
|
-
|
- def __init__(self):
|
- OptionsWithOptionsFile.__init__(self)
|
- self['builders'] = []
|
- self['properties'] = {}
|
-
|
- def opt_builder(self, option):
|
- self['builders'].append(option)
|
-
|
- def opt_properties(self, option):
|
- # We need to split the value of this option into a dictionary of properties
|
- properties = {}
|
- propertylist = option.split(",")
|
- for i in range(0,len(propertylist)):
|
- print propertylist[i]
|
- splitproperty = propertylist[i].split("=")
|
- properties[splitproperty[0]] = splitproperty[1]
|
- self['properties'] = properties
|
-
|
- def opt_patchlevel(self, option):
|
- self['patchlevel'] = int(option)
|
-
|
- def getSynopsis(self):
|
- return "Usage: buildbot try [options]"
|
-
|
- def postOptions(self):
|
- opts = loadOptionsFile()
|
- if not self['builders']:
|
- self['builders'] = opts.get('try_builders', [])
|
- if opts.get('try_wait', False):
|
- self['wait'] = True
|
-
|
-def doTry(config):
|
- from buildbot.scripts import tryclient
|
- t = tryclient.Try(config)
|
- t.run()
|
-
|
-class TryServerOptions(OptionsWithOptionsFile):
|
- optParameters = [
|
- ["jobdir", None, None, "the jobdir (maildir) for submitting jobs"],
|
- ]
|
-
|
-def doTryServer(config):
|
- import md5
|
- jobdir = os.path.expanduser(config["jobdir"])
|
- job = sys.stdin.read()
|
- # now do a 'safecat'-style write to jobdir/tmp, then move atomically to
|
- # jobdir/new . Rather than come up with a unique name randomly, I'm just
|
- # going to MD5 the contents and prepend a timestamp.
|
- timestring = "%d" % time.time()
|
- jobhash = md5.new(job).hexdigest()
|
- fn = "%s-%s" % (timestring, jobhash)
|
- tmpfile = os.path.join(jobdir, "tmp", fn)
|
- newfile = os.path.join(jobdir, "new", fn)
|
- f = open(tmpfile, "w")
|
- f.write(job)
|
- f.close()
|
- os.rename(tmpfile, newfile)
|
-
|
-
|
-class CheckConfigOptions(OptionsWithOptionsFile):
|
- optFlags = [
|
- ['quiet', 'q', "Don't display error messages or tracebacks"],
|
- ]
|
-
|
- def getSynopsis(self):
|
- return "Usage :buildbot checkconfig [configFile]\n" + \
|
- " If not specified, 'master.cfg' will be used as 'configFile'"
|
-
|
- def parseArgs(self, *args):
|
- if len(args) >= 1:
|
- self['configFile'] = args[0]
|
- else:
|
- self['configFile'] = 'master.cfg'
|
-
|
-
|
-def doCheckConfig(config):
|
- quiet = config.get('quiet')
|
- configFileName = config.get('configFile')
|
- try:
|
- from buildbot.scripts.checkconfig import ConfigLoader
|
- if os.path.isdir(configFileName):
|
- ConfigLoader(basedir=configFileName)
|
- else:
|
- ConfigLoader(configFileName=configFileName)
|
- except:
|
- if not quiet:
|
- # Print out the traceback in a nice format
|
- t, v, tb = sys.exc_info()
|
- traceback.print_exception(t, v, tb)
|
- sys.exit(1)
|
-
|
- if not quiet:
|
- print "Config file is good!"
|
-
|
-
|
-class Options(usage.Options):
|
- synopsis = "Usage: buildbot <command> [command options]"
|
-
|
- subCommands = [
|
- # the following are all admin commands
|
- ['create-master', None, MasterOptions,
|
- "Create and populate a directory for a new buildmaster"],
|
- ['upgrade-master', None, UpgradeMasterOptions,
|
- "Upgrade an existing buildmaster directory for the current version"],
|
- ['create-slave', None, SlaveOptions,
|
- "Create and populate a directory for a new buildslave"],
|
- ['start', None, StartOptions, "Start a buildmaster or buildslave"],
|
- ['stop', None, StopOptions, "Stop a buildmaster or buildslave"],
|
- ['restart', None, RestartOptions,
|
- "Restart a buildmaster or buildslave"],
|
-
|
- ['reconfig', None, ReconfigOptions,
|
- "SIGHUP a buildmaster to make it re-read the config file"],
|
- ['sighup', None, ReconfigOptions,
|
- "SIGHUP a buildmaster to make it re-read the config file"],
|
-
|
- ['sendchange', None, SendChangeOptions,
|
- "Send a change to the buildmaster"],
|
-
|
- ['debugclient', None, DebugClientOptions,
|
- "Launch a small debug panel GUI"],
|
-
|
- ['statuslog', None, StatusClientOptions,
|
- "Emit current builder status to stdout"],
|
- ['statusgui', None, StatusClientOptions,
|
- "Display a small window showing current builder status"],
|
-
|
- #['force', None, ForceOptions, "Run a build"],
|
- ['try', None, TryOptions, "Run a build with your local changes"],
|
-
|
- ['tryserver', None, TryServerOptions,
|
- "buildmaster-side 'try' support function, not for users"],
|
-
|
- ['checkconfig', None, CheckConfigOptions,
|
- "test the validity of a master.cfg config file"],
|
-
|
- # TODO: 'watch'
|
- ]
|
-
|
- def opt_version(self):
|
- import buildbot
|
- print "Buildbot version: %s" % buildbot.version
|
- usage.Options.opt_version(self)
|
-
|
- def opt_verbose(self):
|
- from twisted.python import log
|
- log.startLogging(sys.stderr)
|
-
|
- def postOptions(self):
|
- if not hasattr(self, 'subOptions'):
|
- raise usage.UsageError("must specify a command")
|
-
|
-
|
-def run():
|
- config = Options()
|
- try:
|
- config.parseOptions()
|
- except usage.error, e:
|
- print "%s: %s" % (sys.argv[0], e)
|
- print
|
- c = getattr(config, 'subOptions', config)
|
- print str(c)
|
- sys.exit(1)
|
-
|
- command = config.subCommand
|
- so = config.subOptions
|
-
|
- if command == "create-master":
|
- createMaster(so)
|
- elif command == "upgrade-master":
|
- upgradeMaster(so)
|
- elif command == "create-slave":
|
- createSlave(so)
|
- elif command == "start":
|
- from buildbot.scripts.startup import start
|
- start(so)
|
- elif command == "stop":
|
- stop(so, wait=True)
|
- elif command == "restart":
|
- restart(so)
|
- elif command == "reconfig" or command == "sighup":
|
- from buildbot.scripts.reconfig import Reconfigurator
|
- Reconfigurator().run(so)
|
- elif command == "sendchange":
|
- sendchange(so, True)
|
- elif command == "debugclient":
|
- debugclient(so)
|
- elif command == "statuslog":
|
- statuslog(so)
|
- elif command == "statusgui":
|
- statusgui(so)
|
- elif command == "try":
|
- doTry(so)
|
- elif command == "tryserver":
|
- doTryServer(so)
|
- elif command == "checkconfig":
|
- doCheckConfig(so)
|
-
|
-
|
|