Package buildbot :: Package scripts :: Module runner
[frames] | no frames]

Source Code for Module buildbot.scripts.runner

  1  # This file is part of Buildbot.  Buildbot is free software: you can 
  2  # redistribute it and/or modify it under the terms of the GNU General Public 
  3  # License as published by the Free Software Foundation, version 2. 
  4  # 
  5  # This program is distributed in the hope that it will be useful, but WITHOUT 
  6  # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 
  7  # FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more 
  8  # details. 
  9  # 
 10  # You should have received a copy of the GNU General Public License along with 
 11  # this program; if not, write to the Free Software Foundation, Inc., 51 
 12  # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 
 13  # 
 14  # Copyright Buildbot Team Members 
 15   
 16  from __future__ import with_statement 
 17   
 18  # N.B.: don't import anything that might pull in a reactor yet. Some of our 
 19  # subcommands want to load modules that need the gtk reactor. 
 20  # 
 21  # Also don't forget to mirror your changes on command-line options in manual 
 22  # pages and texinfo documentation. 
 23   
 24  from twisted.python import usage, reflect 
 25  import re 
 26  import sys 
 27   
 28  from buildbot.scripts import base 
 29   
 30  # Note that the terms 'options' and 'config' are used intechangeably here - in 
 31  # fact, they are intercanged several times.  Caveat legator. 
 32   
