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