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