1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 from __future__ import with_statement
17
18
19
20
21
22
23
24 from twisted.python import usage, reflect
25 import re
26 import sys
27
28 from buildbot.scripts import base
29
30
31
32
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
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
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 ]
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 ]
139 return "Usage: buildbot stop [<basedir>]"
140
141
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 ]
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 ]
159 return "Usage: buildbot start [<basedir>]"
160
161
163 subcommandFunction = "buildbot.scripts.reconfig.reconfig"
164 optFlags = [
165 ['quiet', 'q', "Don't display log messages about reconfiguration"],
166 ]
168 return "Usage: buildbot reconfig [<basedir>]"
169
170
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
185 return "Usage: buildbot debugclient [options]"
186
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
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
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
220 subcommandFunction = "buildbot.scripts.statuslog.statuslog"
222 return "Usage: buildbot statuslog [options]"
223
224
226 subcommandFunction = "buildbot.scripts.statusgui.statusgui"
228 return "Usage: buildbot statusgui [options]"
229
230
232 subcommandFunction = "buildbot.scripts.sendchange.sendchange"
236
237 optParameters = [
238 ("master", "m", None,
239 "Location of the buildmaster's PBListener (host:port)"),
240
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
276 return "Usage: buildbot sendchange [options] filenames.."
277
280
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
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
329 subcommandFunction = "buildbot.scripts.trycmd.trycmd"
330 optParameters = [
331 ["connect", "c", None,
332 "How to reach the buildmaster, either 'ssh' or 'pb'"],
333
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
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
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
400 buildbotOptions = [
401 [ 'try_connect', 'connect' ],
402
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
417
418
419
420 [ 'try_masterstatus', 'master' ],
421 [ 'try_dir', 'jobdir' ],
422 [ 'try_password', 'passwd' ],
423 ]
424
429
431 self['builders'].append(option)
432
434
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
441 self['patchlevel'] = int(option)
442
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
456
457 if not self['master']:
458 self['master'] = opts.get('masterstatus', None)
459
460
462 subcommandFunction = "buildbot.scripts.tryserver.tryserver"
463 optParameters = [
464 ["jobdir", None, None, "the jobdir (maildir) for submitting jobs"],
465 ]
466 requiredOptions = [ 'jobdir' ]
467
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
477 subcommandFunction = "buildbot.scripts.checkconfig.checkconfig"
478 optFlags = [
479 ['quiet', 'q', "Don't display error messages or tracebacks"],
480 ]
481
483 return "Usage: buildbot checkconfig [configFile]\n" + \
484 " If not specified, 'master.cfg' will be used as 'configFile'"
485
487 if len(args) >= 1:
488 self['configFile'] = args[0]
489 else:
490 self['configFile'] = 'master.cfg'
491
492
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
535
537 id_list = option.split(",")
538 self['ids'].extend(id_list)
539
541
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
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
562 return "Usage: buildbot user [options]"
563
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
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
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
674
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
698