33 -class UpgradeMasterOptions(base.BasedirMixin, base.SubcommandOptions):
34 subcommandFunction = "buildbot.scripts.upgrade_master.upgradeMaster" 35 optFlags = [ 36 ["quiet", "q", "Do not emit the commands being run"], 37 ["replace", "r", "Replace any modified files without confirmation."], 38 ] 39 optParameters = [ 40 ] 41
42 - def getSynopsis(self):
43 return "Usage: buildbot upgrade-master [options] [<basedir>]"
44 45 longdesc = """ 46 This command takes an existing buildmaster working directory and 47 adds/modifies the files there to work with the current version of 48 buildbot. When this command is finished, the buildmaster directory should 49 look much like a brand-new one created by the 'create-master' command. 50 51 Use this after you've upgraded your buildbot installation and before you 52 restart the buildmaster to use the new version. 53 54 If you have modified the files in your working directory, this command 55 will leave them untouched, but will put the new recommended contents in a 56 .new file (for example, if index.html has been modified, this command 57 will create index.html.new). You can then look at the new version and 58 decide how to merge its contents into your modified file. 59 60 When upgrading from a pre-0.8.0 release (which did not use a database), 61 this command will create the given database and migrate data from the old 62 pickle files into it, then move the pickle files out of the way (e.g. to 63 changes.pck.old). 64 65 When upgrading the database, this command uses the database specified in 66 the master configuration file. If you wish to use a database other than 67 the default (sqlite), be sure to set that parameter before upgrading. 68 """
69 70
71 -class CreateMasterOptions(base.BasedirMixin, base.SubcommandOptions):
72 subcommandFunction = "buildbot.scripts.create_master.createMaster" 73 optFlags = [ 74 ["quiet", "q", "Do not emit the commands being run"], 75 ["force", "f", 76 "Re-use an existing directory (will not overwrite master.cfg file)"], 77 ["relocatable", "r", 78 "Create a relocatable buildbot.tac"], 79 ["no-logrotate", "n", 80 "Do not permit buildmaster rotate logs by itself"] 81 ] 82 optParameters = [ 83 ["config", "c", "master.cfg", "name of the buildmaster config file"], 84 ["log-size", "s", "10000000", 85 "size at which to rotate twisted log files"], 86 ["log-count", "l", "10", 87 "limit the number of kept old twisted log files"], 88 ["db", None, "sqlite:///state.sqlite", 89 "which DB to use for scheduler/status state. See below for syntax."], 90 ]
91 - def getSynopsis(self):
92 return "Usage: buildbot create-master [options] [<basedir>]"
93 94 longdesc = """ 95 This command creates a buildmaster working directory and buildbot.tac file. 96 The master will live in <dir> and create various files there. If 97 --relocatable is given, then the resulting buildbot.tac file will be 98 written such that its containing directory is assumed to be the basedir. 99 This is generally a good idea. 100 101 At runtime, the master will read a configuration file (named 102 'master.cfg' by default) in its basedir. This file should contain python 103 code which eventually defines a dictionary named 'BuildmasterConfig'. 104 The elements of this dictionary are used to configure the Buildmaster. 105 See doc/config.xhtml for details about what can be controlled through 106 this interface. 107 108 The --db string is evaluated to build the DB object, which specifies 109 which database the buildmaster should use to hold scheduler state and 110 status information. The default (which creates an SQLite database in 111 BASEDIR/state.sqlite) is equivalent to: 112 113 --db='sqlite:///state.sqlite' 114 115 To use a remote MySQL database instead, use something like: 116 117 --db='mysql://bbuser:bbpasswd@dbhost/bbdb' 118 The --db string is stored verbatim in the buildbot.tac file, and 119 evaluated as 'buildbot start' time to pass a DBConnector instance into 120 the newly-created BuildMaster object. 121 """ 122
123 - def postOptions(self):
124 base.BasedirMixin.postOptions(self) 125 if not re.match('^\d+$', self['log-size']): 126 raise usage.UsageError("log-size parameter needs to be an int") 127 if not re.match('^\d+$', self['log-count']) and \ 128 self['log-count'] != 'None': 129 raise usage.UsageError("log-count parameter needs to be an int "+ 130 " or None")
131 132
133 -class StopOptions(base.BasedirMixin, base.SubcommandOptions):
134 subcommandFunction = "buildbot.scripts.stop.stop" 135 optFlags = [ 136 ["quiet", "q", "Do not emit the commands being run"], 137 ]
138 - def getSynopsis(self):
139 return "Usage: buildbot stop [<basedir>]"
140 141
142 -class RestartOptions(base.BasedirMixin, base.SubcommandOptions):
143 subcommandFunction = "buildbot.scripts.restart.restart" 144 optFlags = [ 145 ['quiet', 'q', "Don't display startup log messages"], 146 ['nodaemon', None, "Don't daemonize (stay in foreground)"], 147 ]
148 - def getSynopsis(self):
149 return "Usage: buildbot restart [<basedir>]"
150 151
152 -class StartOptions(base.BasedirMixin, base.SubcommandOptions):
153 subcommandFunction = "buildbot.scripts.start.start" 154 optFlags = [ 155 ['quiet', 'q', "Don't display startup log messages"], 156 ['nodaemon', None, "Don't daemonize (stay in foreground)"], 157 ]
158 - def getSynopsis(self):
159 return "Usage: buildbot start [<basedir>]"
160 161
162 -class ReconfigOptions(base.BasedirMixin, base.SubcommandOptions):
163 subcommandFunction = "buildbot.scripts.reconfig.reconfig" 164 optFlags = [ 165 ['quiet', 'q', "Don't display log messages about reconfiguration"], 166 ]
167 - def getSynopsis(self):
168 return "Usage: buildbot reconfig [<basedir>]"
169 170
171 -class DebugClientOptions(base.SubcommandOptions):
172 subcommandFunction = "buildbot.scripts.debugclient.debugclient" 173 optParameters = [ 174 ["master", "m", None, 175 "Location of the buildmaster's slaveport (host:port)"], 176 ["passwd", "p", None, "Debug password to use"], 177 ] 178 buildbotOptions = [ 179 [ 'master', 'master' ], 180 [ 'debugMaster', 'master' ], 181 ] 182 requiredOptions = [ 'master', 'passwd' ] 183
184 - def getSynopsis(self):
185 return "Usage: buildbot debugclient [options]"
186
187 - def parseArgs(self, *args):
188 if len(args) > 0: 189 self['master'] = args[0] 190 if len(args) > 1: 191 self['passwd'] = args[1] 192 if len(args) > 2: 193 raise usage.UsageError("I wasn't expecting so many arguments")
194 195
196 -class BaseStatusClientOptions(base.SubcommandOptions):
197 optFlags = [ 198 ['help', 'h', "Display this message"], 199 ] 200 optParameters = [ 201 ["master", "m", None, 202 "Location of the buildmaster's status port (host:port)"], 203 ["username", "u", "statusClient", "Username performing the trial build"], 204 ["passwd", 'p', "clientpw", "password for PB authentication"], 205 ] 206 buildbotOptions = [ 207 [ 'master', 'master' ], 208 [ 'masterstatus', 'master' ], 209 ] 210 requiredOptions = [ 'master' ] 211
212 - def parseArgs(self, *args):
213 if len(args) > 0: 214 self['master'] = args[0] 215 if len(args) > 1: 216 raise usage.UsageError("I wasn't expecting so many arguments")
217 218
219 -class StatusLogOptions(BaseStatusClientOptions):
220 subcommandFunction = "buildbot.scripts.statuslog.statuslog"
221 - def getSynopsis(self):
222 return "Usage: buildbot statuslog [options]"
223 224
225 -class StatusGuiOptions(BaseStatusClientOptions):
226 subcommandFunction = "buildbot.scripts.statusgui.statusgui"
227 - def getSynopsis(self):
228 return "Usage: buildbot statusgui [options]"
229 230
231 -class SendChangeOptions(base.SubcommandOptions):
232 subcommandFunction = "buildbot.scripts.sendchange.sendchange"
233 - def __init__(self):
234 base.SubcommandOptions.__init__(self) 235 self['properties'] = {}
236 237 optParameters = [ 238 ("master", "m", None, 239 "Location of the buildmaster's PBListener (host:port)"), 240 # deprecated in 0.8.3; remove in 0.8.5 (bug #1711) 241 ("auth", "a", 'change:changepw', 242 "Authentication token - username:password, or prompt for password"), 243 ("who", "W", None, "Author of the commit"), 244 ("repository", "R", '', "Repository specifier"), 245 ("vc", "s", None, "The VC system in use, one of: cvs, svn, darcs, hg, " 246 "bzr, git, mtn, p4"), 247 ("project", "P", '', "Project specifier"), 248 ("branch", "b", None, "Branch specifier"), 249 ("category", "C", None, "Category of repository"), 250 ("codebase", None, None, 251 "Codebase this change is in (requires 0.8.7 master or later)"), 252 ("revision", "r", None, "Revision specifier"), 253 ("revision_file", None, None, "Filename containing revision spec"), 254 ("property", "p", None, 255 "A property for the change, in the format: name:value"), 256 ("comments", "c", None, "log message"), 257 ("logfile", "F", None, 258 "Read the log messages from this file (- for stdin)"), 259 ("when", "w", None, "timestamp to use as the change time"), 260 ("revlink", "l", '', "Revision link (revlink)"), 261 ("encoding", "e", 'utf8', 262 "Encoding of other parameters (default utf8)"), 263 ] 264 265 buildbotOptions = [ 266 [ 'master', 'master' ], 267 [ 'who', 'who' ], 268 [ 'branch', 'branch' ], 269 [ 'category', 'category' ], 270 [ 'vc', 'vc' ], 271 ] 272 273 requiredOptions = [ 'who', 'master' ] 274
275 - def getSynopsis(self):
276 return "Usage: buildbot sendchange [options] filenames.."
277
278 - def parseArgs(self, *args):
279 self['files'] = args
280
281 - def opt_property(self, property):
282 name,value = property.split(':', 1) 283 self['properties'][name] = value
284
285 - def postOptions(self):
286 base.SubcommandOptions.postOptions(self) 287 288 if self.get("revision_file"): 289 with open(self["revision_file"],"r") as f: 290 self['revision'] = f.read() 291 292 if self.get('when'): 293 try: 294 self['when'] = float(self['when']) 295 except: 296 raise usage.UsageError('invalid "when" value %s' 297 % (self['when'],)) 298 else: 299 self['when'] = None 300 301 if not self.get('comments') and self.get('logfile'): 302 if self['logfile'] == "-": 303 self['comments'] = sys.stdin.read() 304 else: 305 with open(self['logfile'], "rt") as f: 306 self['comments'] = f.read() 307 if self.get('comments') is None: 308 self['comments'] = "" 309 310 # fix up the auth with a password if none was given 311 auth = self.get('auth') 312 if ':' not in auth: 313 import getpass 314 pw = getpass.getpass("Enter password for '%s': " % auth) 315 auth = "%s:%s" % (auth, pw) 316 self['auth'] = tuple(auth.split(':', 1)) 317 318 vcs = ['cvs', 'svn', 'darcs', 'hg', 'bzr', 'git', 'mtn', 'p4'] 319 if self.get('vc') and self.get('vc') not in vcs: 320 raise usage.UsageError("vc must be one of %s" % (', '.join(vcs))) 321 322 if not self.get('who'): 323 raise usage.UsageError("you must provide a committer (--who)") 324 if not self.get('master'): 325 raise usage.UsageError("you must provide the master location")
326 327
328 -class TryOptions(base.SubcommandOptions):
329 subcommandFunction = "buildbot.scripts.trycmd.trycmd" 330 optParameters = [ 331 ["connect", "c", None, 332 "How to reach the buildmaster, either 'ssh' or 'pb'"], 333 # for ssh, use --host, --username, and --jobdir 334 ["host", None, None, 335 "Hostname (used by ssh) for the buildmaster"], 336 ["jobdir", None, None, 337 "Directory (on the buildmaster host) where try jobs are deposited"], 338 ["username", "u", None, 339 "Username performing the try build"], 340 # for PB, use --master, --username, and --passwd 341 ["master", "m", None, 342 "Location of the buildmaster's PBListener (host:port)"], 343 ["passwd", None, None, 344 "Password for PB authentication"], 345 ["who", "w", None, 346 "Who is responsible for the try build"], 347 ["comment", "C", None, 348 "A comment which can be used in notifications for this build"], 349 350 # for ssh to accommodate running in a virtualenv on the buildmaster 351 ["buildbotbin", None, "buildbot", 352 "buildbot binary to use on the buildmaster host"], 353 354 ["diff", None, None, 355 "Filename of a patch to use instead of scanning a local tree. " 356 "Use '-' for stdin."], 357 ["patchlevel", "p", 0, 358 "Number of slashes to remove from patch pathnames, " 359 "like the -p option to 'patch'"], 360 361 ["baserev", None, None, 362 "Base revision to use instead of scanning a local tree."], 363 364 ["vc", None, None, 365 "The VC system in use, one of: bzr, cvs, darcs, git, hg, " 366 "mtn, p4, svn"], 367 ["branch", None, None, 368 "The branch in use, for VC systems that can't figure it out " 369 "themselves"], 370 ["repository", None, None, 371 "Repository to use, instead of path to working directory."], 372 373 ["builder", "b", None, 374 "Run the trial build on this Builder. Can be used multiple times."], 375 ["properties", None, None, 376 "A set of properties made available in the build environment, " 377 "format is --properties=prop1=value1,prop2=value2,.. " 378 "option can be specified multiple times."], 379 380 ["topfile", None, None, 381 "Name of a file at the top of the tree, used to find the top. " 382 "Only needed for SVN and CVS."], 383 ["topdir", None, None, 384 "Path to the top of the working copy. Only needed for SVN and CVS."], 385 ] 386 387 optFlags = [ 388 ["wait", None, 389 "wait until the builds have finished"], 390 ["dryrun", 'n', 391 "Gather info, but don't actually submit."], 392 ["get-builder-names", None, 393 "Get the names of available builders. Doesn't submit anything. " 394 "Only supported for 'pb' connections."], 395 ["quiet", "q", 396 "Don't print status of current builds while waiting."], 397 ] 398 399 # Mapping of .buildbot/options names to command-line options 400 buildbotOptions = [ 401 [ 'try_connect', 'connect' ], 402 #[ 'try_builders', 'builders' ], <-- handled in postOptions 403 [ 'try_vc', 'vc' ], 404 [ 'try_branch', 'branch' ], 405 [ 'try_repository', 'repository' ], 406 [ 'try_topdir', 'topdir' ], 407 [ 'try_topfile', 'topfile' ], 408 [ 'try_host', 'host' ], 409 [ 'try_username', 'username' ], 410 [ 'try_jobdir', 'jobdir' ], 411 [ 'try_buildbotbin', 'buildbotbin' ], 412 [ 'try_passwd', 'passwd' ], 413 [ 'try_master', 'master' ], 414 [ 'try_who', 'who' ], 415 [ 'try_comment', 'comment' ], 416 #[ 'try_wait', 'wait' ], <-- handled in postOptions 417 #[ 'try_quiet', 'quiet' ], <-- handled in postOptions 418 419 # Deprecated command mappings from the quirky old days: 420 [ 'try_masterstatus', 'master' ], 421 [ 'try_dir', 'jobdir' ], 422 [ 'try_password', 'passwd' ], 423 ] 424
425 - def __init__(self):
426 base.SubcommandOptions.__init__(self) 427 self['builders'] = [] 428 self['properties'] = {}
429
430 - def opt_builder(self, option):
431 self['builders'].append(option)
432
433 - def opt_properties(self, option):
434 # We need to split the value of this option into a dictionary of properties 435 propertylist = option.split(",") 436 for i in range(0,len(propertylist)): 437 splitproperty = propertylist[i].split("=", 1) 438 self['properties'][splitproperty[0]] = splitproperty[1]
439
440 - def opt_patchlevel(self, option):
441 self['patchlevel'] = int(option)
442
443 - def getSynopsis(self):
444 return "Usage: buildbot try [options]"
445
446 - def postOptions(self):
447 base.SubcommandOptions.postOptions(self) 448 opts = self.optionsFile 449 if not self['builders']: 450 self['builders'] = opts.get('try_builders', []) 451 if opts.get('try_wait', False): 452 self['wait'] = True 453 if opts.get('try_quiet', False): 454 self['quiet'] = True 455 # get the global 'masterstatus' option if it's set and no master 456 # was specified otherwise 457 if not self['master']: 458 self['master'] = opts.get('masterstatus', None)
459 460
461 -class TryServerOptions(base.SubcommandOptions):
462 subcommandFunction = "buildbot.scripts.tryserver.tryserver" 463 optParameters = [ 464 ["jobdir", None, None, "the jobdir (maildir) for submitting jobs"], 465 ] 466 requiredOptions = [ 'jobdir' ] 467
468 - def getSynopsis(self):
469 return "Usage: buildbot tryserver [options]"
470
471 - def postOptions(self):
472 if not self['jobdir']: 473 raise usage.UsageError('jobdir is required')
474 475
476 -class CheckConfigOptions(base.SubcommandOptions):
477 subcommandFunction = "buildbot.scripts.checkconfig.checkconfig" 478 optFlags = [ 479 ['quiet', 'q', "Don't display error messages or tracebacks"], 480 ] 481
482 - def getSynopsis(self):
483 return "Usage: buildbot checkconfig [configFile]\n" + \ 484 " If not specified, 'master.cfg' will be used as 'configFile'"
485
486 - def parseArgs(self, *args):
487 if len(args) >= 1: 488 self['configFile'] = args[0] 489 else: 490 self['configFile'] = 'master.cfg'
491 492
493 -class UserOptions(base.SubcommandOptions):
494 subcommandFunction = "buildbot.scripts.user.user" 495 optParameters = [ 496 ["master", "m", None, 497 "Location of the buildmaster's PBListener (host:port)"], 498 ["username", "u", None, 499 "Username for PB authentication"], 500 ["passwd", "p", None, 501 "Password for PB authentication"], 502 ["op", None, None, 503 "User management operation: add, remove, update, get"], 504 ["bb_username", None, None, 505 "Username to set for a given user. Only availabe on 'update', " 506 "and bb_password must be given as well."], 507 ["bb_password", None, None, 508 "Password to set for a given user. Only availabe on 'update', " 509 "and bb_username must be given as well."], 510 ["ids", None, None, 511 "User's identifiers, used to find users in 'remove' and 'get' " 512 "Can be specified multiple times (--ids=id1,id2,id3)"], 513 ["info", None, None, 514 "User information in the form: --info=type=value,type=value,.. " 515 "Used in 'add' and 'update', can be specified multiple times. " 516 "Note that 'update' requires --info=id:type=value..."] 517 ] 518 buildbotOptions = [ 519 [ 'master', 'master' ], 520 [ 'user_master', 'master' ], 521 [ 'user_username', 'username' ], 522 [ 'user_passwd', 'passwd' ], 523 ] 524 requiredOptions = [ 'master' ] 525 526 longdesc = """ 527 Currently implemented types for --info= are:\n 528 git, svn, hg, cvs, darcs, bzr, email 529 """ 530
531 - def __init__(self):
532 base.SubcommandOptions.__init__(self) 533 self['ids'] = [] 534 self['info'] = []
535
536 - def opt_ids(self, option):
537 id_list = option.split(",") 538 self['ids'].extend(id_list)
539
540 - def opt_info(self, option):
541 # splits info into type/value dictionary, appends to info 542 info_list = option.split(",") 543 info_elem = {} 544 545 if len(info_list) == 1 and '=' not in info_list[0]: 546 info_elem["identifier"] = info_list[0] 547 self['info'].append(info_elem) 548 else: 549 for i in range(0, len(info_list)): 550 split_info = info_list[i].split("=", 1) 551 552 # pull identifier from update --info 553 if ":" in split_info[0]: 554 split_id = split_info[0].split(":") 555 info_elem["identifier"] = split_id[0] 556 split_info[0] = split_id[1] 557 558 info_elem[split_info[0]] = split_info[1] 559 self['info'].append(info_elem)
560
561 - def getSynopsis(self):
562 return "Usage: buildbot user [options]"
563
564 - def _checkValidTypes(self, info):
565 from buildbot.process.users import users 566 valid = set(['identifier', 'email'] + users.srcs) 567 568 for user in info: 569 for attr_type in user: 570 if attr_type not in valid: 571 raise usage.UsageError( 572 "Type not a valid attr_type, must be in: %s" 573 % ', '.join(valid))
574
575 - def postOptions(self):
576 base.SubcommandOptions.postOptions(self) 577 578 master = self.get('master') 579 try: 580 master, port = master.split(":") 581 port = int(port) 582 except: 583 raise usage.UsageError("master must have the form 'hostname:port'") 584 585 op = self.get('op') 586 if not op: 587 raise usage.UsageError("you must specify an operation: add, " 588 "remove, update, get") 589 if op not in ['add', 'remove', 'update', 'get']: 590 raise usage.UsageError("bad op %r, use 'add', 'remove', 'update', " 591 "or 'get'" % op) 592 593 if not self.get('username') or not self.get('passwd'): 594 raise usage.UsageError("A username and password must be given") 595 596 bb_username = self.get('bb_username') 597 bb_password = self.get('bb_password') 598 if bb_username or bb_password: 599 if op != 'update': 600 raise usage.UsageError("bb_username and bb_password only work " 601 "with update") 602 if not bb_username or not bb_password: 603 raise usage.UsageError("Must specify both bb_username and " 604 "bb_password or neither.") 605 606 info = self.get('info') 607 ids = self.get('ids') 608 609 # check for erroneous args 610 if not info and not ids: 611 raise usage.UsageError("must specify either --ids or --info") 612 613 if op == 'add' or op == 'update': 614 if ids: 615 raise usage.UsageError("cannot use --ids with 'add' or " 616 "'update'") 617 self._checkValidTypes(info) 618 if op == 'update': 619 for user in info: 620 if 'identifier' not in user: 621 raise usage.UsageError("no ids found in update info; " 622 "use: --info=id:type=value,type=value,..") 623 if op == 'add': 624 for user in info: 625 if 'identifier' in user: 626 raise usage.UsageError("identifier found in add info, " 627 "use: --info=type=value,type=value,..") 628 if op == 'remove' or op == 'get': 629 if info: 630 raise usage.UsageError("cannot use --info with 'remove' " 631 "or 'get'")
632 633
634 -class Options(usage.Options):
635 synopsis = "Usage: buildbot <command> [command options]" 636 637 subCommands = [ 638 ['create-master', None, CreateMasterOptions, 639 "Create and populate a directory for a new buildmaster"], 640 ['upgrade-master', None, UpgradeMasterOptions, 641 "Upgrade an existing buildmaster directory for the current version"], 642 ['start', None, StartOptions, 643 "Start a buildmaster"], 644 ['stop', None, StopOptions, 645 "Stop a buildmaster"], 646 ['restart', None, RestartOptions, 647 "Restart a buildmaster"], 648 ['reconfig', None, ReconfigOptions, 649 "SIGHUP a buildmaster to make it re-read the config file"], 650 ['sighup', None, ReconfigOptions, 651 "SIGHUP a buildmaster to make it re-read the config file"], 652 ['sendchange', None, SendChangeOptions, 653 "Send a change to the buildmaster"], 654 ['debugclient', None, DebugClientOptions, 655 "Launch a small debug panel GUI"], 656 ['statuslog', None, StatusLogOptions, 657 "Emit current builder status to stdout"], 658 ['statusgui', None, StatusGuiOptions, 659 "Display a small window showing current builder status"], 660 ['try', None, TryOptions, 661 "Run a build with your local changes"], 662 ['tryserver', None, TryServerOptions, 663 "buildmaster-side 'try' support function, not for users"], 664 ['checkconfig', None, CheckConfigOptions, 665 "test the validity of a master.cfg config file"], 666 ['user', None, UserOptions, 667 "Manage users in buildbot's database"] 668 ] 669
670 - def opt_version(self):
671 import buildbot 672 print "Buildbot version: %s" % buildbot.version 673 usage.Options.opt_version(self)
674
675 - def opt_verbose(self):
676 from twisted.python import log 677 log.startLogging(sys.stderr)
678
679 - def postOptions(self):
680 if not hasattr(self, 'subOptions'): 681 raise usage.UsageError("must specify a command")
682 683
684 -def run():
685 config = Options() 686 try: 687 config.parseOptions(sys.argv[1:]) 688 except usage.error, e: 689 print "%s: %s" % (sys.argv[0], e) 690 print 691 c = getattr(config, 'subOptions', config) 692 print str(c) 693 sys.exit(1) 694 695 subconfig = config.subOptions 696 subcommandFunction = reflect.namedObject(subconfig.subcommandFunction) 697 sys.exit(subcommandFunction(subconfig))
698