1
2
3
4
5 import os, sys, stat, re, time
6 import traceback
7 from twisted.python import usage, util, runtime
8
9 from buildbot.interfaces import BuildbotNotRunningError
10
11
12
13
14
15
16
35
37 """Find the .buildbot/FILENAME file. Crawl from the current directory up
38 towards the root, and also look in ~/.buildbot . The first directory
39 that's owned by the user and has the file we're looking for wins. Windows
40 skips the owned-by-user test.
41
42 @rtype: dict
43 @return: a dictionary of names defined in the options file. If no options
44 file was found, return an empty dict.
45 """
46
47 if here is None:
48 here = os.getcwd()
49 here = os.path.abspath(here)
50
51 if home is None:
52 if runtime.platformType == 'win32':
53 home = os.path.join(os.environ['APPDATA'], "buildbot")
54 else:
55 home = os.path.expanduser("~/.buildbot")
56
57 searchpath = []
58 toomany = 20
59 while True:
60 searchpath.append(os.path.join(here, ".buildbot"))
61 next = os.path.dirname(here)
62 if next == here:
63 break
64 here = next
65 toomany -= 1
66 if toomany == 0:
67 raise ValueError("Hey, I seem to have wandered up into the "
68 "infinite glories of the heavens. Oops.")
69 searchpath.append(home)
70
71 localDict = {}
72
73 for d in searchpath:
74 if os.path.isdir(d):
75 if runtime.platformType != 'win32':
76 if os.stat(d)[stat.ST_UID] != os.getuid():
77 print "skipping %s because you don't own it" % d
78 continue
79 optfile = os.path.join(d, filename)
80 if os.path.exists(optfile):
81 try:
82 f = open(optfile, "r")
83 options = f.read()
84 exec options in localDict
85 except:
86 print "error while reading %s" % optfile
87 raise
88 break
89
90 for k in localDict.keys():
91 if k.startswith("__"):
92 del localDict[k]
93 return localDict
94
96 optFlags = [
97 ['help', 'h', "Display this message"],
98 ["quiet", "q", "Do not emit the commands being run"],
99 ]
100
101 longdesc = """
102 Operates upon the specified <basedir> (or the current directory, if not
103 specified).
104 """
105
106 opt_h = usage.Options.opt_help
107
109 if len(args) > 0:
110 self['basedir'] = args[0]
111 else:
112
113 self['basedir'] = os.getcwd()
114 if len(args) > 1:
115 raise usage.UsageError("I wasn't expecting so many arguments")
116
117 - def postOptions(self):
118 self['basedir'] = os.path.abspath(self['basedir'])
119
120 makefile_sample = """# -*- makefile -*-
121
122 # This is a simple makefile which lives in a buildmaster/buildslave
123 # directory (next to the buildbot.tac file). It allows you to start/stop the
124 # master or slave by doing 'make start' or 'make stop'.
125
126 # The 'reconfig' target will tell a buildmaster to reload its config file.
127
128 start:
129 twistd --no_save -y buildbot.tac
130
131 stop:
132 kill `cat twistd.pid`
133
134 reconfig:
135 kill -HUP `cat twistd.pid`
136
137 log:
138 tail -f twistd.log
139 """
140
147
149 if os.path.exists(self.basedir):
150 if not self.quiet:
151 print "updating existing installation"
152 return
153 if not self.quiet: print "mkdir", self.basedir
154 os.mkdir(self.basedir)
155
157 path = os.path.join(self.basedir, "info")
158 if not os.path.exists(path):
159 if not self.quiet: print "mkdir", path
160 os.mkdir(path)
161 created = False
162 admin = os.path.join(path, "admin")
163 if not os.path.exists(admin):
164 if not self.quiet:
165 print "Creating info/admin, you need to edit it appropriately"
166 f = open(admin, "wt")
167 f.write("Your Name Here <admin@youraddress.invalid>\n")
168 f.close()
169 created = True
170 host = os.path.join(path, "host")
171 if not os.path.exists(host):
172 if not self.quiet:
173 print "Creating info/host, you need to edit it appropriately"
174 f = open(host, "wt")
175 f.write("Please put a description of this build host here\n")
176 f.close()
177 created = True
178 access_uri = os.path.join(path, "access_uri")
179 if not os.path.exists(access_uri):
180 if not self.quiet:
181 print "Not creating info/access_uri - add it if you wish"
182 if created and not self.quiet:
183 print "Please edit the files in %s appropriately." % path
184
188
189 - def makeTAC(self, contents, secret=False):
190 tacfile = "buildbot.tac"
191 if os.path.exists(tacfile):
192 oldcontents = open(tacfile, "rt").read()
193 if oldcontents == contents:
194 if not self.quiet:
195 print "buildbot.tac already exists and is correct"
196 return
197 if not self.quiet:
198 print "not touching existing buildbot.tac"
199 print "creating buildbot.tac.new instead"
200 tacfile = "buildbot.tac.new"
201 f = open(tacfile, "wt")
202 f.write(contents)
203 f.close()
204 if secret:
205 os.chmod(tacfile, 0600)
206
208 target = "Makefile.sample"
209 if os.path.exists(target):
210 oldcontents = open(target, "rt").read()
211 if oldcontents == makefile_sample:
212 if not self.quiet:
213 print "Makefile.sample already exists and is correct"
214 return
215 if not self.quiet:
216 print "replacing Makefile.sample"
217 else:
218 if not self.quiet:
219 print "creating Makefile.sample"
220 f = open(target, "wt")
221 f.write(makefile_sample)
222 f.close()
223
225 target = "master.cfg.sample"
226 config_sample = open(source, "rt").read()
227 if os.path.exists(target):
228 oldcontents = open(target, "rt").read()
229 if oldcontents == config_sample:
230 if not self.quiet:
231 print "master.cfg.sample already exists and is up-to-date"
232 return
233 if not self.quiet:
234 print "replacing master.cfg.sample"
235 else:
236 if not self.quiet:
237 print "creating master.cfg.sample"
238 f = open(target, "wt")
239 f.write(config_sample)
240 f.close()
241 os.chmod(target, 0600)
242
244 webdir = os.path.join(self.basedir, "public_html")
245 if os.path.exists(webdir):
246 if not self.quiet:
247 print "public_html/ already exists: not replacing"
248 return
249 else:
250 os.mkdir(webdir)
251 if not self.quiet:
252 print "populating public_html/"
253 for target, source in files.iteritems():
254 target = os.path.join(webdir, target)
255 f = open(target, "wt")
256 f.write(open(source, "rt").read())
257 f.close()
258
260 new_contents = open(source, "rt").read()
261 if os.path.exists(target):
262 old_contents = open(target, "rt").read()
263 if old_contents != new_contents:
264 if overwrite:
265 if not self.quiet:
266 print "%s has old/modified contents" % target
267 print " overwriting it with new contents"
268 open(target, "wt").write(new_contents)
269 else:
270 if not self.quiet:
271 print "%s has old/modified contents" % target
272 print " writing new contents to %s.new" % target
273 open(target + ".new", "wt").write(new_contents)
274
275 else:
276 if not self.quiet:
277 print "populating %s" % target
278 open(target, "wt").write(new_contents)
279
281 webdir = os.path.join(self.basedir, "public_html")
282 if not os.path.exists(webdir):
283 if not self.quiet:
284 print "populating public_html/"
285 os.mkdir(webdir)
286 for target, source in files.iteritems():
287 self.populate_if_missing(os.path.join(webdir, target),
288 source)
289
291 from buildbot.master import BuildMaster
292 from twisted.python import log, failure
293
294 master_cfg = os.path.join(self.basedir, "master.cfg")
295 if not os.path.exists(master_cfg):
296 if not self.quiet:
297 print "No master.cfg found"
298 return 1
299
300
301
302
303
304
305
306
307
308
309
310
311
312 if sys.path[0] != self.basedir:
313 sys.path.insert(0, self.basedir)
314
315 m = BuildMaster(self.basedir)
316
317
318
319
320 messages = []
321 log.addObserver(messages.append)
322 try:
323
324
325
326
327 m.loadConfig(open(master_cfg, "r"))
328 except:
329 f = failure.Failure()
330 if not self.quiet:
331 print
332 for m in messages:
333 print "".join(m['message'])
334 print f
335 print
336 print "An error was detected in the master.cfg file."
337 print "Please correct the problem and run 'buildbot upgrade-master' again."
338 print
339 return 1
340 return 0
341
343 optFlags = [
344 ["replace", "r", "Replace any modified files without confirmation."],
345 ]
346
348 return "Usage: buildbot upgrade-master [options] [<basedir>]"
349
350 longdesc = """
351 This command takes an existing buildmaster working directory and
352 adds/modifies the files there to work with the current version of
353 buildbot. When this command is finished, the buildmaster directory should
354 look much like a brand-new one created by the 'create-master' command.
355
356 Use this after you've upgraded your buildbot installation and before you
357 restart the buildmaster to use the new version.
358
359 If you have modified the files in your working directory, this command
360 will leave them untouched, but will put the new recommended contents in a
361 .new file (for example, if index.html has been modified, this command
362 will create index.html.new). You can then look at the new version and
363 decide how to merge its contents into your modified file.
364 """
365
367 basedir = config['basedir']
368 m = Maker(config)
369
370
371
372 webdir = os.path.join(basedir, "public_html")
373 m.upgrade_public_html({
374 'index.html' : util.sibpath(__file__, "../status/web/index.html"),
375 'bg_gradient.jpg' : util.sibpath(__file__, "../status/web/bg_gradient.jpg"),
376 'buildbot.css' : util.sibpath(__file__, "../status/web/default.css"),
377 'robots.txt' : util.sibpath(__file__, "../status/web/robots.txt"),
378 })
379 m.populate_if_missing(os.path.join(basedir, "master.cfg.sample"),
380 util.sibpath(__file__, "sample.cfg"),
381 overwrite=True)
382 rc = m.check_master_cfg()
383 if rc:
384 return rc
385 if not config['quiet']:
386 print "upgrade complete"
387
388
390 optFlags = [
391 ["force", "f",
392 "Re-use an existing directory (will not overwrite master.cfg file)"],
393 ]
394 optParameters = [
395 ["config", "c", "master.cfg", "name of the buildmaster config file"],
396 ["log-size", "s", "1000000",
397 "size at which to rotate twisted log files"],
398 ["log-count", "l", "None",
399 "limit the number of kept old twisted log files"],
400 ]
402 return "Usage: buildbot create-master [options] [<basedir>]"
403
404 longdesc = """
405 This command creates a buildmaster working directory and buildbot.tac
406 file. The master will live in <dir> and create various files there.
407
408 At runtime, the master will read a configuration file (named
409 'master.cfg' by default) in its basedir. This file should contain python
410 code which eventually defines a dictionary named 'BuildmasterConfig'.
411 The elements of this dictionary are used to configure the Buildmaster.
412 See doc/config.xhtml for details about what can be controlled through
413 this interface."""
414
415 - def postOptions(self):
416 MakerBase.postOptions(self)
417 if not re.match('^\d+$', self['log-size']):
418 raise usage.UsageError("log-size parameter needs to be an int")
419 if not re.match('^\d+$', self['log-count']) and \
420 self['log-count'] != 'None':
421 raise usage.UsageError("log-count parameter needs to be an int "+
422 " or None")
423
424
425 masterTAC = """
426 from twisted.application import service
427 from buildbot.master import BuildMaster
428
429 basedir = r'%(basedir)s'
430 configfile = r'%(config)s'
431 rotateLength = %(log-size)s
432 maxRotatedFiles = %(log-count)s
433
434 application = service.Application('buildmaster')
435 try:
436 from twisted.python.logfile import LogFile
437 from twisted.python.log import ILogObserver, FileLogObserver
438 logfile = LogFile.fromFullPath("twistd.log", rotateLength=rotateLength,
439 maxRotatedFiles=maxRotatedFiles)
440 application.setComponent(ILogObserver, FileLogObserver(logfile).emit)
441 except ImportError:
442 # probably not yet twisted 8.2.0 and beyond, can't set log yet
443 pass
444 BuildMaster(basedir, configfile).setServiceParent(application)
445
446 """
447
449 m = Maker(config)
450 m.mkdir()
451 m.chdir()
452 contents = masterTAC % config
453 m.makeTAC(contents)
454 m.sampleconfig(util.sibpath(__file__, "sample.cfg"))
455 m.public_html({
456 'index.html' : util.sibpath(__file__, "../status/web/index.html"),
457 'bg_gradient.jpg' : util.sibpath(__file__, "../status/web/bg_gradient.jpg"),
458 'buildbot.css' : util.sibpath(__file__, "../status/web/default.css"),
459 'robots.txt' : util.sibpath(__file__, "../status/web/robots.txt"),
460 })
461 m.makefile()
462
463 if not m.quiet: print "buildmaster configured in %s" % m.basedir
464
466 optFlags = [
467 ["force", "f", "Re-use an existing directory"],
468 ]
469 optParameters = [
470
471
472
473
474
475
476 ["keepalive", "k", 600,
477 "Interval at which keepalives should be sent (in seconds)"],
478 ["usepty", None, 0,
479 "(1 or 0) child processes should be run in a pty (default 0)"],
480 ["umask", None, "None",
481 "controls permissions of generated files. Use --umask=022 to be world-readable"],
482 ["maxdelay", None, 300,
483 "Maximum time between connection attempts"],
484 ["log-size", "s", "1000000",
485 "size at which to rotate twisted log files"],
486 ["log-count", "l", "None",
487 "limit the number of kept old twisted log files"],
488 ]
489
490 longdesc = """
491 This command creates a buildslave working directory and buildbot.tac
492 file. The bot will use the <name> and <passwd> arguments to authenticate
493 itself when connecting to the master. All commands are run in a
494 build-specific subdirectory of <basedir>. <master> is a string of the
495 form 'hostname:port', and specifies where the buildmaster can be reached.
496
497 <name>, <passwd>, and <master> will be provided by the buildmaster
498 administrator for your bot. You must choose <basedir> yourself.
499 """
500
502 return "Usage: buildbot create-slave [options] <basedir> <master> <name> <passwd>"
503
505 if len(args) < 4:
506 raise usage.UsageError("command needs more arguments")
507 basedir, master, name, passwd = args
508 if master[:5] == "http:":
509 raise usage.UsageError("<master> is not a URL - do not use URL")
510 self['basedir'] = basedir
511 self['master'] = master
512 self['name'] = name
513 self['passwd'] = passwd
514
515 - def postOptions(self):
516 MakerBase.postOptions(self)
517 self['usepty'] = int(self['usepty'])
518 self['keepalive'] = int(self['keepalive'])
519 self['maxdelay'] = int(self['maxdelay'])
520 if self['master'].find(":") == -1:
521 raise usage.UsageError("--master must be in the form host:portnum")
522 if not re.match('^\d+$', self['log-size']):
523 raise usage.UsageError("log-size parameter needs to be an int")
524 if not re.match('^\d+$', self['log-count']) and \
525 self['log-count'] != 'None':
526 raise usage.UsageError("log-count parameter needs to be an int "+
527 " or None")
528
529 slaveTAC = """
530 from twisted.application import service
531 from buildbot.slave.bot import BuildSlave
532
533 basedir = r'%(basedir)s'
534 buildmaster_host = '%(host)s'
535 port = %(port)d
536 slavename = '%(name)s'
537 passwd = '%(passwd)s'
538 keepalive = %(keepalive)d
539 usepty = %(usepty)d
540 umask = %(umask)s
541 maxdelay = %(maxdelay)d
542 rotateLength = %(log-size)s
543 maxRotatedFiles = %(log-count)s
544
545 application = service.Application('buildslave')
546 try:
547 from twisted.python.logfile import LogFile
548 from twisted.python.log import ILogObserver, FileLogObserver
549 logfile = LogFile.fromFullPath("twistd.log", rotateLength=rotateLength,
550 maxRotatedFiles=maxRotatedFiles)
551 application.setComponent(ILogObserver, FileLogObserver(logfile).emit)
552 except ImportError:
553 # probably not yet twisted 8.2.0 and beyond, can't set log yet
554 pass
555 s = BuildSlave(buildmaster_host, port, slavename, passwd, basedir,
556 keepalive, usepty, umask=umask, maxdelay=maxdelay)
557 s.setServiceParent(application)
558
559 """
560
582
583
584
585 -def stop(config, signame="TERM", wait=False):
586 import signal
587 basedir = config['basedir']
588 quiet = config['quiet']
589 os.chdir(basedir)
590 try:
591 f = open("twistd.pid", "rt")
592 except:
593 raise BuildbotNotRunningError
594 pid = int(f.read().strip())
595 signum = getattr(signal, "SIG"+signame)
596 timer = 0
597 try:
598 os.kill(pid, signum)
599 except OSError, e:
600 if e.errno != 3:
601 raise
602
603 if not wait:
604 if not quiet:
605 print "sent SIG%s to process" % signame
606 return
607 time.sleep(0.1)
608 while timer < 10:
609
610 try:
611 os.kill(pid, 0)
612 except OSError:
613 if not quiet:
614 print "buildbot process %d is dead" % pid
615 return
616 timer += 1
617 time.sleep(1)
618 if not quiet:
619 print "never saw process go away"
620
631
632
634 optFlags = [
635 ['quiet', 'q', "Don't display startup log messages"],
636 ]
638 return "Usage: buildbot start [<basedir>]"
639
642 return "Usage: buildbot stop [<basedir>]"
643
645 optFlags = [
646 ['quiet', 'q', "Don't display log messages about reconfiguration"],
647 ]
649 return "Usage: buildbot reconfig [<basedir>]"
650
651
652
654 optFlags = [
655 ['quiet', 'q', "Don't display startup log messages"],
656 ]
658 return "Usage: buildbot restart [<basedir>]"
659
661 optFlags = [
662 ['help', 'h', "Display this message"],
663 ]
664 optParameters = [
665 ["master", "m", None,
666 "Location of the buildmaster's slaveport (host:port)"],
667 ["passwd", "p", None, "Debug password to use"],
668 ["myoption", "O", "DEF", "My Option!"],
669 ]
670 buildbotOptions = [
671 [ 'debugMaster', 'passwd' ],
672 [ 'master', 'master' ],
673 ]
674
676 if len(args) > 0:
677 self['master'] = args[0]
678 if len(args) > 1:
679 self['passwd'] = args[1]
680 if len(args) > 2:
681 raise usage.UsageError("I wasn't expecting so many arguments")
682
683 - def postOptions(self):
684 print self['myoption']
685 sys.exit(1)
686
702
704 optFlags = [
705 ['help', 'h', "Display this message"],
706 ]
707 optParameters = [
708 ["master", "m", None,
709 "Location of the buildmaster's status port (host:port)"],
710 ]
711 buildbotOptions = [
712 [ 'masterstatus', 'master' ],
713 ]
714
716 if len(args) > 0:
717 self['master'] = args[0]
718 if len(args) > 1:
719 raise usage.UsageError("I wasn't expecting so many arguments")
720
729
738
743
744 optParameters = [
745 ("master", "m", None,
746 "Location of the buildmaster's PBListener (host:port)"),
747 ("username", "u", None, "Username performing the commit"),
748 ("branch", "b", None, "Branch specifier"),
749 ("category", "c", None, "Category of repository"),
750 ("revision", "r", None, "Revision specifier (string)"),
751 ("revision_number", "n", None, "Revision specifier (integer)"),
752 ("revision_file", None, None, "Filename containing revision spec"),
753 ("property", "p", None,
754 "A property for the change, in the format: name:value"),
755 ("comments", "m", None, "log message"),
756 ("logfile", "F", None,
757 "Read the log messages from this file (- for stdin)"),
758 ("when", "w", None, "timestamp to use as the change time"),
759 ]
760
761 buildbotOptions = [
762 [ 'master', 'master' ],
763 [ 'username', 'username' ],
764 [ 'branch', 'branch' ],
765 [ 'category', 'category' ],
766 ]
767
769 return "Usage: buildbot sendchange [options] filenames.."
773 name,value = property.split(':')
774 self['properties'][name] = value
775
776
778 """Send a single change to the buildmaster's PBChangeSource. The
779 connection will be drpoped as soon as the Change has been sent."""
780 from buildbot.clients.sendchange import Sender
781
782 user = config.get('username')
783 master = config.get('master')
784 branch = config.get('branch')
785 category = config.get('category')
786 revision = config.get('revision')
787 properties = config.get('properties', {})
788 if config.get('when'):
789 when = float(config.get('when'))
790 else:
791 when = None
792
793 if config.get("revision_number"):
794 revision = int(config['revision_number'])
795 if config.get("revision_file"):
796 revision = open(config["revision_file"],"r").read()
797
798 comments = config.get('comments')
799 if not comments and config.get('logfile'):
800 if config['logfile'] == "-":
801 f = sys.stdin
802 else:
803 f = open(config['logfile'], "rt")
804 comments = f.read()
805 if comments is None:
806 comments = ""
807
808 files = config.get('files', [])
809
810 assert user, "you must provide a username"
811 assert master, "you must provide the master location"
812
813 s = Sender(master, user)
814 d = s.send(branch, revision, comments, files, category=category, when=when,
815 properties=properties)
816 if runReactor:
817 d.addCallbacks(s.printSuccess, s.printFailure)
818 d.addBoth(s.stop)
819 s.run()
820 return d
821
822
824 optParameters = [
825 ["builder", None, None, "which Builder to start"],
826 ["branch", None, None, "which branch to build"],
827 ["revision", None, None, "which revision to build"],
828 ["reason", None, None, "the reason for starting the build"],
829 ]
830
832 args = list(args)
833 if len(args) > 0:
834 if self['builder'] is not None:
835 raise usage.UsageError("--builder provided in two ways")
836 self['builder'] = args.pop(0)
837 if len(args) > 0:
838 if self['reason'] is not None:
839 raise usage.UsageError("--reason provided in two ways")
840 self['reason'] = " ".join(args)
841
842
844 optParameters = [
845 ["connect", "c", None,
846 "how to reach the buildmaster, either 'ssh' or 'pb'"],
847
848 ["tryhost", None, None,
849 "the hostname (used by ssh) for the buildmaster"],
850 ["trydir", None, None,
851 "the directory (on the tryhost) where tryjobs are deposited"],
852 ["username", "u", None, "Username performing the trial build"],
853
854 ["master", "m", None,
855 "Location of the buildmaster's PBListener (host:port)"],
856 ["passwd", None, None, "password for PB authentication"],
857
858 ["diff", None, None,
859 "Filename of a patch to use instead of scanning a local tree. Use '-' for stdin."],
860 ["patchlevel", "p", 0,
861 "Number of slashes to remove from patch pathnames, like the -p option to 'patch'"],
862
863 ["baserev", None, None,
864 "Base revision to use instead of scanning a local tree."],
865
866 ["vc", None, None,
867 "The VC system in use, one of: cvs,svn,tla,baz,darcs"],
868 ["branch", None, None,
869 "The branch in use, for VC systems that can't figure it out"
870 " themselves"],
871
872 ["builder", "b", None,
873 "Run the trial build on this Builder. Can be used multiple times."],
874 ["properties", None, None,
875 "A set of properties made available in the build environment, format:prop=value,propb=valueb..."],
876
877 ["try-topfile", None, None,
878 "Name of a file at the top of the tree, used to find the top. Only needed for SVN and CVS."],
879 ["try-topdir", None, None,
880 "Path to the top of the working copy. Only needed for SVN and CVS."],
881
882 ]
883
884 optFlags = [
885 ["wait", None, "wait until the builds have finished"],
886 ["dryrun", 'n', "Gather info, but don't actually submit."],
887 ]
888
889
890
891 buildbotOptions = [
892 [ 'try_connect', 'connect' ],
893
894 [ 'try_vc', 'vc' ],
895 [ 'try_branch', 'branch' ],
896 [ 'try_topdir', 'try-topdir' ],
897 [ 'try_topfile', 'try-topfile' ],
898 [ 'try_host', 'tryhost' ],
899 [ 'try_username', 'username' ],
900 [ 'try_dir', 'trydir' ],
901 [ 'try_password', 'passwd' ],
902 [ 'try_master', 'master' ],
903
904 [ 'masterstatus', 'master' ],
905 ]
906
911
913 self['builders'].append(option)
914
916
917 properties = {}
918 propertylist = option.split(",")
919 for i in range(0,len(propertylist)):
920 print propertylist[i]
921 splitproperty = propertylist[i].split("=")
922 properties[splitproperty[0]] = splitproperty[1]
923 self['properties'] = properties
924
926 self['patchlevel'] = int(option)
927
929 return "Usage: buildbot try [options]"
930
931 - def postOptions(self):
932 opts = loadOptionsFile()
933 if not self['builders']:
934 self['builders'] = opts.get('try_builders', [])
935 if opts.get('try_wait', False):
936 self['wait'] = True
937
942
944 optParameters = [
945 ["jobdir", None, None, "the jobdir (maildir) for submitting jobs"],
946 ]
947
949 import md5
950 jobdir = os.path.expanduser(config["jobdir"])
951 job = sys.stdin.read()
952
953
954
955 timestring = "%d" % time.time()
956 jobhash = md5.new(job).hexdigest()
957 fn = "%s-%s" % (timestring, jobhash)
958 tmpfile = os.path.join(jobdir, "tmp", fn)
959 newfile = os.path.join(jobdir, "new", fn)
960 f = open(tmpfile, "w")
961 f.write(job)
962 f.close()
963 os.rename(tmpfile, newfile)
964
965
967 optFlags = [
968 ['quiet', 'q', "Don't display error messages or tracebacks"],
969 ]
970
972 return "Usage :buildbot checkconfig [configFile]\n" + \
973 " If not specified, 'master.cfg' will be used as 'configFile'"
974
976 if len(args) >= 1:
977 self['configFile'] = args[0]
978 else:
979 self['configFile'] = 'master.cfg'
980
981
1000
1001
1003 synopsis = "Usage: buildbot <command> [command options]"
1004
1005 subCommands = [
1006
1007 ['create-master', None, MasterOptions,
1008 "Create and populate a directory for a new buildmaster"],
1009 ['upgrade-master', None, UpgradeMasterOptions,
1010 "Upgrade an existing buildmaster directory for the current version"],
1011 ['create-slave', None, SlaveOptions,
1012 "Create and populate a directory for a new buildslave"],
1013 ['start', None, StartOptions, "Start a buildmaster or buildslave"],
1014 ['stop', None, StopOptions, "Stop a buildmaster or buildslave"],
1015 ['restart', None, RestartOptions,
1016 "Restart a buildmaster or buildslave"],
1017
1018 ['reconfig', None, ReconfigOptions,
1019 "SIGHUP a buildmaster to make it re-read the config file"],
1020 ['sighup', None, ReconfigOptions,
1021 "SIGHUP a buildmaster to make it re-read the config file"],
1022
1023 ['sendchange', None, SendChangeOptions,
1024 "Send a change to the buildmaster"],
1025
1026 ['debugclient', None, DebugClientOptions,
1027 "Launch a small debug panel GUI"],
1028
1029 ['statuslog', None, StatusClientOptions,
1030 "Emit current builder status to stdout"],
1031 ['statusgui', None, StatusClientOptions,
1032 "Display a small window showing current builder status"],
1033
1034
1035 ['try', None, TryOptions, "Run a build with your local changes"],
1036
1037 ['tryserver', None, TryServerOptions,
1038 "buildmaster-side 'try' support function, not for users"],
1039
1040 ['checkconfig', None, CheckConfigOptions,
1041 "test the validity of a master.cfg config file"],
1042
1043
1044 ]
1045
1050
1052 from twisted.python import log
1053 log.startLogging(sys.stderr)
1054
1055 - def postOptions(self):
1056 if not hasattr(self, 'subOptions'):
1057 raise usage.UsageError("must specify a command")
1058
1059
1104