fix regression of #592438 caused by commit for #592433
[jhbuild.git] / jhbuild / commands / bot.py
blob4ab28c581576ce04f40662dff0dfa3acc9e2e2d6
1 # jhbuild - a build script for GNOME 1.x and 2.x
2 # Copyright (C) 2001-2006 James Henstridge
3 # Copyright (C) 2008 Frederic Peters
5 # bot.py: buildbot control commands
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 2 of the License, or
10 # (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 # Some methods are derived from Buildbot own methods (when it was not possible
23 # to override just some parts of them). Buildbot is also licensed under the
24 # GNU General Public License.
26 import os
27 import signal
28 import sys
29 import urllib
30 from optparse import make_option
31 import socket
32 import __builtin__
33 import csv
34 import logging
36 try:
37 import elementtree.ElementTree as ET
38 except ImportError:
39 import xml.etree.ElementTree as ET
41 import jhbuild.moduleset
42 import jhbuild.frontends
43 from jhbuild.commands import Command, register_command
44 from jhbuild.commands.base import cmd_build
45 from jhbuild.config import addpath
46 from jhbuild.errors import UsageError, FatalError, CommandError
48 try:
49 import buildbot
50 except ImportError:
51 buildbot = None
53 class cmd_bot(Command):
54 doc = N_('Control buildbot')
56 name = 'bot'
57 usage_args = N_('[ options ... ]')
59 def __init__(self):
60 Command.__init__(self, [
61 make_option('--setup',
62 action='store_true', dest='setup', default=False,
63 help=_('setup a buildbot environment')),
64 make_option('--start',
65 action='store_true', dest='start', default=False,
66 help=_('start a buildbot slave server')),
67 make_option('--stop',
68 action='store_true', dest='stop', default=False,
69 help=_('stop a buildbot slave server')),
70 make_option('--start-server',
71 action='store_true', dest='start_server', default=False,
72 help=_('start a buildbot master server')),
73 make_option('--reload-server-config',
74 action='store_true', dest='reload_server_config', default=False,
75 help=_('reload a buildbot master server configuration')),
76 make_option('--stop-server',
77 action='store_true', dest='stop_server', default=False,
78 help=_('stop a buildbot master server')),
79 make_option('--daemon',
80 action='store_true', dest='daemon', default=False,
81 help=_('start as daemon')),
82 make_option('--pidfile', metavar='PIDFILE',
83 action='store', dest='pidfile', default=None,
84 help=_('pid file location')),
85 make_option('--logfile', metavar='LOGFILE',
86 action='store', dest='logfile', default=None,
87 help=_('log file location')),
88 make_option('--slaves-dir', metavar='SLAVESDIR',
89 action='store', dest='slaves_dir', default=None,
90 help=_('directory with slave files (only with --start-server)')),
91 make_option('--buildbot-dir', metavar='BUILDBOTDIR',
92 action='store', dest='buildbot_dir', default=None,
93 help=_('directory with buildbot work files (only with --start-server)')),
94 make_option('--mastercfg', metavar='CFGFILE',
95 action='store', dest='mastercfgfile', default=None,
96 help=_('master cfg file location (only with --start-server)')),
97 make_option('--step',
98 action='store_true', dest='step', default=False,
99 help=_('exec a buildbot step (internal use only)')),
102 def run(self, config, options, args, help=None):
103 if options.setup:
104 return self.setup(config)
106 global buildbot
107 if buildbot is None:
108 import site
109 pythonversion = 'python' + str(sys.version_info[0]) + '.' + str(sys.version_info[1])
110 pythonpath = os.path.join(config.prefix, 'lib', pythonversion, 'site-packages')
111 site.addsitedir(pythonpath)
112 if config.use_lib64:
113 pythonpath = os.path.join(config.prefix, 'lib64', pythonversion, 'site-packages')
114 site.addsitedir(pythonpath)
115 try:
116 import buildbot
117 except ImportError:
118 raise FatalError(_('buildbot and twisted not found, run jhbuild bot --setup'))
120 # make jhbuild config file accessible to buildbot files
121 # (master.cfg , steps.py, etc.)
122 __builtin__.__dict__['jhbuild_config'] = config
124 daemonize = False
125 pidfile = None
126 logfile = None
127 slaves_dir = config.jhbuildbot_slaves_dir
128 mastercfgfile = config.jhbuildbot_mastercfg
129 buildbot_dir = config.jhbuildbot_dir
131 if options.daemon:
132 daemonize = True
133 if options.pidfile:
134 pidfile = options.pidfile
135 if options.logfile:
136 logfile = options.logfile
137 if options.slaves_dir:
138 slaves_dir = options.slaves_dir
139 if options.mastercfgfile:
140 mastercfgfile = options.mastercfgfile
141 if options.buildbot_dir:
142 buildbot_dir = os.path.abspath(options.buildbot_dir)
144 if options.start:
145 return self.start(config, daemonize, pidfile, logfile)
147 if options.step:
148 os.environ['JHBUILDRC'] = config.filename
149 os.environ['LC_ALL'] = 'C'
150 os.environ['LANGUAGE'] = 'C'
151 os.environ['LANG'] = 'C'
152 __builtin__.__dict__['_'] = lambda x: x
153 config.interact = False
154 config.nonetwork = True
155 os.environ['TERM'] = 'dumb'
156 if args[0] in ('update', 'build', 'check', 'clean'):
157 module_set = jhbuild.moduleset.load(config)
158 buildscript = jhbuild.frontends.get_buildscript(config,
159 [module_set.get_module(x, ignore_case=True) for x in args[1:]])
160 phases = None
161 if args[0] == 'update':
162 config.nonetwork = False
163 phases = ['checkout']
164 elif args[0] == 'build':
165 config.alwaysautogen = True
166 # make check will be run in another step
167 config.makecheck = False
168 config.build_targets = ['install']
169 elif args[0] == 'check':
170 config.makecheck = True
171 config.build_targets = ['check']
172 phases = ['check']
173 elif args[0] == 'clean':
174 phases = ['clean']
175 rc = buildscript.build(phases=phases)
176 else:
177 command = args[0]
178 rc = jhbuild.commands.run(command, config, args[1:], help=None)
179 sys.exit(rc)
181 if options.start_server:
182 return self.start_server(config, daemonize, pidfile, logfile,
183 slaves_dir, mastercfgfile, buildbot_dir)
185 if options.stop or options.stop_server:
186 return self.stop(config, pidfile)
188 if options.reload_server_config:
189 return self.reload_server_config(config, pidfile)
191 def setup(self, config):
192 module_set = jhbuild.moduleset.load(config, 'buildbot')
193 module_list = module_set.get_module_list('all', config.skip)
194 build = jhbuild.frontends.get_buildscript(config, module_list)
195 return build.build()
197 def start(self, config, daemonize, pidfile, logfile):
198 from twisted.application import service
199 application = service.Application('buildslave')
200 if ':' in config.jhbuildbot_master:
201 master_host, master_port = config.jhbuildbot_master.split(':')
202 master_port = int(master_port)
203 else:
204 master_host, master_port = config.jhbuildbot_master, 9070
206 slave_name = config.jhbuildbot_slavename or socket.gethostname()
208 keepalive = 600
209 usepty = 0
210 umask = None
211 basedir = os.path.join(config.checkoutroot, 'jhbuildbot')
212 if not os.path.exists(os.path.join(basedir, 'builddir')):
213 os.makedirs(os.path.join(basedir, 'builddir'))
214 os.chdir(basedir)
216 from buildbot.slave.bot import BuildSlave
217 s = BuildSlave(master_host, master_port,
218 slave_name, config.jhbuildbot_password, basedir,
219 keepalive, usepty, umask=umask)
220 s.setServiceParent(application)
223 from twisted.scripts._twistd_unix import UnixApplicationRunner, ServerOptions
225 opts = ['--no_save']
226 if not daemonize:
227 opts.append('--nodaemon')
228 if pidfile:
229 opts.extend(['--pidfile', pidfile])
230 if logfile:
231 opts.extend(['--logfile', logfile])
232 options = ServerOptions()
233 options.parseOptions(opts)
235 class JhBuildbotApplicationRunner(UnixApplicationRunner):
236 application = None
238 def createOrGetApplication(self):
239 return self.application
241 JhBuildbotApplicationRunner.application = application
242 JhBuildbotApplicationRunner(options).run()
244 def start_server(self, config, daemonize, pidfile, logfile, slaves_dir,
245 mastercfgfile, buildbot_dir):
247 from twisted.scripts._twistd_unix import UnixApplicationRunner, ServerOptions
249 opts = ['--no_save']
250 if not daemonize:
251 opts.append('--nodaemon')
252 if pidfile:
253 opts.extend(['--pidfile', pidfile])
254 if pidfile:
255 opts.extend(['--logfile', logfile])
256 options = ServerOptions()
257 options.parseOptions(opts)
259 class JhBuildbotApplicationRunner(UnixApplicationRunner):
260 application = None
262 def createOrGetApplication(self):
263 return self.application
265 from twisted.application import service, strports
266 from buildbot.master import BuildMaster
267 application = service.Application('buildmaster')
268 from buildbot.buildslave import BuildSlave
270 from twisted.python import log
271 from twisted.internet import defer
272 from buildbot import interfaces
273 from buildbot.process.properties import Properties
275 class JhBuildSlave(BuildSlave):
276 contact_name = None
277 contact_email = None
278 url = None
279 distribution = None
280 architecture = None
281 version = None
283 max_builds = 2
284 scheduler = None
286 run_checks = True
287 run_coverage_report = False
288 run_clean_afterwards = False
290 def load_extra_configuration(self, slaves_dir):
291 from twisted.python import log
292 slave_xml_file = os.path.join(slaves_dir, self.slavename + '.xml')
293 if not os.path.exists(slave_xml_file):
294 log.msg(_('No description for slave %s.') % self.slavename)
295 return
296 try:
297 cfg = ET.parse(slave_xml_file)
298 except: # parse error
299 log.msg(_('Failed to parse slave config for %s.') % self.slavename)
300 return
302 for attribute in ('config/max_builds', 'config/missing_timeout',
303 'config/run_checks', 'config/run_coverage_report',
304 'config/run_clean_afterwards',
305 'config/scheduler',
306 'nightly_scheduler/minute',
307 'nightly_scheduler/hour',
308 'nightly_scheduler/dayOfMonth',
309 'nightly_scheduler/month',
310 'nightly_scheduler/dayOfWeek',
311 'info/contact_name', 'info/contact_email',
312 'info/url', 'info/distribution', 'info/architecture',
313 'info/version'):
314 attr_name = attribute.split('/')[-1]
315 try:
316 value = cfg.find(attribute).text
317 except AttributeError:
318 continue
320 if attr_name in ('max_builds', 'missing_timeout'): # int value
321 try:
322 value = int(value)
323 except ValueError:
324 continue
326 if attr_name in ('run_checks', 'run_coverage_report', 'run_clean_afterwards'):
327 value = (value == 'yes')
329 if attr_name in ('minute', 'hour', 'dayOfMonth', 'month', 'dayOfWeek'):
330 try:
331 value = int(value)
332 except ValueError:
333 value = '*'
335 setattr(self, attr_name, value)
337 if self.scheduler == 'nightly':
338 self.nightly_kwargs = {}
339 for attr_name in ('minute', 'hour', 'dayOfMonth', 'month', 'dayOfWeek'):
340 if hasattr(self, attr_name):
341 self.nightly_kwargs[attr_name] = getattr(self, attr_name)
343 class JhBuildMaster(BuildMaster):
344 jhbuild_config = config
345 def loadConfig(self, f):
346 # modified from parent method to get slaves, projects, change
347 # sources, schedulers, builders and web status ouf of
348 # master.cfg [it would have been cleaner if jhbuild didn't
349 # have to copy all that code.]
350 localDict = {'basedir': os.path.expanduser(self.basedir)}
351 try:
352 exec f in localDict
353 except:
354 log.msg("error while parsing config file")
355 raise
357 jhbuild_config.load()
359 try:
360 config = localDict['BuildmasterConfig']
361 except KeyError:
362 log.err("missing config dictionary")
363 log.err("config file must define BuildmasterConfig")
364 raise
366 known_keys = ("bots", "slaves",
367 "sources", "change_source",
368 "schedulers", "builders",
369 "slavePortnum", "debugPassword", "manhole",
370 "status", "projectName", "projectURL", "buildbotURL",
371 "properties"
373 for k in config.keys():
374 if k not in known_keys:
375 log.msg("unknown key '%s' defined in config dictionary" % k)
377 # the 'slaves' list is read from the 'slaves.csv' file in the
378 # current directory (unless instructed different from command line)
379 # it is a CSV file structured like this:
380 # slavename,password
381 config['slaves'] = []
382 slaves_csv_file = os.path.join(slaves_dir, 'slaves.csv')
383 if os.path.exists(slaves_csv_file):
384 for x in csv.reader(file(slaves_csv_file)):
385 if not x or x[0].startswith('#'):
386 continue
387 kw = {}
388 build_slave = JhBuildSlave(x[0], x[1])
389 build_slave.load_extra_configuration(slaves_dir)
390 config['slaves'].append(build_slave)
392 if len(config['slaves']) == 0:
393 log.msg('you must fill slaves.csv with slaves')
395 module_set = jhbuild.moduleset.load(self.jhbuild_config)
396 module_list = module_set.get_module_list(
397 self.jhbuild_config.modules,
398 self.jhbuild_config.skip,
399 include_optional_modules=True)
400 config['projects'] = [x.name for x in module_list \
401 if not x.name.startswith('meta-')]
403 if self.jhbuild_config.jhbuildbot_svn_commits_box:
404 # trigger builds from mails to svn-commit-list
405 # (note Maildir must be correct, or everything will fail)
406 from jhbuild.buildbot.changes import GnomeMaildirSource
407 config['change_source'] = GnomeMaildirSource(
408 self.jhbuild_config.jhbuildbot_svn_commits_box,
409 prefix=None)
410 else:
411 # support injection (use 'buildbot sendchange')
412 from buildbot.changes.pb import PBChangeSource
413 config['change_source'] = PBChangeSource()
415 # Schedulers
416 from jhbuild.buildbot.scheduler import SerialScheduler, NightlySerialScheduler, OnCommitScheduler
417 config['schedulers'] = []
418 for slave in config['slaves']:
419 s = None
420 for project in config['projects']:
421 buildername = str('%s-%s' % (project, slave.slavename))
422 scheduler_kwargs = {}
423 if slave.scheduler == 'nightly':
424 scheduler_class = NightlySerialScheduler
425 scheduler_kwargs = slave.nightly_kwargs
426 else:
427 scheduler_class = SerialScheduler
428 s = scheduler_class(buildername, project, upstream=s,
429 builderNames=[buildername],
430 **scheduler_kwargs)
431 config['schedulers'].append(s)
432 if self.jhbuild_config.jhbuildbot_svn_commits_box:
433 # schedulers that will launch job when receiving
434 # change notifications
435 s2 = OnCommitScheduler('oc-' + buildername,
436 project, builderNames=[buildername])
437 config['schedulers'].append(s2)
439 # Builders
440 from jhbuild.buildbot.factory import JHBuildFactory
441 config['builders'] = []
442 for project in config['projects']:
443 for slave in config['slaves']:
444 f = JHBuildFactory(project, slave)
445 config['builders'].append({
446 'name' : "%s-%s" % (project, slave.slavename),
447 'slavename' : slave.slavename,
448 'builddir' : 'builddir/%s.%s' % (project, slave.slavename),
449 'factory' : f,
450 'category' : project
453 # Status targets
454 if not config.has_key('status'):
455 # let it be possible to define additional status in
456 # master.cfg
457 config['status'] = []
459 from jhbuild.buildbot.status.web import JHBuildWebStatus
460 config['status'].append(
461 JHBuildWebStatus(
462 self.jhbuild_config.moduleset,
463 config['projects'],
464 [x.slavename for x in config['slaves']],
465 http_port=8080, allowForce=True)
468 # remaining of the method is a straight copy from buildbot
469 # ...
470 try:
471 # required
472 schedulers = config['schedulers']
473 builders = config['builders']
474 for k in builders:
475 if k['name'].startswith("_"):
476 errmsg = ("builder names must not start with an "
477 "underscore: " + k['name'])
478 log.err(errmsg)
479 raise ValueError(errmsg)
481 slavePortnum = config['slavePortnum']
482 #slaves = config['slaves']
483 #change_source = config['change_source']
485 # optional
486 debugPassword = config.get('debugPassword')
487 manhole = config.get('manhole')
488 status = config.get('status', [])
489 projectName = config.get('projectName')
490 projectURL = config.get('projectURL')
491 buildbotURL = config.get('buildbotURL')
492 properties = config.get('properties', {})
494 except KeyError, e:
495 log.msg("config dictionary is missing a required parameter")
496 log.msg("leaving old configuration in place")
497 raise
499 #if "bots" in config:
500 # raise KeyError("c['bots'] is no longer accepted")
502 slaves = config.get('slaves', [])
503 if "bots" in config:
504 m = ("c['bots'] is deprecated as of 0.7.6 and will be "
505 "removed by 0.8.0 . Please use c['slaves'] instead.")
506 log.msg(m)
507 warnings.warn(m, DeprecationWarning)
508 for name, passwd in config['bots']:
509 slaves.append(JhBuildSlave(name, passwd))
511 if "bots" not in config and "slaves" not in config:
512 log.msg("config dictionary must have either 'bots' or 'slaves'")
513 log.msg("leaving old configuration in place")
514 raise KeyError("must have either 'bots' or 'slaves'")
516 #if "sources" in config:
517 # raise KeyError("c['sources'] is no longer accepted")
519 change_source = config.get('change_source', [])
520 if isinstance(change_source, (list, tuple)):
521 change_sources = change_source
522 else:
523 change_sources = [change_source]
524 if "sources" in config:
525 m = ("c['sources'] is deprecated as of 0.7.6 and will be "
526 "removed by 0.8.0 . Please use c['change_source'] instead.")
527 log.msg(m)
528 warnings.warn(m, DeprecationWarning)
529 for s in config['sources']:
530 change_sources.append(s)
532 # do some validation first
533 for s in slaves:
534 assert isinstance(s, JhBuildSlave)
535 if s.slavename in ("debug", "change", "status"):
536 raise KeyError, "reserved name '%s' used for a bot" % s.slavename
537 if config.has_key('interlocks'):
538 raise KeyError("c['interlocks'] is no longer accepted")
540 assert isinstance(change_sources, (list, tuple))
541 for s in change_sources:
542 assert interfaces.IChangeSource(s, None)
543 # this assertion catches c['schedulers'] = Scheduler(), since
544 # Schedulers are service.MultiServices and thus iterable.
545 errmsg = "c['schedulers'] must be a list of Scheduler instances"
546 assert isinstance(schedulers, (list, tuple)), errmsg
547 for s in schedulers:
548 assert interfaces.IScheduler(s, None), errmsg
549 assert isinstance(status, (list, tuple))
550 for s in status:
551 assert interfaces.IStatusReceiver(s, None)
553 slavenames = [s.slavename for s in slaves]
554 buildernames = []
555 dirnames = []
556 for b in builders:
557 if type(b) is tuple:
558 raise ValueError("builder %s must be defined with a dict, "
559 "not a tuple" % b[0])
560 if b.has_key('slavename') and b['slavename'] not in slavenames:
561 raise ValueError("builder %s uses undefined slave %s" \
562 % (b['name'], b['slavename']))
563 for n in b.get('slavenames', []):
564 if n not in slavenames:
565 raise ValueError("builder %s uses undefined slave %s" \
566 % (b['name'], n))
567 if b['name'] in buildernames:
568 raise ValueError("duplicate builder name %s"
569 % b['name'])
570 buildernames.append(b['name'])
571 if b['builddir'] in dirnames:
572 raise ValueError("builder %s reuses builddir %s"
573 % (b['name'], b['builddir']))
574 dirnames.append(b['builddir'])
576 unscheduled_buildernames = buildernames[:]
577 schedulernames = []
578 for s in schedulers:
579 for b in s.listBuilderNames():
580 assert b in buildernames, \
581 "%s uses unknown builder %s" % (s, b)
582 if b in unscheduled_buildernames:
583 unscheduled_buildernames.remove(b)
585 if s.name in schedulernames:
586 # TODO: schedulers share a namespace with other Service
587 # children of the BuildMaster node, like status plugins, the
588 # Manhole, the ChangeMaster, and the BotMaster (although most
589 # of these don't have names)
590 msg = ("Schedulers must have unique names, but "
591 "'%s' was a duplicate" % (s.name,))
592 raise ValueError(msg)
593 schedulernames.append(s.name)
595 if unscheduled_buildernames:
596 log.msg("Warning: some Builders have no Schedulers to drive them:"
597 " %s" % (unscheduled_buildernames,))
599 # assert that all locks used by the Builds and their Steps are
600 # uniquely named.
601 locks = {}
602 for b in builders:
603 for l in b.get('locks', []):
604 if locks.has_key(l.name):
605 if locks[l.name] is not l:
606 raise ValueError("Two different locks (%s and %s) "
607 "share the name %s"
608 % (l, locks[l.name], l.name))
609 else:
610 locks[l.name] = l
611 # TODO: this will break with any BuildFactory that doesn't use a
612 # .steps list, but I think the verification step is more
613 # important.
614 for s in b['factory'].steps:
615 for l in s[1].get('locks', []):
616 if locks.has_key(l.name):
617 if locks[l.name] is not l:
618 raise ValueError("Two different locks (%s and %s)"
619 " share the name %s"
620 % (l, locks[l.name], l.name))
621 else:
622 locks[l.name] = l
624 if not isinstance(properties, dict):
625 raise ValueError("c['properties'] must be a dictionary")
627 # slavePortnum supposed to be a strports specification
628 if type(slavePortnum) is int:
629 slavePortnum = "tcp:%d" % slavePortnum
631 # now we're committed to implementing the new configuration, so do
632 # it atomically
633 # TODO: actually, this is spread across a couple of Deferreds, so it
634 # really isn't atomic.
636 d = defer.succeed(None)
638 self.projectName = projectName
639 self.projectURL = projectURL
640 self.buildbotURL = buildbotURL
642 self.properties = Properties()
643 self.properties.update(properties, self.configFileName)
645 # self.slaves: Disconnect any that were attached and removed from the
646 # list. Update self.checker with the new list of passwords, including
647 # debug/change/status.
648 d.addCallback(lambda res: self.loadConfig_Slaves(slaves))
650 # self.debugPassword
651 if debugPassword:
652 self.checker.addUser("debug", debugPassword)
653 self.debugPassword = debugPassword
655 # self.manhole
656 if manhole != self.manhole:
657 # changing
658 if self.manhole:
659 # disownServiceParent may return a Deferred
660 d.addCallback(lambda res: self.manhole.disownServiceParent())
661 def _remove(res):
662 self.manhole = None
663 return res
664 d.addCallback(_remove)
665 if manhole:
666 def _add(res):
667 self.manhole = manhole
668 manhole.setServiceParent(self)
669 d.addCallback(_add)
671 # add/remove self.botmaster.builders to match builders. The
672 # botmaster will handle startup/shutdown issues.
673 d.addCallback(lambda res: self.loadConfig_Builders(builders))
675 d.addCallback(lambda res: self.loadConfig_status(status))
677 # Schedulers are added after Builders in case they start right away
678 d.addCallback(lambda res: self.loadConfig_Schedulers(schedulers))
679 # and Sources go after Schedulers for the same reason
680 d.addCallback(lambda res: self.loadConfig_Sources(change_sources))
682 # self.slavePort
683 if self.slavePortnum != slavePortnum:
684 if self.slavePort:
685 def closeSlavePort(res):
686 d1 = self.slavePort.disownServiceParent()
687 self.slavePort = None
688 return d1
689 d.addCallback(closeSlavePort)
690 if slavePortnum is not None:
691 def openSlavePort(res):
692 self.slavePort = strports.service(slavePortnum,
693 self.slaveFactory)
694 self.slavePort.setServiceParent(self)
695 d.addCallback(openSlavePort)
696 log.msg("BuildMaster listening on port %s" % slavePortnum)
697 self.slavePortnum = slavePortnum
699 log.msg("configuration update started")
700 def _done(res):
701 self.readConfig = True
702 log.msg("configuration update complete")
703 d.addCallback(_done)
704 d.addCallback(lambda res: self.botmaster.maybeStartAllBuilds())
705 return d
707 if buildbot_dir:
708 basedir = buildbot_dir
709 else:
710 if PKGDATADIR:
711 basedir = os.path.join(PKGDATADIR, 'buildbot')
712 else:
713 basedir = os.path.join(SRCDIR, 'buildbot')
714 os.chdir(basedir)
715 if not os.path.exists(os.path.join(basedir, 'builddir')):
716 os.makedirs(os.path.join(basedir, 'builddir'))
717 master_cfg_path = mastercfgfile
719 JhBuildMaster(basedir, master_cfg_path).setServiceParent(application)
721 JhBuildbotApplicationRunner.application = application
722 JhBuildbotApplicationRunner(options).run()
724 def stop(self, config, pidfile):
725 try:
726 pid = int(file(pidfile).read())
727 except:
728 raise FatalError(_('failed to get buildbot PID'))
730 os.kill(pid, signal.SIGTERM)
732 def reload_server_config(self, config, pidfile):
733 try:
734 pid = int(file(pidfile).read())
735 except:
736 raise FatalError(_('failed to get buildbot PID'))
738 os.kill(pid, signal.SIGHUP)
741 register_command(cmd_bot)