1
2
3
4
5
6
7
8
9 import os, sys, stat, re, time
10 import traceback
11 from twisted.python import usage, util, runtime
12
13 from buildbot.interfaces import BuildbotNotRunningError
14
16 buildbot_tac = os.path.join(dir, "buildbot.tac")
17 if not os.path.isfile(buildbot_tac):
18 print "no buildbot.tac"
19 return False
20
21 contents = open(buildbot_tac, "r").read()
22 return "Application('buildmaster')" in contents
23
24
25
26
27
28
29
48
50 """Find the .buildbot/FILENAME file. Crawl from the current directory up
51 towards the root, and also look in ~/.buildbot . The first directory
52 that's owned by the user and has the file we're looking for wins. Windows
53 skips the owned-by-user test.
54
55 @rtype: dict
56 @return: a dictionary of names defined in the options file. If no options
57 file was found, return an empty dict.
58 """
59
60 if here is None:
61 here = os.getcwd()
62 here = os.path.abspath(here)
63
64 if home is None:
65 if runtime.platformType == 'win32':
66
67 from win32com.shell import shellcon, shell
68 appdata = shell.SHGetFolderPath(0, shellcon.CSIDL_APPDATA, 0, 0)
69 home = os.path.join(appdata, "buildbot")
70 else:
71 home = os.path.expanduser("~/.buildbot")
72
73 searchpath = []
74 toomany = 20
75 while True:
76 searchpath.append(os.path.join(here, ".buildbot"))
77 next = os.path.dirname(here)
78 if next == here:
79 break
80 here = next
81 toomany -= 1
82 if toomany == 0:
83 raise ValueError("Hey, I seem to have wandered up into the "
84 "infinite glories of the heavens. Oops.")
85 searchpath.append(home)
86
87 localDict = {}
88
89 for d in searchpath:
90 if os.path.isdir(d):
91 if runtime.platformType != 'win32':
92 if os.stat(d)[stat.ST_UID] != os.getuid():
93 print "skipping %s because you don't own it" % d
94 continue
95 optfile = os.path.join(d, filename)
96 if os.path.exists(optfile):
97 try:
98 f = open(optfile, "r")
99 options = f.read()
100 exec options in localDict
101 except:
102 print "error while reading %s" % optfile
103 raise
104 break
105
106 for k in localDict.keys():
107 if k.startswith("__"):
108 del localDict[k]
109 return localDict
110
112 optFlags = [
113 ['help', 'h', "Display this message"],
114 ["quiet", "q", "Do not emit the commands being run"],
115 ]
116
117 longdesc = """
118 Operates upon the specified <basedir> (or the current directory, if not
119 specified).
120 """
121
122 opt_h = usage.Options.opt_help
123
125 if len(args) > 0:
126 self['basedir'] = args[0]
127 else:
128
129 self['basedir'] = os.getcwd()
130 if len(args) > 1:
131 raise usage.UsageError("I wasn't expecting so many arguments")
132
133 - def postOptions(self):
134 self['basedir'] = os.path.abspath(self['basedir'])
135
136 makefile_sample = """# -*- makefile -*-
137
138 # This is a simple makefile which lives in a buildmaster
139 # directory (next to the buildbot.tac file). It allows you to start/stop the
140 # master by doing 'make start' or 'make stop'.
141
142 # The 'reconfig' target will tell a buildmaster to reload its config file.
143
144 start:
145 twistd --no_save -y buildbot.tac
146
147 stop:
148 if [ -e twistd.pid ]; \\
149 then kill `cat twistd.pid`; \\
150 else echo "Nothing to stop."; \\
151 fi
152
153 reconfig:
154 if [ -e twistd.pid ]; \\
155 then kill -HUP `cat twistd.pid`; \\
156 else echo "Nothing to reconfig."; \\
157 fi
158
159 log:
160 if [ -e twistd.log ]; \\
161 then tail -f twistd.log; \\
162 else echo "Nothing to tail."; \\
163 fi
164 """
165
172
180
182 path = os.path.join(self.basedir, "info")
183 if not os.path.exists(path):
184 if not self.quiet: print "mkdir", path
185 os.mkdir(path)
186 created = False
187 admin = os.path.join(path, "admin")
188 if not os.path.exists(admin):
189 if not self.quiet:
190 print "Creating info/admin, you need to edit it appropriately"
191 f = open(admin, "wt")
192 f.write("Your Name Here <admin@youraddress.invalid>\n")
193 f.close()
194 created = True
195 host = os.path.join(path, "host")
196 if not os.path.exists(host):
197 if not self.quiet:
198 print "Creating info/host, you need to edit it appropriately"
199 f = open(host, "wt")
200 f.write("Please put a description of this build host here\n")
201 f.close()
202 created = True
203 access_uri = os.path.join(path, "access_uri")
204 if not os.path.exists(access_uri):
205 if not self.quiet:
206 print "Not creating info/access_uri - add it if you wish"
207 if created and not self.quiet:
208 print "Please edit the files in %s appropriately." % path
209
213
214 - def makeTAC(self, contents, secret=False):
215 tacfile = "buildbot.tac"
216 if os.path.exists(tacfile):
217 oldcontents = open(tacfile, "rt").read()
218 if oldcontents == contents:
219 if not self.quiet:
220 print "buildbot.tac already exists and is correct"
221 return
222 if not self.quiet:
223 print "not touching existing buildbot.tac"
224 print "creating buildbot.tac.new instead"
225 tacfile = "buildbot.tac.new"
226 f = open(tacfile, "wt")
227 f.write(contents)
228 f.close()
229 if secret:
230 os.chmod(tacfile, 0600)
231
233 target = "Makefile.sample"
234 if os.path.exists(target):
235 oldcontents = open(target, "rt").read()
236 if oldcontents == makefile_sample:
237 if not self.quiet:
238 print "Makefile.sample already exists and is correct"
239 return
240 if not self.quiet:
241 print "replacing Makefile.sample"
242 else:
243 if not self.quiet:
244 print "creating Makefile.sample"
245 f = open(target, "wt")
246 f.write(makefile_sample)
247 f.close()
248
250 target = "master.cfg.sample"
251 config_sample = open(source, "rt").read()
252 if os.path.exists(target):
253 oldcontents = open(target, "rt").read()
254 if oldcontents == config_sample:
255 if not self.quiet:
256 print "master.cfg.sample already exists and is up-to-date"
257 return
258 if not self.quiet:
259 print "replacing master.cfg.sample"
260 else:
261 if not self.quiet:
262 print "creating master.cfg.sample"
263 f = open(target, "wt")
264 f.write(config_sample)
265 f.close()
266 os.chmod(target, 0600)
267
269 webdir = os.path.join(self.basedir, "public_html")
270 if os.path.exists(webdir):
271 if not self.quiet:
272 print "public_html/ already exists: not replacing"
273 return
274 else:
275 os.mkdir(webdir)
276 if not self.quiet:
277 print "populating public_html/"
278 for target, source in files.iteritems():
279 target = os.path.join(webdir, target)
280 f = open(target, "wt")
281 f.write(open(source, "rt").read())
282 f.close()
283
295
297 new_contents = open(source, "rt").read()
298 if os.path.exists(target):
299 old_contents = open(target, "rt").read()
300 if old_contents != new_contents:
301 if overwrite:
302 if not self.quiet:
303 print "%s has old/modified contents" % target
304 print " overwriting it with new contents"
305 open(target, "wt").write(new_contents)
306 else:
307 if not self.quiet:
308 print "%s has old/modified contents" % target
309 print " writing new contents to %s.new" % target
310 open(target + ".new", "wt").write(new_contents)
311
312 else:
313 if not self.quiet:
314 print "populating %s" % target
315 open(target, "wt").write(new_contents)
316
318 if os.path.exists(source):
319 if os.path.exists(dest):
320 print "Notice: %s now overrides %s" % (dest, source)
321 print " as the latter is not used by buildbot anymore."
322 print " Decide which one you want to keep."
323 else:
324 try:
325 print "Notice: Moving %s to %s." % (source, dest)
326 print " You can (and probably want to) remove it if you haven't modified this file."
327 os.renames(source, dest)
328 except Exception, e:
329 print "Error moving %s to %s: %s" % (source, dest, str(e))
330
340
342 from buildbot.master import BuildMaster
343 from twisted.python import log, failure
344
345 master_cfg = os.path.join(self.basedir, "master.cfg")
346 if not os.path.exists(master_cfg):
347 if not self.quiet:
348 print "No master.cfg found"
349 return 1
350
351
352
353
354
355
356
357
358
359
360
361
362
363 if sys.path[0] != self.basedir:
364 sys.path.insert(0, self.basedir)
365
366 m = BuildMaster(self.basedir)
367
368
369
370
371 messages = []
372 log.addObserver(messages.append)
373 try:
374
375
376
377
378 m.loadConfig(open(master_cfg, "r"), check_synchronously_only=True)
379 except:
380 f = failure.Failure()
381 if not self.quiet:
382 print
383 for m in messages:
384 print "".join(m['message'])
385 print f
386 print
387 print "An error was detected in the master.cfg file."
388 print "Please correct the problem and run 'buildbot upgrade-master' again."
389 print
390 return 1
391 return 0
392
393 DB_HELP = """
394 The --db string is evaluated to build the DB object, which specifies
395 which database the buildmaster should use to hold scheduler state and
396 status information. The default (which creates an SQLite database in
397 BASEDIR/state.sqlite) is equivalent to:
398
399 --db='DBSpec("sqlite3", basedir+"/state.sqlite"))'
400 --db='sqlite:///state.sqlite'
401
402 To use a remote MySQL database instead, use something like:
403
404 --db='mysql://bbuser:bbpasswd@dbhost/bbdb'
405 """
406
408 optFlags = [
409 ["replace", "r", "Replace any modified files without confirmation."],
410 ]
411 optParameters = [
412 ["db", None, "sqlite:///state.sqlite",
413 "which DB to use for scheduler/status state. See below for syntax."],
414 ]
415
417 return "Usage: buildbot upgrade-master [options] [<basedir>]"
418
419 longdesc = """
420 This command takes an existing buildmaster working directory and
421 adds/modifies the files there to work with the current version of
422 buildbot. When this command is finished, the buildmaster directory should
423 look much like a brand-new one created by the 'create-master' command.
424
425 Use this after you've upgraded your buildbot installation and before you
426 restart the buildmaster to use the new version.
427
428 If you have modified the files in your working directory, this command
429 will leave them untouched, but will put the new recommended contents in a
430 .new file (for example, if index.html has been modified, this command
431 will create index.html.new). You can then look at the new version and
432 decide how to merge its contents into your modified file.
433 """+DB_HELP+"""
434 When upgrading from a pre-0.8.0 release (which did not use a database),
435 this command will create the given database and migrate data from the old
436 pickle files into it, then move the pickle files out of the way (e.g. to
437 changes.pck.old). To revert to an older release, rename the pickle files
438 back. When you are satisfied with the new version, you can delete the old
439 pickle files.
440 """
441
443 basedir = os.path.expanduser(config['basedir'])
444 m = Maker(config)
445
446
447
448 m.upgrade_public_html({
449 'bg_gradient.jpg' : util.sibpath(__file__, "../status/web/files/bg_gradient.jpg"),
450 'default.css' : util.sibpath(__file__, "../status/web/files/default.css"),
451 'robots.txt' : util.sibpath(__file__, "../status/web/files/robots.txt"),
452 'favicon.ico' : util.sibpath(__file__, "../status/web/files/favicon.ico"),
453 })
454 m.populate_if_missing(os.path.join(basedir, "master.cfg.sample"),
455 util.sibpath(__file__, "sample.cfg"),
456 overwrite=True)
457
458 m.move_if_present(os.path.join(basedir, "public_html/index.html"),
459 os.path.join(basedir, "templates/root.html"))
460
461 from buildbot.db import connector, dbspec
462 spec = dbspec.DBSpec.from_url(config["db"], basedir)
463
464
465
466 from buildbot.db.schema import manager
467 sm = manager.DBSchemaManager(spec, basedir)
468 sm.upgrade()
469
470
471 rc = m.check_master_cfg()
472 if rc:
473 return rc
474 if not config['quiet']: print "upgrade complete"
475 return 0
476
477
479 optFlags = [
480 ["force", "f",
481 "Re-use an existing directory (will not overwrite master.cfg file)"],
482 ["relocatable", "r",
483 "Create a relocatable buildbot.tac"],
484 ["no-logrotate", "n",
485 "Do not permit buildmaster rotate logs by itself"]
486 ]
487 optParameters = [
488 ["config", "c", "master.cfg", "name of the buildmaster config file"],
489 ["log-size", "s", "10000000",
490 "size at which to rotate twisted log files"],
491 ["log-count", "l", "10",
492 "limit the number of kept old twisted log files"],
493 ["db", None, "sqlite:///state.sqlite",
494 "which DB to use for scheduler/status state. See below for syntax."],
495 ]
497 return "Usage: buildbot create-master [options] [<basedir>]"
498
499 longdesc = """
500 This command creates a buildmaster working directory and buildbot.tac file.
501 The master will live in <dir> and create various files there. If
502 --relocatable is given, then the resulting buildbot.tac file will be
503 written such that its containing directory is assumed to be the basedir.
504 This is generally a good idea.
505
506 At runtime, the master will read a configuration file (named
507 'master.cfg' by default) in its basedir. This file should contain python
508 code which eventually defines a dictionary named 'BuildmasterConfig'.
509 The elements of this dictionary are used to configure the Buildmaster.
510 See doc/config.xhtml for details about what can be controlled through
511 this interface.
512 """ + DB_HELP + """
513 The --db string is stored verbatim in the buildbot.tac file, and
514 evaluated as 'buildbot start' time to pass a DBConnector instance into
515 the newly-created BuildMaster object.
516 """
517
518 - def postOptions(self):
519 MakerBase.postOptions(self)
520 if not re.match('^\d+$', self['log-size']):
521 raise usage.UsageError("log-size parameter needs to be an int")
522 if not re.match('^\d+$', self['log-count']) and \
523 self['log-count'] != 'None':
524 raise usage.UsageError("log-count parameter needs to be an int "+
525 " or None")
526
527
528 masterTACTemplate = ["""
529 import os
530
531 from twisted.application import service
532 from buildbot.master import BuildMaster
533
534 basedir = r'%(basedir)s'
535 rotateLength = %(log-size)s
536 maxRotatedFiles = %(log-count)s
537
538 # if this is a relocatable tac file, get the directory containing the TAC
539 if basedir == '.':
540 import os.path
541 basedir = os.path.abspath(os.path.dirname(__file__))
542
543 # note: this line is matched against to check that this is a buildmaster
544 # directory; do not edit it.
545 application = service.Application('buildmaster')
546 """,
547 """
548 try:
549 from twisted.python.logfile import LogFile
550 from twisted.python.log import ILogObserver, FileLogObserver
551 logfile = LogFile.fromFullPath(os.path.join(basedir, "twistd.log"), rotateLength=rotateLength,
552 maxRotatedFiles=maxRotatedFiles)
553 application.setComponent(ILogObserver, FileLogObserver(logfile).emit)
554 except ImportError:
555 # probably not yet twisted 8.2.0 and beyond, can't set log yet
556 pass
557 """,
558 """
559 configfile = r'%(config)s'
560
561 m = BuildMaster(basedir, configfile)
562 m.setServiceParent(application)
563 m.log_rotation.rotateLength = rotateLength
564 m.log_rotation.maxRotatedFiles = maxRotatedFiles
565
566 """]
567
591
592 -def stop(config, signame="TERM", wait=False):
593 import signal
594 basedir = config['basedir']
595 quiet = config['quiet']
596
597 if not isBuildmasterDir(config['basedir']):
598 print "not a buildmaster directory"
599 sys.exit(1)
600
601 os.chdir(basedir)
602 try:
603 f = open("twistd.pid", "rt")
604 except:
605 raise BuildbotNotRunningError
606 pid = int(f.read().strip())
607 signum = getattr(signal, "SIG"+signame)
608 timer = 0
609 try:
610 os.kill(pid, signum)
611 except OSError, e:
612 if e.errno != 3:
613 raise
614
615 if not wait:
616 if not quiet:
617 print "sent SIG%s to process" % signame
618 return
619 time.sleep(0.1)
620 while timer < 10:
621
622 try:
623 os.kill(pid, 0)
624 except OSError:
625 if not quiet:
626 print "buildbot process %d is dead" % pid
627 return
628 timer += 1
629 time.sleep(1)
630 if not quiet:
631 print "never saw process go away"
632
649
650
652 optFlags = [
653 ['quiet', 'q', "Don't display startup log messages"],
654 ]
656 return "Usage: buildbot start [<basedir>]"
657
660 return "Usage: buildbot stop [<basedir>]"
661
663 optFlags = [
664 ['quiet', 'q', "Don't display log messages about reconfiguration"],
665 ]
667 return "Usage: buildbot reconfig [<basedir>]"
668
669
670
672 optFlags = [
673 ['quiet', 'q', "Don't display startup log messages"],
674 ]
676 return "Usage: buildbot restart [<basedir>]"
677
679 optFlags = [
680 ['help', 'h', "Display this message"],
681 ]
682 optParameters = [
683 ["master", "m", None,
684 "Location of the buildmaster's slaveport (host:port)"],
685 ["passwd", "p", None, "Debug password to use"],
686 ]
687 buildbotOptions = [
688 [ 'debugMaster', 'passwd' ],
689 [ 'master', 'master' ],
690 ]
692 return "Usage: buildbot debugclient [options]"
693
695 if len(args) > 0:
696 self['master'] = args[0]
697 if len(args) > 1:
698 self['passwd'] = args[1]
699 if len(args) > 2:
700 raise usage.UsageError("I wasn't expecting so many arguments")
701
717
719 optFlags = [
720 ['help', 'h', "Display this message"],
721 ]
722 optParameters = [
723 ["master", "m", None,
724 "Location of the buildmaster's status port (host:port)"],
725 ["username", "u", "statusClient", "Username performing the trial build"],
726 ["passwd", None, "clientpw", "password for PB authentication"],
727 ]
728 buildbotOptions = [
729 [ 'masterstatus', 'master' ],
730 ]
731
733 if len(args) > 0:
734 self['master'] = args[0]
735 if len(args) > 1:
736 raise usage.UsageError("I wasn't expecting so many arguments")
737
740 return "Usage: buildbot statuslog [options]"
741
744 return "Usage: buildbot statusgui [options]"
745
756
765
770
771 optParameters = [
772 ("master", "m", None,
773 "Location of the buildmaster's PBListener (host:port)"),
774 ("username", "u", None, "Username performing the commit"),
775 ("repository", "R", None, "Repository specifier"),
776 ("project", "P", None, "Project specifier"),
777 ("branch", "b", None, "Branch specifier"),
778 ("category", "C", None, "Category of repository"),
779 ("revision", "r", None, "Revision specifier"),
780 ("revision_file", None, None, "Filename containing revision spec"),
781 ("property", "p", None,
782 "A property for the change, in the format: name:value"),
783 ("comments", "c", None, "log message"),
784 ("logfile", "F", None,
785 "Read the log messages from this file (- for stdin)"),
786 ("when", "w", None, "timestamp to use as the change time"),
787 ("revlink", "l", None, "Revision link (revlink)"),
788 ]
789
790 buildbotOptions = [
791 [ 'master', 'master' ],
792 [ 'username', 'username' ],
793 [ 'branch', 'branch' ],
794 [ 'category', 'category' ],
795 ]
796
798 return "Usage: buildbot sendchange [options] filenames.."
802 name,value = property.split(':')
803 self['properties'][name] = value
804
805
807 """Send a single change to the buildmaster's PBChangeSource. The
808 connection will be drpoped as soon as the Change has been sent."""
809 from buildbot.clients.sendchange import Sender
810
811 user = config.get('username')
812 master = config.get('master')
813 branch = config.get('branch')
814 category = config.get('category')
815 revision = config.get('revision')
816 properties = config.get('properties', {})
817 repository = config.get('repository', '')
818 project = config.get('project', '')
819 revlink = config.get('revlink', '')
820 if config.get('when'):
821 when = float(config.get('when'))
822 else:
823 when = None
824 if config.get("revision_file"):
825 revision = open(config["revision_file"],"r").read()
826
827 comments = config.get('comments')
828 if not comments and config.get('logfile'):
829 if config['logfile'] == "-":
830 f = sys.stdin
831 else:
832 f = open(config['logfile'], "rt")
833 comments = f.read()
834 if comments is None:
835 comments = ""
836
837 files = config.get('files', [])
838
839 assert user, "you must provide a username"
840 assert master, "you must provide the master location"
841
842 s = Sender(master, user)
843 d = s.send(branch, revision, comments, files, category=category, when=when,
844 properties=properties, repository=repository, project=project,
845 revlink=revlink)
846 if runReactor:
847 status = [True]
848 def failed(res):
849 status[0] = False
850 s.printFailure(res)
851 d.addCallbacks(s.printSuccess, failed)
852 d.addBoth(s.stop)
853 s.run()
854 return status[0]
855 return d
856
857
859 optParameters = [
860 ["builder", None, None, "which Builder to start"],
861 ["branch", None, None, "which branch to build"],
862 ["revision", None, None, "which revision to build"],
863 ["reason", None, None, "the reason for starting the build"],
864 ]
865
867 args = list(args)
868 if len(args) > 0:
869 if self['builder'] is not None:
870 raise usage.UsageError("--builder provided in two ways")
871 self['builder'] = args.pop(0)
872 if len(args) > 0:
873 if self['reason'] is not None:
874 raise usage.UsageError("--reason provided in two ways")
875 self['reason'] = " ".join(args)
876
877
879 optParameters = [
880 ["connect", "c", None,
881 "how to reach the buildmaster, either 'ssh' or 'pb'"],
882
883 ["tryhost", None, None,
884 "the hostname (used by ssh) for the buildmaster"],
885 ["trydir", None, None,
886 "the directory (on the tryhost) where tryjobs are deposited"],
887 ["username", "u", None, "Username performing the trial build"],
888
889 ["master", "m", None,
890 "Location of the buildmaster's PBListener (host:port)"],
891 ["passwd", None, None, "password for PB authentication"],
892
893 ["diff", None, None,
894 "Filename of a patch to use instead of scanning a local tree. Use '-' for stdin."],
895 ["patchlevel", "p", 0,
896 "Number of slashes to remove from patch pathnames, like the -p option to 'patch'"],
897
898 ["baserev", None, None,
899 "Base revision to use instead of scanning a local tree."],
900
901 ["vc", None, None,
902 "The VC system in use, one of: cvs,svn,bzr,darcs,p4"],
903 ["branch", None, None,
904 "The branch in use, for VC systems that can't figure it out"
905 " themselves"],
906
907 ["builder", "b", None,
908 "Run the trial build on this Builder. Can be used multiple times."],
909 ["properties", None, None,
910 "A set of properties made available in the build environment, format:prop1=value1,prop2=value2..."],
911
912 ["try-topfile", None, None,
913 "Name of a file at the top of the tree, used to find the top. Only needed for SVN and CVS."],
914 ["try-topdir", None, None,
915 "Path to the top of the working copy. Only needed for SVN and CVS."],
916
917 ]
918
919 optFlags = [
920 ["wait", None, "wait until the builds have finished"],
921 ["dryrun", 'n', "Gather info, but don't actually submit."],
922 ["get-builder-names", None, "Get the names of available builders. Doesn't submit anything. Only supported for 'pb' connections."],
923 ]
924
925
926
927 buildbotOptions = [
928 [ 'try_connect', 'connect' ],
929
930 [ 'try_vc', 'vc' ],
931 [ 'try_branch', 'branch' ],
932 [ 'try_topdir', 'try-topdir' ],
933 [ 'try_topfile', 'try-topfile' ],
934 [ 'try_host', 'tryhost' ],
935 [ 'try_username', 'username' ],
936 [ 'try_dir', 'trydir' ],
937 [ 'try_password', 'passwd' ],
938 [ 'try_master', 'master' ],
939
940 [ 'masterstatus', 'master' ],
941 ]
942
947
949 self['builders'].append(option)
950
952
953 properties = {}
954 propertylist = option.split(",")
955 for i in range(0,len(propertylist)):
956 print propertylist[i]
957 splitproperty = propertylist[i].split("=")
958 properties[splitproperty[0]] = splitproperty[1]
959 self['properties'] = properties
960
962 self['patchlevel'] = int(option)
963
965 return "Usage: buildbot try [options]"
966
967 - def postOptions(self):
968 opts = loadOptionsFile()
969 if not self['builders']:
970 self['builders'] = opts.get('try_builders', [])
971 if opts.get('try_wait', False):
972 self['wait'] = True
973
978
980 optParameters = [
981 ["jobdir", None, None, "the jobdir (maildir) for submitting jobs"],
982 ]
984 return "Usage: buildbot tryserver [options]"
985
986
988 try:
989 from hashlib import md5
990 except ImportError:
991
992 import md5
993 jobdir = os.path.expanduser(config["jobdir"])
994 job = sys.stdin.read()
995
996
997
998 timestring = "%d" % time.time()
999 try:
1000 m = md5()
1001 except TypeError:
1002
1003 m = md5.new()
1004 m.update(job)
1005 jobhash = m.hexdigest()
1006 fn = "%s-%s" % (timestring, jobhash)
1007 tmpfile = os.path.join(jobdir, "tmp", fn)
1008 newfile = os.path.join(jobdir, "new", fn)
1009 f = open(tmpfile, "w")
1010 f.write(job)
1011 f.close()
1012 os.rename(tmpfile, newfile)
1013
1014
1016 optFlags = [
1017 ['quiet', 'q', "Don't display error messages or tracebacks"],
1018 ]
1019
1021 return "Usage: buildbot checkconfig [configFile]\n" + \
1022 " If not specified, 'master.cfg' will be used as 'configFile'"
1023
1025 if len(args) >= 1:
1026 self['configFile'] = args[0]
1027 else:
1028 self['configFile'] = 'master.cfg'
1029
1030
1049
1050
1052 synopsis = "Usage: buildbot <command> [command options]"
1053
1054 subCommands = [
1055
1056 ['create-master', None, MasterOptions,
1057 "Create and populate a directory for a new buildmaster"],
1058 ['upgrade-master', None, UpgradeMasterOptions,
1059 "Upgrade an existing buildmaster directory for the current version"],
1060 ['start', None, StartOptions, "Start a buildmaster"],
1061 ['stop', None, StopOptions, "Stop a buildmaster"],
1062 ['restart', None, RestartOptions,
1063 "Restart a buildmaster"],
1064
1065 ['reconfig', None, ReconfigOptions,
1066 "SIGHUP a buildmaster to make it re-read the config file"],
1067 ['sighup', None, ReconfigOptions,
1068 "SIGHUP a buildmaster to make it re-read the config file"],
1069
1070 ['sendchange', None, SendChangeOptions,
1071 "Send a change to the buildmaster"],
1072
1073 ['debugclient', None, DebugClientOptions,
1074 "Launch a small debug panel GUI"],
1075
1076 ['statuslog', None, StatusLogOptions,
1077 "Emit current builder status to stdout"],
1078 ['statusgui', None, StatusGuiOptions,
1079 "Display a small window showing current builder status"],
1080
1081
1082 ['try', None, TryOptions, "Run a build with your local changes"],
1083
1084 ['tryserver', None, TryServerOptions,
1085 "buildmaster-side 'try' support function, not for users"],
1086
1087 ['checkconfig', None, CheckConfigOptions,
1088 "test the validity of a master.cfg config file"],
1089
1090
1091 ]
1092
1097
1099 from twisted.python import log
1100 log.startLogging(sys.stderr)
1101
1102 - def postOptions(self):
1103 if not hasattr(self, 'subOptions'):
1104 raise usage.UsageError("must specify a command")
1105
1106
1162