1
2
3 from warnings import warn
4 from email.Utils import formatdate
5 from twisted.python import log
6 from buildbot.process.buildstep import LoggingBuildStep, LoggedRemoteCommand
7 from buildbot.interfaces import BuildSlaveTooOldError
8 from buildbot.status.builder import SKIPPED
9
10
11 -class Source(LoggingBuildStep):
12 """This is a base class to generate a source tree in the buildslave.
13 Each version control system has a specialized subclass, and is expected
14 to override __init__ and implement computeSourceRevision() and
15 startVC(). The class as a whole builds up the self.args dictionary, then
16 starts a LoggedRemoteCommand with those arguments.
17 """
18
19
20 haltOnFailure = True
21 flunkOnFailure = True
22 notReally = False
23
24 branch = None
25
26 - def __init__(self, workdir=None, mode='update', alwaysUseLatest=False,
27 timeout=20*60, retry=None, **kwargs):
28 """
29 @type workdir: string
30 @param workdir: local directory (relative to the Builder's root)
31 where the tree should be placed
32
33 @type mode: string
34 @param mode: the kind of VC operation that is desired:
35 - 'update': specifies that the checkout/update should be
36 performed directly into the workdir. Each build is performed
37 in the same directory, allowing for incremental builds. This
38 minimizes disk space, bandwidth, and CPU time. However, it
39 may encounter problems if the build process does not handle
40 dependencies properly (if you must sometimes do a 'clean
41 build' to make sure everything gets compiled), or if source
42 files are deleted but generated files can influence test
43 behavior (e.g. python's .pyc files), or when source
44 directories are deleted but generated files prevent CVS from
45 removing them.
46
47 - 'copy': specifies that the source-controlled workspace
48 should be maintained in a separate directory (called the
49 'copydir'), using checkout or update as necessary. For each
50 build, a new workdir is created with a copy of the source
51 tree (rm -rf workdir; cp -R -P -p copydir workdir). This
52 doubles the disk space required, but keeps the bandwidth low
53 (update instead of a full checkout). A full 'clean' build
54 is performed each time. This avoids any generated-file
55 build problems, but is still occasionally vulnerable to
56 problems such as a CVS repository being manually rearranged
57 (causing CVS errors on update) which are not an issue with
58 a full checkout.
59
60 - 'clobber': specifies that the working directory should be
61 deleted each time, necessitating a full checkout for each
62 build. This insures a clean build off a complete checkout,
63 avoiding any of the problems described above, but is
64 bandwidth intensive, as the whole source tree must be
65 pulled down for each build.
66
67 - 'export': is like 'clobber', except that e.g. the 'cvs
68 export' command is used to create the working directory.
69 This command removes all VC metadata files (the
70 CVS/.svn/{arch} directories) from the tree, which is
71 sometimes useful for creating source tarballs (to avoid
72 including the metadata in the tar file). Not all VC systems
73 support export.
74
75 @type alwaysUseLatest: boolean
76 @param alwaysUseLatest: whether to always update to the most
77 recent available sources for this build.
78
79 Normally the Source step asks its Build for a list of all
80 Changes that are supposed to go into the build, then computes a
81 'source stamp' (revision number or timestamp) that will cause
82 exactly that set of changes to be present in the checked out
83 tree. This is turned into, e.g., 'cvs update -D timestamp', or
84 'svn update -r revnum'. If alwaysUseLatest=True, bypass this
85 computation and always update to the latest available sources
86 for each build.
87
88 The source stamp helps avoid a race condition in which someone
89 commits a change after the master has decided to start a build
90 but before the slave finishes checking out the sources. At best
91 this results in a build which contains more changes than the
92 buildmaster thinks it has (possibly resulting in the wrong
93 person taking the blame for any problems that result), at worst
94 is can result in an incoherent set of sources (splitting a
95 non-atomic commit) which may not build at all.
96
97 @type retry: tuple of ints (delay, repeats) (or None)
98 @param retry: if provided, VC update failures are re-attempted up
99 to REPEATS times, with DELAY seconds between each
100 attempt. Some users have slaves with poor connectivity
101 to their VC repository, and they say that up to 80% of
102 their build failures are due to transient network
103 failures that could be handled by simply retrying a
104 couple times.
105
106 """
107
108 LoggingBuildStep.__init__(self, **kwargs)
109 self.addFactoryArguments(workdir=workdir,
110 mode=mode,
111 alwaysUseLatest=alwaysUseLatest,
112 timeout=timeout,
113 retry=retry,
114 )
115
116 assert mode in ("update", "copy", "clobber", "export")
117 if retry:
118 delay, repeats = retry
119 assert isinstance(repeats, int)
120 assert repeats > 0
121 self.args = {'mode': mode,
122 'workdir': workdir,
123 'timeout': timeout,
124 'retry': retry,
125 'patch': None,
126 }
127 self.alwaysUseLatest = alwaysUseLatest
128
129
130 description = ["updating"]
131 descriptionDone = ["update"]
132 if mode == "clobber":
133 description = ["checkout"]
134
135 descriptionDone = ["checkout"]
136 elif mode == "export":
137 description = ["exporting"]
138 descriptionDone = ["export"]
139 self.description = description
140 self.descriptionDone = descriptionDone
141
143 self.args['workdir'] = self.args['workdir'] or workdir
144
149
151 """Each subclass must implement this method to do something more
152 precise than -rHEAD every time. For version control systems that use
153 repository-wide change numbers (SVN, P4), this can simply take the
154 maximum such number from all the changes involved in this build. For
155 systems that do not (CVS), it needs to create a timestamp based upon
156 the latest Change, the Build's treeStableTimer, and an optional
157 self.checkoutDelay value."""
158 return None
159
185
187 if cmd.updates.has_key("got_revision"):
188 got_revision = cmd.updates["got_revision"][-1]
189 if got_revision is not None:
190 self.setProperty("got_revision", str(got_revision), "Source")
191
192
193
195 """I do CVS checkout/update operations.
196
197 Note: if you are doing anonymous/pserver CVS operations, you will need
198 to manually do a 'cvs login' on each buildslave before the slave has any
199 hope of success. XXX: fix then, take a cvs password as an argument and
200 figure out how to do a 'cvs login' on each build
201 """
202
203 name = "cvs"
204
205
206
207
208
209
210
211
212
213
214
215 - def __init__(self, cvsroot, cvsmodule,
216 global_options=[], branch=None, checkoutDelay=None,
217 checkout_options=[],
218 login=None,
219 **kwargs):
220
221 """
222 @type cvsroot: string
223 @param cvsroot: CVS Repository from which the source tree should
224 be obtained. '/home/warner/Repository' for local
225 or NFS-reachable repositories,
226 ':pserver:anon@foo.com:/cvs' for anonymous CVS,
227 'user@host.com:/cvs' for non-anonymous CVS or
228 CVS over ssh. Lots of possibilities, check the
229 CVS documentation for more.
230
231 @type cvsmodule: string
232 @param cvsmodule: subdirectory of CVS repository that should be
233 retrieved
234
235 @type login: string or None
236 @param login: if not None, a string which will be provided as a
237 password to the 'cvs login' command, used when a
238 :pserver: method is used to access the repository.
239 This login is only needed once, but must be run
240 each time (just before the CVS operation) because
241 there is no way for the buildslave to tell whether
242 it was previously performed or not.
243
244 @type branch: string
245 @param branch: the default branch name, will be used in a '-r'
246 argument to specify which branch of the source tree
247 should be used for this checkout. Defaults to None,
248 which means to use 'HEAD'.
249
250 @type checkoutDelay: int or None
251 @param checkoutDelay: if not None, the number of seconds to put
252 between the last known Change and the
253 timestamp given to the -D argument. This
254 defaults to exactly half of the parent
255 Build's .treeStableTimer, but it could be
256 set to something else if your CVS change
257 notification has particularly weird
258 latency characteristics.
259
260 @type global_options: list of strings
261 @param global_options: these arguments are inserted in the cvs
262 command line, before the
263 'checkout'/'update' command word. See
264 'cvs --help-options' for a list of what
265 may be accepted here. ['-r'] will make
266 the checked out files read only. ['-r',
267 '-R'] will also assume the repository is
268 read-only (I assume this means it won't
269 use locks to insure atomic access to the
270 ,v files).
271
272 @type checkout_options: list of strings
273 @param checkout_options: these arguments are inserted in the cvs
274 command line, after 'checkout' but before
275 branch or revision specifiers.
276 """
277
278 self.checkoutDelay = checkoutDelay
279 self.branch = branch
280
281 Source.__init__(self, **kwargs)
282 self.addFactoryArguments(cvsroot=cvsroot,
283 cvsmodule=cvsmodule,
284 global_options=global_options,
285 checkout_options=checkout_options,
286 branch=branch,
287 checkoutDelay=checkoutDelay,
288 login=login,
289 )
290
291 self.args.update({'cvsroot': cvsroot,
292 'cvsmodule': cvsmodule,
293 'global_options': global_options,
294 'checkout_options':checkout_options,
295 'login': login,
296 })
297
299 if not changes:
300 return None
301 lastChange = max([c.when for c in changes])
302 if self.checkoutDelay is not None:
303 when = lastChange + self.checkoutDelay
304 else:
305 lastSubmit = max([r.submittedAt for r in self.build.requests])
306 when = (lastChange + lastSubmit) / 2
307 return formatdate(when)
308
309 - def startVC(self, branch, revision, patch):
310 if self.slaveVersionIsOlderThan("cvs", "1.39"):
311
312
313
314
315
316 if (branch != self.branch
317 and self.args['mode'] in ("update", "copy")):
318 m = ("This buildslave (%s) does not know about multiple "
319 "branches, and using mode=%s would probably build the "
320 "wrong tree. "
321 "Refusing to build. Please upgrade the buildslave to "
322 "buildbot-0.7.0 or newer." % (self.build.slavename,
323 self.args['mode']))
324 log.msg(m)
325 raise BuildSlaveTooOldError(m)
326
327 if branch is None:
328 branch = "HEAD"
329 self.args['branch'] = branch
330 self.args['revision'] = revision
331 self.args['patch'] = patch
332
333 if self.args['branch'] == "HEAD" and self.args['revision']:
334
335
336 self.args['branch'] = None
337
338
339 warnings = []
340 slavever = self.slaveVersion("cvs", "old")
341
342 if slavever == "old":
343
344 if self.args['mode'] == "export":
345 self.args['export'] = 1
346 elif self.args['mode'] == "clobber":
347 self.args['clobber'] = 1
348 elif self.args['mode'] == "copy":
349 self.args['copydir'] = "source"
350 self.args['tag'] = self.args['branch']
351 assert not self.args['patch']
352
353 cmd = LoggedRemoteCommand("cvs", self.args)
354 self.startCommand(cmd, warnings)
355
356
358 """I perform Subversion checkout/update operations."""
359
360 name = 'svn'
361
362 - def __init__(self, svnurl=None, baseURL=None, defaultBranch=None,
363 directory=None, username=None, password=None,
364 extra_args=None, **kwargs):
365 """
366 @type svnurl: string
367 @param svnurl: the URL which points to the Subversion server,
368 combining the access method (HTTP, ssh, local file),
369 the repository host/port, the repository path, the
370 sub-tree within the repository, and the branch to
371 check out. Using C{svnurl} does not enable builds of
372 alternate branches: use C{baseURL} to enable this.
373 Use exactly one of C{svnurl} and C{baseURL}.
374
375 @param baseURL: if branches are enabled, this is the base URL to
376 which a branch name will be appended. It should
377 probably end in a slash. Use exactly one of
378 C{svnurl} and C{baseURL}.
379
380 @param defaultBranch: if branches are enabled, this is the branch
381 to use if the Build does not specify one
382 explicitly. It will simply be appended
383 to C{baseURL} and the result handed to
384 the SVN command.
385
386 @param username: username to pass to svn's --username
387 @param password: username to pass to svn's --password
388 """
389
390 if not kwargs.has_key('workdir') and directory is not None:
391
392 warn("Please use workdir=, not directory=", DeprecationWarning)
393 kwargs['workdir'] = directory
394
395 self.svnurl = svnurl
396 self.baseURL = baseURL
397 self.branch = defaultBranch
398 self.username = username
399 self.password = password
400 self.extra_args = extra_args
401
402 Source.__init__(self, **kwargs)
403 self.addFactoryArguments(svnurl=svnurl,
404 baseURL=baseURL,
405 defaultBranch=defaultBranch,
406 directory=directory,
407 username=username,
408 password=password,
409 extra_args=extra_args,
410 )
411
412 if not svnurl and not baseURL:
413 raise ValueError("you must use exactly one of svnurl and baseURL")
414
415
421
422 - def startVC(self, branch, revision, patch):
423
424
425 warnings = []
426 slavever = self.slaveVersion("svn", "old")
427 if not slavever:
428 m = "slave does not have the 'svn' command"
429 raise BuildSlaveTooOldError(m)
430
431 if self.slaveVersionIsOlderThan("svn", "1.39"):
432
433
434
435
436
437 if (branch != self.branch
438 and self.args['mode'] in ("update", "copy")):
439 m = ("This buildslave (%s) does not know about multiple "
440 "branches, and using mode=%s would probably build the "
441 "wrong tree. "
442 "Refusing to build. Please upgrade the buildslave to "
443 "buildbot-0.7.0 or newer." % (self.build.slavename,
444 self.args['mode']))
445 raise BuildSlaveTooOldError(m)
446
447 if slavever == "old":
448
449 if self.args['mode'] in ("clobber", "copy"):
450
451
452
453 warnings.append("WARNING: this slave can only do SVN updates"
454 ", not mode=%s\n" % self.args['mode'])
455 log.msg("WARNING: this slave only does mode=update")
456 if self.args['mode'] == "export":
457 raise BuildSlaveTooOldError("old slave does not have "
458 "mode=export")
459 self.args['directory'] = self.args['workdir']
460 if revision is not None:
461
462
463
464
465 m = ("WARNING: old slave can only update to HEAD, not "
466 "revision=%s" % revision)
467 log.msg(m)
468 warnings.append(m + "\n")
469 revision = "HEAD"
470 if patch:
471 raise BuildSlaveTooOldError("old slave can't do patch")
472
473 if self.svnurl:
474 assert not branch
475 self.args['svnurl'] = self.svnurl
476 else:
477 self.args['svnurl'] = self.baseURL + branch
478 self.args['revision'] = revision
479 self.args['patch'] = patch
480
481 if self.username is not None or self.password is not None:
482 if self.slaveVersionIsOlderThan("svn", "2.8"):
483 m = ("This buildslave (%s) does not support svn usernames "
484 "and passwords. "
485 "Refusing to build. Please upgrade the buildslave to "
486 "buildbot-0.7.10 or newer." % (self.build.slavename,))
487 raise BuildSlaveTooOldError(m)
488 if self.username is not None: self.args['username'] = self.username
489 if self.password is not None: self.args['password'] = self.password
490
491 if self.extra_args is not None:
492 self.args['extra_args'] = self.extra_args
493
494 revstuff = []
495 if branch is not None and branch != self.branch:
496 revstuff.append("[branch]")
497 if revision is not None:
498 revstuff.append("r%s" % revision)
499 if patch is not None:
500 revstuff.append("[patch]")
501 self.description.extend(revstuff)
502 self.descriptionDone.extend(revstuff)
503
504 cmd = LoggedRemoteCommand("svn", self.args)
505 self.startCommand(cmd, warnings)
506
507
509 """Check out a source tree from a Darcs repository at 'repourl'.
510
511 Darcs has no concept of file modes. This means the eXecute-bit will be
512 cleared on all source files. As a result, you may need to invoke
513 configuration scripts with something like:
514
515 C{s(step.Configure, command=['/bin/sh', './configure'])}
516 """
517
518 name = "darcs"
519
520 - def __init__(self, repourl=None, baseURL=None, defaultBranch=None,
521 **kwargs):
522 """
523 @type repourl: string
524 @param repourl: the URL which points at the Darcs repository. This
525 is used as the default branch. Using C{repourl} does
526 not enable builds of alternate branches: use
527 C{baseURL} to enable this. Use either C{repourl} or
528 C{baseURL}, not both.
529
530 @param baseURL: if branches are enabled, this is the base URL to
531 which a branch name will be appended. It should
532 probably end in a slash. Use exactly one of
533 C{repourl} and C{baseURL}.
534
535 @param defaultBranch: if branches are enabled, this is the branch
536 to use if the Build does not specify one
537 explicitly. It will simply be appended to
538 C{baseURL} and the result handed to the
539 'darcs pull' command.
540 """
541 self.repourl = repourl
542 self.baseURL = baseURL
543 self.branch = defaultBranch
544 Source.__init__(self, **kwargs)
545 self.addFactoryArguments(repourl=repourl,
546 baseURL=baseURL,
547 defaultBranch=defaultBranch,
548 )
549 assert self.args['mode'] != "export", \
550 "Darcs does not have an 'export' mode"
551 if (not repourl and not baseURL) or (repourl and baseURL):
552 raise ValueError("you must provide exactly one of repourl and"
553 " baseURL")
554
555 - def startVC(self, branch, revision, patch):
556 slavever = self.slaveVersion("darcs")
557 if not slavever:
558 m = "slave is too old, does not know about darcs"
559 raise BuildSlaveTooOldError(m)
560
561 if self.slaveVersionIsOlderThan("darcs", "1.39"):
562 if revision:
563
564 m = "0.6.6 slaves can't handle args['revision']"
565 raise BuildSlaveTooOldError(m)
566
567
568
569
570
571
572 if (branch != self.branch
573 and self.args['mode'] in ("update", "copy")):
574 m = ("This buildslave (%s) does not know about multiple "
575 "branches, and using mode=%s would probably build the "
576 "wrong tree. "
577 "Refusing to build. Please upgrade the buildslave to "
578 "buildbot-0.7.0 or newer." % (self.build.slavename,
579 self.args['mode']))
580 raise BuildSlaveTooOldError(m)
581
582 if self.repourl:
583 assert not branch
584 self.args['repourl'] = self.repourl
585 else:
586 self.args['repourl'] = self.baseURL + branch
587 self.args['revision'] = revision
588 self.args['patch'] = patch
589
590 revstuff = []
591 if branch is not None and branch != self.branch:
592 revstuff.append("[branch]")
593 self.description.extend(revstuff)
594 self.descriptionDone.extend(revstuff)
595
596 cmd = LoggedRemoteCommand("darcs", self.args)
597 self.startCommand(cmd)
598
599
601 """Check out a source tree from a git repository 'repourl'."""
602
603 name = "git"
604
605 - def __init__(self, repourl,
606 branch="master",
607 submodules=False,
608 **kwargs):
609 """
610 @type repourl: string
611 @param repourl: the URL which points at the git repository
612
613 @type branch: string
614 @param branch: The branch or tag to check out by default. If
615 a build specifies a different branch, it will
616 be used instead of this.
617
618 @type submodules: boolean
619 @param submodules: Whether or not to update (and initialize)
620 git submodules.
621
622 """
623 Source.__init__(self, **kwargs)
624 self.addFactoryArguments(repourl=repourl,
625 branch=branch,
626 submodules=submodules,
627 )
628 self.args.update({'repourl': repourl,
629 'branch': branch,
630 'submodules' : submodules,
631 })
632
637
638 - def startVC(self, branch, revision, patch):
650
651
653 """Check out a source tree from an Arch repository named 'archive'
654 available at 'url'. 'version' specifies which version number (development
655 line) will be used for the checkout: this is mostly equivalent to a
656 branch name. This version uses the 'tla' tool to do the checkout, to use
657 'baz' see L{Bazaar} instead.
658 """
659
660 name = "arch"
661
662
663 - def __init__(self, url, version, archive=None, **kwargs):
664 """
665 @type url: string
666 @param url: the Arch coordinates of the repository. This is
667 typically an http:// URL, but could also be the absolute
668 pathname of a local directory instead.
669
670 @type version: string
671 @param version: the category--branch--version to check out. This is
672 the default branch. If a build specifies a different
673 branch, it will be used instead of this.
674
675 @type archive: string
676 @param archive: The archive name. If provided, it must match the one
677 that comes from the repository. If not, the
678 repository's default will be used.
679 """
680 self.branch = version
681 Source.__init__(self, **kwargs)
682 self.addFactoryArguments(url=url,
683 version=version,
684 archive=archive,
685 )
686 self.args.update({'url': url,
687 'archive': archive,
688 })
689
691
692
693
694
695
696 if not changes:
697 return None
698 lastChange = None
699 for c in changes:
700 if not c.revision:
701 continue
702 if c.revision.endswith("--base-0"):
703 rev = 0
704 else:
705 i = c.revision.rindex("patch")
706 rev = int(c.revision[i+len("patch-"):])
707 lastChange = max(lastChange, rev)
708 if lastChange is None:
709 return None
710 if lastChange == 0:
711 return "base-0"
712 return "patch-%d" % lastChange
713
715 warnings = []
716 slavever = self.slaveVersion(cmd)
717 if not slavever:
718 m = "slave is too old, does not know about %s" % cmd
719 raise BuildSlaveTooOldError(m)
720
721
722 if self.slaveVersionIsOlderThan(cmd, "1.28"):
723 if not self.alwaysUseLatest:
724
725
726
727
728 m = "WARNING, buildslave is too old to use a revision"
729 log.msg(m)
730 warnings.append(m + "\n")
731
732 if self.slaveVersionIsOlderThan(cmd, "1.39"):
733
734
735
736
737
738 if (branch != self.branch
739 and self.args['mode'] in ("update", "copy")):
740 m = ("This buildslave (%s) does not know about multiple "
741 "branches, and using mode=%s would probably build the "
742 "wrong tree. "
743 "Refusing to build. Please upgrade the buildslave to "
744 "buildbot-0.7.0 or newer." % (self.build.slavename,
745 self.args['mode']))
746 log.msg(m)
747 raise BuildSlaveTooOldError(m)
748
749 return warnings
750
751 - def startVC(self, branch, revision, patch):
767
768
770 """Bazaar is an alternative client for Arch repositories. baz is mostly
771 compatible with tla, but archive registration is slightly different."""
772
773
774
775 - def __init__(self, url, version, archive, **kwargs):
776 """
777 @type url: string
778 @param url: the Arch coordinates of the repository. This is
779 typically an http:// URL, but could also be the absolute
780 pathname of a local directory instead.
781
782 @type version: string
783 @param version: the category--branch--version to check out
784
785 @type archive: string
786 @param archive: The archive name (required). This must always match
787 the one that comes from the repository, otherwise the
788 buildslave will attempt to get sources from the wrong
789 archive.
790 """
791 self.branch = version
792 Source.__init__(self, **kwargs)
793 self.addFactoryArguments(url=url,
794 version=version,
795 archive=archive,
796 )
797 self.args.update({'url': url,
798 'archive': archive,
799 })
800
801 - def startVC(self, branch, revision, patch):
817
819 """Check out a source tree from a bzr (Bazaar) repository at 'repourl'.
820
821 """
822
823 name = "bzr"
824
825 - def __init__(self, repourl=None, baseURL=None, defaultBranch=None,
826 forceSharedRepo=None,
827 **kwargs):
828 """
829 @type repourl: string
830 @param repourl: the URL which points at the bzr repository. This
831 is used as the default branch. Using C{repourl} does
832 not enable builds of alternate branches: use
833 C{baseURL} to enable this. Use either C{repourl} or
834 C{baseURL}, not both.
835
836 @param baseURL: if branches are enabled, this is the base URL to
837 which a branch name will be appended. It should
838 probably end in a slash. Use exactly one of
839 C{repourl} and C{baseURL}.
840
841 @param defaultBranch: if branches are enabled, this is the branch
842 to use if the Build does not specify one
843 explicitly. It will simply be appended to
844 C{baseURL} and the result handed to the
845 'bzr checkout pull' command.
846
847
848 @param forceSharedRepo: Boolean, defaults to False. If set to True,
849 the working directory will be made into a
850 bzr shared repository if it is not already.
851 Shared repository greatly reduces the amount
852 of history data that needs to be downloaded
853 if not using update/copy mode, or if using
854 update/copy mode with multiple branches.
855 """
856 self.repourl = repourl
857 self.baseURL = baseURL
858 self.branch = defaultBranch
859 Source.__init__(self, **kwargs)
860 self.addFactoryArguments(repourl=repourl,
861 baseURL=baseURL,
862 defaultBranch=defaultBranch,
863 forceSharedRepo=forceSharedRepo
864 )
865 self.args.update({'forceSharedRepo': forceSharedRepo})
866 if (not repourl and not baseURL) or (repourl and baseURL):
867 raise ValueError("you must provide exactly one of repourl and"
868 " baseURL")
869
871 if not changes:
872 return None
873 lastChange = max([int(c.revision) for c in changes])
874 return lastChange
875
876 - def startVC(self, branch, revision, patch):
900
901
903 """Check out a source tree from a mercurial repository 'repourl'."""
904
905 name = "hg"
906
907 - def __init__(self, repourl=None, baseURL=None, defaultBranch=None,
908 branchType='dirname', clobberOnBranchChange=True, **kwargs):
909 """
910 @type repourl: string
911 @param repourl: the URL which points at the Mercurial repository.
912 This uses the 'default' branch unless defaultBranch is
913 specified below and the C{branchType} is set to
914 'inrepo'. It is an error to specify a branch without
915 setting the C{branchType} to 'inrepo'.
916
917 @param baseURL: if 'dirname' branches are enabled, this is the base URL
918 to which a branch name will be appended. It should
919 probably end in a slash. Use exactly one of C{repourl}
920 and C{baseURL}.
921
922 @param defaultBranch: if branches are enabled, this is the branch
923 to use if the Build does not specify one
924 explicitly.
925 For 'dirname' branches, It will simply be
926 appended to C{baseURL} and the result handed to
927 the 'hg update' command.
928 For 'inrepo' branches, this specifies the named
929 revision to which the tree will update after a
930 clone.
931
932 @param branchType: either 'dirname' or 'inrepo' depending on whether
933 the branch name should be appended to the C{baseURL}
934 or the branch is a mercurial named branch and can be
935 found within the C{repourl}
936
937 @param clobberOnBranchChange: boolean, defaults to True. If set and
938 using inrepos branches, clobber the tree
939 at each branch change. Otherwise, just
940 update to the branch.
941 """
942 self.repourl = repourl
943 self.baseURL = baseURL
944 self.branch = defaultBranch
945 self.branchType = branchType
946 self.clobberOnBranchChange = clobberOnBranchChange
947 Source.__init__(self, **kwargs)
948 self.addFactoryArguments(repourl=repourl,
949 baseURL=baseURL,
950 defaultBranch=defaultBranch,
951 branchType=branchType,
952 clobberOnBranchChange=clobberOnBranchChange,
953 )
954 if (not repourl and not baseURL) or (repourl and baseURL):
955 raise ValueError("you must provide exactly one of repourl and"
956 " baseURL")
957
958 - def startVC(self, branch, revision, patch):
959 slavever = self.slaveVersion("hg")
960 if not slavever:
961 raise BuildSlaveTooOldError("slave is too old, does not know "
962 "about hg")
963
964 if self.repourl:
965
966 assert self.branchType == 'inrepo' or not branch
967 self.args['repourl'] = self.repourl
968 if branch:
969 self.args['branch'] = branch
970 else:
971 self.args['repourl'] = self.baseURL + (branch or '')
972 self.args['revision'] = revision
973 self.args['patch'] = patch
974 self.args['clobberOnBranchChange'] = self.clobberOnBranchChange
975 self.args['branchType'] = self.branchType
976
977 revstuff = []
978 if branch is not None and branch != self.branch:
979 revstuff.append("[branch]")
980 self.description.extend(revstuff)
981 self.descriptionDone.extend(revstuff)
982
983 cmd = LoggedRemoteCommand("hg", self.args)
984 self.startCommand(cmd)
985
987 if not changes:
988 return None
989
990
991
992
993 if len(changes) > 1:
994 log.msg("Mercurial.computeSourceRevision: warning: "
995 "there are %d changes here, assuming the last one is "
996 "the most recent" % len(changes))
997 return changes[-1].revision
998
999
1001 """ P4 is a class for accessing perforce revision control"""
1002 name = "p4"
1003
1004 - def __init__(self, p4base, defaultBranch=None, p4port=None, p4user=None,
1005 p4passwd=None, p4extra_views=[],
1006 p4client='buildbot_%(slave)s_%(builder)s', **kwargs):
1007 """
1008 @type p4base: string
1009 @param p4base: A view into a perforce depot, typically
1010 "//depot/proj/"
1011
1012 @type defaultBranch: string
1013 @param defaultBranch: Identify a branch to build by default. Perforce
1014 is a view based branching system. So, the branch
1015 is normally the name after the base. For example,
1016 branch=1.0 is view=//depot/proj/1.0/...
1017 branch=1.1 is view=//depot/proj/1.1/...
1018
1019 @type p4port: string
1020 @param p4port: Specify the perforce server to connection in the format
1021 <host>:<port>. Example "perforce.example.com:1666"
1022
1023 @type p4user: string
1024 @param p4user: The perforce user to run the command as.
1025
1026 @type p4passwd: string
1027 @param p4passwd: The password for the perforce user.
1028
1029 @type p4extra_views: list of tuples
1030 @param p4extra_views: Extra views to be added to
1031 the client that is being used.
1032
1033 @type p4client: string
1034 @param p4client: The perforce client to use for this buildslave.
1035 """
1036
1037 self.branch = defaultBranch
1038 Source.__init__(self, **kwargs)
1039 self.addFactoryArguments(p4base=p4base,
1040 defaultBranch=defaultBranch,
1041 p4port=p4port,
1042 p4user=p4user,
1043 p4passwd=p4passwd,
1044 p4extra_views=p4extra_views,
1045 p4client=p4client,
1046 )
1047 self.args['p4port'] = p4port
1048 self.args['p4user'] = p4user
1049 self.args['p4passwd'] = p4passwd
1050 self.args['p4base'] = p4base
1051 self.args['p4extra_views'] = p4extra_views
1052 self.p4client = p4client
1053
1060
1062 if not changes:
1063 return None
1064 lastChange = max([int(c.revision) for c in changes])
1065 return lastChange
1066
1067 - def startVC(self, branch, revision, patch):
1076
1078 """This is a partial solution for using a P4 source repository. You are
1079 required to manually set up each build slave with a useful P4
1080 environment, which means setting various per-slave environment variables,
1081 and creating a P4 client specification which maps the right files into
1082 the slave's working directory. Once you have done that, this step merely
1083 performs a 'p4 sync' to update that workspace with the newest files.
1084
1085 Each slave needs the following environment:
1086
1087 - PATH: the 'p4' binary must be on the slave's PATH
1088 - P4USER: each slave needs a distinct user account
1089 - P4CLIENT: each slave needs a distinct client specification
1090
1091 You should use 'p4 client' (?) to set up a client view spec which maps
1092 the desired files into $SLAVEBASE/$BUILDERBASE/source .
1093 """
1094
1095 name = "p4sync"
1096
1097 - def __init__(self, p4port, p4user, p4passwd, p4client, **kwargs):
1098 assert kwargs['mode'] == "copy", "P4Sync can only be used in mode=copy"
1099 self.branch = None
1100 Source.__init__(self, **kwargs)
1101 self.addFactoryArguments(p4port=p4port,
1102 p4user=p4user,
1103 p4passwd=p4passwd,
1104 p4client=p4client,
1105 )
1106 self.args['p4port'] = p4port
1107 self.args['p4user'] = p4user
1108 self.args['p4passwd'] = p4passwd
1109 self.args['p4client'] = p4client
1110
1112 if not changes:
1113 return None
1114 lastChange = max([int(c.revision) for c in changes])
1115 return lastChange
1116
1117 - def startVC(self, branch, revision, patch):
1122
1124 """Check out a revision from a monotone server at 'server_addr',
1125 branch 'branch'. 'revision' specifies which revision id to check
1126 out.
1127
1128 This step will first create a local database, if necessary, and then pull
1129 the contents of the server into the database. Then it will do the
1130 checkout/update from this database."""
1131
1132 name = "monotone"
1133
1134 - def __init__(self, server_addr, branch, db_path="monotone.db",
1135 monotone="monotone",
1136 **kwargs):
1147
1152
1158