diff options
Diffstat (limited to 'buildbot/buildbot/scripts/runner.py')
-rw-r--r-- | buildbot/buildbot/scripts/runner.py | 1023 |
1 files changed, 0 insertions, 1023 deletions
diff --git a/buildbot/buildbot/scripts/runner.py b/buildbot/buildbot/scripts/runner.py deleted file mode 100644 index 4e22dbc..0000000 --- a/buildbot/buildbot/scripts/runner.py +++ /dev/null @@ -1,1023 +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 - -# this is mostly just a front-end for mktap, twistd, and kill(1), but in the -# future it will also provide an interface to some developer tools that talk -# directly to a remote buildmaster (like 'try' and a status client) - -# the create/start/stop commands should all be run as the same user, -# preferably a separate 'buildbot' account. - -class MakerBase(usage.Options): - optFlags = [ - ['help', 'h', "Display this message"], - ["quiet", "q", "Do not emit the commands being run"], - ] - - #["basedir", "d", None, "Base directory for the buildmaster"], - opt_h = usage.Options.opt_help - - def parseArgs(self, *args): - if len(args) > 0: - self['basedir'] = args[0] - else: - self['basedir'] = None - if len(args) > 1: - raise usage.UsageError("I wasn't expecting so many arguments") - - def postOptions(self): - if self['basedir'] is None: - raise usage.UsageError("<basedir> parameter is required") - 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 - 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, index_html, buildbot_css, robots_txt): - 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/" - target = os.path.join(webdir, "index.html") - f = open(target, "wt") - f.write(open(index_html, "rt").read()) - f.close() - - target = os.path.join(webdir, "buildbot.css") - f = open(target, "wt") - f.write(open(buildbot_css, "rt").read()) - f.close() - - target = os.path.join(webdir, "robots.txt") - f = open(target, "wt") - f.write(open(robots_txt, "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, index_html, buildbot_css, robots_txt): - 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) - self.populate_if_missing(os.path.join(webdir, "index.html"), - index_html) - self.populate_if_missing(os.path.join(webdir, "buildbot.css"), - buildbot_css) - self.populate_if_missing(os.path.join(webdir, "robots.txt"), - robots_txt) - - 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, classic.css, robots.txt - webdir = os.path.join(basedir, "public_html") - m.upgrade_public_html(util.sibpath(__file__, "../status/web/index.html"), - util.sibpath(__file__, "../status/web/classic.css"), - 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(util.sibpath(__file__, "../status/web/index.html"), - util.sibpath(__file__, "../status/web/classic.css"), - 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 - 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 - os.kill(pid, signum) - 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) - - -def loadOptions(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 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(usage.Options): - 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"], - ] - - 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 debugclient(config): - from buildbot.clients import debug - opts = loadOptions() - - master = config.get('master') - if not master: - master = opts.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 not passwd: - passwd = opts.get('debugPassword') - 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(usage.Options): - optFlags = [ - ['help', 'h', "Display this message"], - ] - optParameters = [ - ["master", "m", None, - "Location of the buildmaster's status port (host:port)"], - ] - - 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 - opts = loadOptions() - master = config.get('master') - if not master: - master = opts.get('masterstatus') - 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 - opts = loadOptions() - master = config.get('master') - if not master: - master = opts.get('masterstatus') - 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(usage.Options): - 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"), - ("comments", "m", None, "log message"), - ("logfile", "F", None, - "Read the log messages from this file (- for stdin)"), - ] - def getSynopsis(self): - return "Usage: buildbot sendchange [options] filenames.." - def parseArgs(self, *args): - self['files'] = args - - -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 - - opts = loadOptions() - user = config.get('username', opts.get('username')) - master = config.get('master', opts.get('master')) - branch = config.get('branch', opts.get('branch')) - category = config.get('category', opts.get('category')) - revision = config.get('revision') - # 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) - if runReactor: - d.addCallbacks(s.printSuccess, s.printFailure) - d.addBoth(s.stop) - s.run() - return d - - -class ForceOptions(usage.Options): - 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(usage.Options): - 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..."], - ] - - optFlags = [ - ["wait", None, "wait until the builds have finished"], - ["dryrun", 'n', "Gather info, but don't actually submit."], - ] - - def __init__(self): - super(TryOptions, self).__init__() - 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 doTry(config): - from buildbot.scripts import tryclient - t = tryclient.Try(config) - t.run() - -class TryServerOptions(usage.Options): - 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(usage.Options): - 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') - configFile = config.get('configFile') - try: - from buildbot.scripts.checkconfig import ConfigLoader - ConfigLoader(configFile) - 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) - - |