1 # -*- test-case-name: buildbot.test.test_vc -*-
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
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.
19 # if the checkout fails, there's no point in doing anything else
23 branch
= None # the default branch, should be set in __init__
25 def __init__(self
, workdir
, mode
='update', alwaysUseLatest
=False,
26 timeout
=20*60, retry
=None, **kwargs
):
29 @param workdir: local directory (relative to the Builder's root)
30 where the tree should be placed
33 @param mode: the kind of VC operation that is desired:
34 - 'update': specifies that the checkout/update should be
35 performed directly into the workdir. Each build is performed
36 in the same directory, allowing for incremental builds. This
37 minimizes disk space, bandwidth, and CPU time. However, it
38 may encounter problems if the build process does not handle
39 dependencies properly (if you must sometimes do a 'clean
40 build' to make sure everything gets compiled), or if source
41 files are deleted but generated files can influence test
42 behavior (e.g. python's .pyc files), or when source
43 directories are deleted but generated files prevent CVS from
46 - 'copy': specifies that the source-controlled workspace
47 should be maintained in a separate directory (called the
48 'copydir'), using checkout or update as necessary. For each
49 build, a new workdir is created with a copy of the source
50 tree (rm -rf workdir; cp -r copydir workdir). This doubles
51 the disk space required, but keeps the bandwidth low
52 (update instead of a full checkout). A full 'clean' build
53 is performed each time. This avoids any generated-file
54 build problems, but is still occasionally vulnerable to
55 problems such as a CVS repository being manually rearranged
56 (causing CVS errors on update) which are not an issue with
59 - 'clobber': specifies that the working directory should be
60 deleted each time, necessitating a full checkout for each
61 build. This insures a clean build off a complete checkout,
62 avoiding any of the problems described above, but is
63 bandwidth intensive, as the whole source tree must be
64 pulled down for each build.
66 - 'export': is like 'clobber', except that e.g. the 'cvs
67 export' command is used to create the working directory.
68 This command removes all VC metadata files (the
69 CVS/.svn/{arch} directories) from the tree, which is
70 sometimes useful for creating source tarballs (to avoid
71 including the metadata in the tar file). Not all VC systems
74 @type alwaysUseLatest: boolean
75 @param alwaysUseLatest: whether to always update to the most
76 recent available sources for this build.
78 Normally the Source step asks its Build for a list of all
79 Changes that are supposed to go into the build, then computes a
80 'source stamp' (revision number or timestamp) that will cause
81 exactly that set of changes to be present in the checked out
82 tree. This is turned into, e.g., 'cvs update -D timestamp', or
83 'svn update -r revnum'. If alwaysUseLatest=True, bypass this
84 computation and always update to the latest available sources
87 The source stamp helps avoid a race condition in which someone
88 commits a change after the master has decided to start a build
89 but before the slave finishes checking out the sources. At best
90 this results in a build which contains more changes than the
91 buildmaster thinks it has (possibly resulting in the wrong
92 person taking the blame for any problems that result), at worst
93 is can result in an incoherent set of sources (splitting a
94 non-atomic commit) which may not build at all.
96 @type retry: tuple of ints (delay, repeats) (or None)
97 @param retry: if provided, VC update failures are re-attempted up
98 to REPEATS times, with DELAY seconds between each
99 attempt. Some users have slaves with poor connectivity
100 to their VC repository, and they say that up to 80% of
101 their build failures are due to transient network
102 failures that could be handled by simply retrying a
107 LoggingBuildStep
.__init
__(self
, **kwargs
)
109 assert mode
in ("update", "copy", "clobber", "export")
111 delay
, repeats
= retry
112 assert isinstance(repeats
, int)
114 self
.args
= {'mode': mode
,
118 'patch': None, # set during .start
120 self
.alwaysUseLatest
= alwaysUseLatest
122 # Compute defaults for descriptions:
123 description
= ["updating"]
124 descriptionDone
= ["update"]
125 if mode
== "clobber":
126 description
= ["checkout"]
127 # because checkingouting takes too much space
128 descriptionDone
= ["checkout"]
129 elif mode
== "export":
130 description
= ["exporting"]
131 descriptionDone
= ["export"]
132 self
.description
= description
133 self
.descriptionDone
= descriptionDone
135 def describe(self
, done
=False):
137 return self
.descriptionDone
138 return self
.description
140 def computeSourceRevision(self
, changes
):
141 """Each subclass must implement this method to do something more
142 precise than -rHEAD every time. For version control systems that use
143 repository-wide change numbers (SVN, P4), this can simply take the
144 maximum such number from all the changes involved in this build. For
145 systems that do not (CVS), it needs to create a timestamp based upon
146 the latest Change, the Build's treeStableTimer, and an optional
147 self.checkoutDelay value."""
152 log
.msg("faking %s checkout/update" % self
.name
)
153 self
.step_status
.setColor("green")
154 self
.step_status
.setText(["fake", self
.name
, "successful"])
155 self
.addCompleteLog("log",
156 "Faked %s checkout/update 'successful'\n" \
160 # what source stamp would this build like to use?
161 s
= self
.build
.getSourceStamp()
162 # if branch is None, then use the Step's "default" branch
163 branch
= s
.branch
or self
.branch
164 # if revision is None, use the latest sources (-rHEAD)
165 revision
= s
.revision
166 if not revision
and not self
.alwaysUseLatest
:
167 revision
= self
.computeSourceRevision(s
.changes
)
168 # if patch is None, then do not patch the tree after checkout
170 # 'patch' is None or a tuple of (patchlevel, diff)
173 self
.addCompleteLog("patch", patch
[1])
175 self
.startVC(branch
, revision
, patch
)
177 def commandComplete(self
, cmd
):
179 if cmd
.updates
.has_key("got_revision"):
180 got_revision
= cmd
.updates
["got_revision"][-1]
181 self
.setProperty("got_revision", got_revision
)
186 """I do CVS checkout/update operations.
188 Note: if you are doing anonymous/pserver CVS operations, you will need
189 to manually do a 'cvs login' on each buildslave before the slave has any
190 hope of success. XXX: fix then, take a cvs password as an argument and
191 figure out how to do a 'cvs login' on each build
196 #progressMetrics = ('output',)
198 # additional things to track: update gives one stderr line per directory
199 # (starting with 'cvs server: Updating ') (and is fairly stable if files
200 # is empty), export gives one line per directory (starting with 'cvs
201 # export: Updating ') and another line per file (starting with U). Would
202 # be nice to track these, requires grepping LogFile data for lines,
203 # parsing each line. Might be handy to have a hook in LogFile that gets
204 # called with each complete line.
206 def __init__(self
, cvsroot
, cvsmodule
,
207 global_options
=[], branch
=None, checkoutDelay
=None,
209 clobber
=0, export
=0, copydir
=None,
213 @type cvsroot: string
214 @param cvsroot: CVS Repository from which the source tree should
215 be obtained. '/home/warner/Repository' for local
216 or NFS-reachable repositories,
217 ':pserver:anon@foo.com:/cvs' for anonymous CVS,
218 'user@host.com:/cvs' for non-anonymous CVS or
219 CVS over ssh. Lots of possibilities, check the
220 CVS documentation for more.
222 @type cvsmodule: string
223 @param cvsmodule: subdirectory of CVS repository that should be
226 @type login: string or None
227 @param login: if not None, a string which will be provided as a
228 password to the 'cvs login' command, used when a
229 :pserver: method is used to access the repository.
230 This login is only needed once, but must be run
231 each time (just before the CVS operation) because
232 there is no way for the buildslave to tell whether
233 it was previously performed or not.
236 @param branch: the default branch name, will be used in a '-r'
237 argument to specify which branch of the source tree
238 should be used for this checkout. Defaults to None,
239 which means to use 'HEAD'.
241 @type checkoutDelay: int or None
242 @param checkoutDelay: if not None, the number of seconds to put
243 between the last known Change and the
244 timestamp given to the -D argument. This
245 defaults to exactly half of the parent
246 Build's .treeStableTimer, but it could be
247 set to something else if your CVS change
248 notification has particularly weird
249 latency characteristics.
251 @type global_options: list of strings
252 @param global_options: these arguments are inserted in the cvs
253 command line, before the
254 'checkout'/'update' command word. See
255 'cvs --help-options' for a list of what
256 may be accepted here. ['-r'] will make
257 the checked out files read only. ['-r',
258 '-R'] will also assume the repository is
259 read-only (I assume this means it won't
260 use locks to insure atomic access to the
263 self
.checkoutDelay
= checkoutDelay
266 if not kwargs
.has_key('mode') and (clobber
or export
or copydir
):
267 # deal with old configs
268 warnings
.warn("Please use mode=, not clobber/export/copydir",
271 kwargs
['mode'] = "export"
273 kwargs
['mode'] = "clobber"
275 kwargs
['mode'] = "copy"
277 kwargs
['mode'] = "update"
279 Source
.__init
__(self
, **kwargs
)
281 self
.args
.update({'cvsroot': cvsroot
,
282 'cvsmodule': cvsmodule
,
283 'global_options': global_options
,
287 def computeSourceRevision(self
, changes
):
290 lastChange
= max([c
.when
for c
in changes
])
291 if self
.checkoutDelay
is not None:
292 when
= lastChange
+ self
.checkoutDelay
294 lastSubmit
= max([r
.submittedAt
for r
in self
.build
.requests
])
295 when
= (lastChange
+ lastSubmit
) / 2
296 return formatdate(when
)
298 def startVC(self
, branch
, revision
, patch
):
299 if self
.slaveVersionIsOlderThan("cvs", "1.39"):
300 # the slave doesn't know to avoid re-using the same sourcedir
301 # when the branch changes. We have no way of knowing which branch
302 # the last build used, so if we're using a non-default branch and
303 # either 'update' or 'copy' modes, it is safer to refuse to
304 # build, and tell the user they need to upgrade the buildslave.
305 if (branch
!= self
.branch
306 and self
.args
['mode'] in ("update", "copy")):
307 m
= ("This buildslave (%s) does not know about multiple "
308 "branches, and using mode=%s would probably build the "
310 "Refusing to build. Please upgrade the buildslave to "
311 "buildbot-0.7.0 or newer." % (self
.build
.slavename
,
314 raise BuildSlaveTooOldError(m
)
318 self
.args
['branch'] = branch
319 self
.args
['revision'] = revision
320 self
.args
['patch'] = patch
322 if self
.args
['branch'] == "HEAD" and self
.args
['revision']:
323 # special case. 'cvs update -r HEAD -D today' gives no files
324 # TODO: figure out why, see if it applies to -r BRANCH
325 self
.args
['branch'] = None
327 # deal with old slaves
329 slavever
= self
.slaveVersion("cvs", "old")
331 if slavever
== "old":
333 if self
.args
['mode'] == "export":
334 self
.args
['export'] = 1
335 elif self
.args
['mode'] == "clobber":
336 self
.args
['clobber'] = 1
337 elif self
.args
['mode'] == "copy":
338 self
.args
['copydir'] = "source"
339 self
.args
['tag'] = self
.args
['branch']
340 assert not self
.args
['patch'] # 0.5.0 slave can't do patch
342 cmd
= LoggedRemoteCommand("cvs", self
.args
)
343 self
.startCommand(cmd
, warnings
)
347 """I perform Subversion checkout/update operations."""
351 def __init__(self
, svnurl
=None, baseURL
=None, defaultBranch
=None,
352 directory
=None, **kwargs
):
355 @param svnurl: the URL which points to the Subversion server,
356 combining the access method (HTTP, ssh, local file),
357 the repository host/port, the repository path, the
358 sub-tree within the repository, and the branch to
359 check out. Using C{svnurl} does not enable builds of
360 alternate branches: use C{baseURL} to enable this.
361 Use exactly one of C{svnurl} and C{baseURL}.
363 @param baseURL: if branches are enabled, this is the base URL to
364 which a branch name will be appended. It should
365 probably end in a slash. Use exactly one of
366 C{svnurl} and C{baseURL}.
368 @param defaultBranch: if branches are enabled, this is the branch
369 to use if the Build does not specify one
370 explicitly. It will simply be appended
371 to C{baseURL} and the result handed to
375 if not kwargs
.has_key('workdir') and directory
is not None:
376 # deal with old configs
377 warnings
.warn("Please use workdir=, not directory=",
379 kwargs
['workdir'] = directory
382 self
.baseURL
= baseURL
383 self
.branch
= defaultBranch
385 Source
.__init
__(self
, **kwargs
)
387 if not svnurl
and not baseURL
:
388 raise ValueError("you must use exactly one of svnurl and baseURL")
391 def computeSourceRevision(self
, changes
):
394 lastChange
= max([int(c
.revision
) for c
in changes
])
397 def startVC(self
, branch
, revision
, patch
):
401 slavever
= self
.slaveVersion("svn", "old")
403 m
= "slave does not have the 'svn' command"
404 raise BuildSlaveTooOldError(m
)
406 if self
.slaveVersionIsOlderThan("svn", "1.39"):
407 # the slave doesn't know to avoid re-using the same sourcedir
408 # when the branch changes. We have no way of knowing which branch
409 # the last build used, so if we're using a non-default branch and
410 # either 'update' or 'copy' modes, it is safer to refuse to
411 # build, and tell the user they need to upgrade the buildslave.
412 if (branch
!= self
.branch
413 and self
.args
['mode'] in ("update", "copy")):
414 m
= ("This buildslave (%s) does not know about multiple "
415 "branches, and using mode=%s would probably build the "
417 "Refusing to build. Please upgrade the buildslave to "
418 "buildbot-0.7.0 or newer." % (self
.build
.slavename
,
420 raise BuildSlaveTooOldError(m
)
422 if slavever
== "old":
423 # 0.5.0 compatibility
424 if self
.args
['mode'] in ("clobber", "copy"):
425 # TODO: use some shell commands to make up for the
426 # deficiency, by blowing away the old directory first (thus
427 # forcing a full checkout)
428 warnings
.append("WARNING: this slave can only do SVN updates"
429 ", not mode=%s\n" % self
.args
['mode'])
430 log
.msg("WARNING: this slave only does mode=update")
431 if self
.args
['mode'] == "export":
432 raise BuildSlaveTooOldError("old slave does not have "
434 self
.args
['directory'] = self
.args
['workdir']
435 if revision
is not None:
436 # 0.5.0 can only do HEAD. We have no way of knowing whether
437 # the requested revision is HEAD or not, and for
438 # slowly-changing trees this will probably do the right
439 # thing, so let it pass with a warning
440 m
= ("WARNING: old slave can only update to HEAD, not "
441 "revision=%s" % revision
)
443 warnings
.append(m
+ "\n")
444 revision
= "HEAD" # interprets this key differently
446 raise BuildSlaveTooOldError("old slave can't do patch")
449 assert not branch
# we need baseURL= to use branches
450 self
.args
['svnurl'] = self
.svnurl
452 self
.args
['svnurl'] = self
.baseURL
+ branch
453 self
.args
['revision'] = revision
454 self
.args
['patch'] = patch
457 if branch
is not None and branch
!= self
.branch
:
458 revstuff
.append("[branch]")
459 if revision
is not None:
460 revstuff
.append("r%s" % revision
)
461 self
.description
.extend(revstuff
)
462 self
.descriptionDone
.extend(revstuff
)
464 cmd
= LoggedRemoteCommand("svn", self
.args
)
465 self
.startCommand(cmd
, warnings
)
469 """Check out a source tree from a Darcs repository at 'repourl'.
471 To the best of my knowledge, Darcs has no concept of file modes. This
472 means the eXecute-bit will be cleared on all source files. As a result,
473 you may need to invoke configuration scripts with something like:
475 C{s(step.Configure, command=['/bin/sh', './configure'])}
480 def __init__(self
, repourl
=None, baseURL
=None, defaultBranch
=None,
483 @type repourl: string
484 @param repourl: the URL which points at the Darcs repository. This
485 is used as the default branch. Using C{repourl} does
486 not enable builds of alternate branches: use
487 C{baseURL} to enable this. Use either C{repourl} or
488 C{baseURL}, not both.
490 @param baseURL: if branches are enabled, this is the base URL to
491 which a branch name will be appended. It should
492 probably end in a slash. Use exactly one of
493 C{repourl} and C{baseURL}.
495 @param defaultBranch: if branches are enabled, this is the branch
496 to use if the Build does not specify one
497 explicitly. It will simply be appended to
498 C{baseURL} and the result handed to the
499 'darcs pull' command.
501 self
.repourl
= repourl
502 self
.baseURL
= baseURL
503 self
.branch
= defaultBranch
504 Source
.__init
__(self
, **kwargs
)
505 assert kwargs
['mode'] != "export", \
506 "Darcs does not have an 'export' mode"
507 if (not repourl
and not baseURL
) or (repourl
and baseURL
):
508 raise ValueError("you must provide exactly one of repourl and"
511 def startVC(self
, branch
, revision
, patch
):
512 slavever
= self
.slaveVersion("darcs")
514 m
= "slave is too old, does not know about darcs"
515 raise BuildSlaveTooOldError(m
)
517 if self
.slaveVersionIsOlderThan("darcs", "1.39"):
519 # TODO: revisit this once we implement computeSourceRevision
520 m
= "0.6.6 slaves can't handle args['revision']"
521 raise BuildSlaveTooOldError(m
)
523 # the slave doesn't know to avoid re-using the same sourcedir
524 # when the branch changes. We have no way of knowing which branch
525 # the last build used, so if we're using a non-default branch and
526 # either 'update' or 'copy' modes, it is safer to refuse to
527 # build, and tell the user they need to upgrade the buildslave.
528 if (branch
!= self
.branch
529 and self
.args
['mode'] in ("update", "copy")):
530 m
= ("This buildslave (%s) does not know about multiple "
531 "branches, and using mode=%s would probably build the "
533 "Refusing to build. Please upgrade the buildslave to "
534 "buildbot-0.7.0 or newer." % (self
.build
.slavename
,
536 raise BuildSlaveTooOldError(m
)
539 assert not branch
# we need baseURL= to use branches
540 self
.args
['repourl'] = self
.repourl
542 self
.args
['repourl'] = self
.baseURL
+ branch
543 self
.args
['revision'] = revision
544 self
.args
['patch'] = patch
547 if branch
is not None and branch
!= self
.branch
:
548 revstuff
.append("[branch]")
549 self
.description
.extend(revstuff
)
550 self
.descriptionDone
.extend(revstuff
)
552 cmd
= LoggedRemoteCommand("darcs", self
.args
)
553 self
.startCommand(cmd
)
557 """Check out a source tree from a git repository 'repourl'."""
561 def __init__(self
, repourl
, **kwargs
):
563 @type repourl: string
564 @param repourl: the URL which points at the git repository
566 self
.branch
= None # TODO
567 Source
.__init
__(self
, **kwargs
)
568 self
.args
['repourl'] = repourl
570 def startVC(self
, branch
, revision
, patch
):
571 self
.args
['branch'] = branch
572 self
.args
['revision'] = revision
573 self
.args
['patch'] = patch
574 slavever
= self
.slaveVersion("git")
576 raise BuildSlaveTooOldError("slave is too old, does not know "
578 cmd
= LoggedRemoteCommand("git", self
.args
)
579 self
.startCommand(cmd
)
583 """Check out a source tree from an Arch repository named 'archive'
584 available at 'url'. 'version' specifies which version number (development
585 line) will be used for the checkout: this is mostly equivalent to a
586 branch name. This version uses the 'tla' tool to do the checkout, to use
587 'baz' see L{Bazaar} instead.
591 # TODO: slaves >0.6.6 will accept args['build-config'], so use it
593 def __init__(self
, url
, version
, archive
=None, **kwargs
):
596 @param url: the Arch coordinates of the repository. This is
597 typically an http:// URL, but could also be the absolute
598 pathname of a local directory instead.
600 @type version: string
601 @param version: the category--branch--version to check out. This is
602 the default branch. If a build specifies a different
603 branch, it will be used instead of this.
605 @type archive: string
606 @param archive: The archive name. If provided, it must match the one
607 that comes from the repository. If not, the
608 repository's default will be used.
610 self
.branch
= version
611 Source
.__init
__(self
, **kwargs
)
612 self
.args
.update({'url': url
,
616 def computeSourceRevision(self
, changes
):
617 # in Arch, fully-qualified revision numbers look like:
618 # arch@buildbot.sourceforge.net--2004/buildbot--dev--0--patch-104
619 # For any given builder, all of this is fixed except the patch-104.
620 # The Change might have any part of the fully-qualified string, so we
621 # just look for the last part. We return the "patch-NN" string.
628 if c
.revision
.endswith("--base-0"):
631 i
= c
.revision
.rindex("patch")
632 rev
= int(c
.revision
[i
+len("patch-"):])
633 lastChange
= max(lastChange
, rev
)
634 if lastChange
is None:
638 return "patch-%d" % lastChange
640 def checkSlaveVersion(self
, cmd
, branch
):
642 slavever
= self
.slaveVersion(cmd
)
644 m
= "slave is too old, does not know about %s" % cmd
645 raise BuildSlaveTooOldError(m
)
647 # slave 1.28 and later understand 'revision'
648 if self
.slaveVersionIsOlderThan(cmd
, "1.28"):
649 if not self
.alwaysUseLatest
:
650 # we don't know whether our requested revision is the latest
651 # or not. If the tree does not change very quickly, this will
652 # probably build the right thing, so emit a warning rather
653 # than refuse to build at all
654 m
= "WARNING, buildslave is too old to use a revision"
656 warnings
.append(m
+ "\n")
658 if self
.slaveVersionIsOlderThan(cmd
, "1.39"):
659 # the slave doesn't know to avoid re-using the same sourcedir
660 # when the branch changes. We have no way of knowing which branch
661 # the last build used, so if we're using a non-default branch and
662 # either 'update' or 'copy' modes, it is safer to refuse to
663 # build, and tell the user they need to upgrade the buildslave.
664 if (branch
!= self
.branch
665 and self
.args
['mode'] in ("update", "copy")):
666 m
= ("This buildslave (%s) does not know about multiple "
667 "branches, and using mode=%s would probably build the "
669 "Refusing to build. Please upgrade the buildslave to "
670 "buildbot-0.7.0 or newer." % (self
.build
.slavename
,
673 raise BuildSlaveTooOldError(m
)
677 def startVC(self
, branch
, revision
, patch
):
678 self
.args
['version'] = branch
679 self
.args
['revision'] = revision
680 self
.args
['patch'] = patch
681 warnings
= self
.checkSlaveVersion("arch", branch
)
684 if branch
is not None and branch
!= self
.branch
:
685 revstuff
.append("[branch]")
686 if revision
is not None:
687 revstuff
.append("patch%s" % revision
)
688 self
.description
.extend(revstuff
)
689 self
.descriptionDone
.extend(revstuff
)
691 cmd
= LoggedRemoteCommand("arch", self
.args
)
692 self
.startCommand(cmd
, warnings
)
696 """Bazaar is an alternative client for Arch repositories. baz is mostly
697 compatible with tla, but archive registration is slightly different."""
699 # TODO: slaves >0.6.6 will accept args['build-config'], so use it
701 def __init__(self
, url
, version
, archive
, **kwargs
):
704 @param url: the Arch coordinates of the repository. This is
705 typically an http:// URL, but could also be the absolute
706 pathname of a local directory instead.
708 @type version: string
709 @param version: the category--branch--version to check out
711 @type archive: string
712 @param archive: The archive name (required). This must always match
713 the one that comes from the repository, otherwise the
714 buildslave will attempt to get sources from the wrong
717 self
.branch
= version
718 Source
.__init
__(self
, **kwargs
)
719 self
.args
.update({'url': url
,
723 def startVC(self
, branch
, revision
, patch
):
724 self
.args
['version'] = branch
725 self
.args
['revision'] = revision
726 self
.args
['patch'] = patch
727 warnings
= self
.checkSlaveVersion("bazaar", branch
)
730 if branch
is not None and branch
!= self
.branch
:
731 revstuff
.append("[branch]")
732 if revision
is not None:
733 revstuff
.append("patch%s" % revision
)
734 self
.description
.extend(revstuff
)
735 self
.descriptionDone
.extend(revstuff
)
737 cmd
= LoggedRemoteCommand("bazaar", self
.args
)
738 self
.startCommand(cmd
, warnings
)
741 """Check out a source tree from a bzr (Bazaar) repository at 'repourl'.
747 def __init__(self
, repourl
=None, baseURL
=None, defaultBranch
=None,
750 @type repourl: string
751 @param repourl: the URL which points at the bzr repository. This
752 is used as the default branch. Using C{repourl} does
753 not enable builds of alternate branches: use
754 C{baseURL} to enable this. Use either C{repourl} or
755 C{baseURL}, not both.
757 @param baseURL: if branches are enabled, this is the base URL to
758 which a branch name will be appended. It should
759 probably end in a slash. Use exactly one of
760 C{repourl} and C{baseURL}.
762 @param defaultBranch: if branches are enabled, this is the branch
763 to use if the Build does not specify one
764 explicitly. It will simply be appended to
765 C{baseURL} and the result handed to the
766 'bzr checkout pull' command.
768 self
.repourl
= repourl
769 self
.baseURL
= baseURL
770 self
.branch
= defaultBranch
771 Source
.__init
__(self
, **kwargs
)
772 if (not repourl
and not baseURL
) or (repourl
and baseURL
):
773 raise ValueError("you must provide exactly one of repourl and"
776 def computeSourceRevision(self
, changes
):
779 lastChange
= max([int(c
.revision
) for c
in changes
])
782 def startVC(self
, branch
, revision
, patch
):
783 slavever
= self
.slaveVersion("bzr")
785 m
= "slave is too old, does not know about bzr"
786 raise BuildSlaveTooOldError(m
)
789 assert not branch
# we need baseURL= to use branches
790 self
.args
['repourl'] = self
.repourl
792 self
.args
['repourl'] = self
.baseURL
+ branch
793 self
.args
['revision'] = revision
794 self
.args
['patch'] = patch
797 if branch
is not None and branch
!= self
.branch
:
798 revstuff
.append("[branch]")
799 self
.description
.extend(revstuff
)
800 self
.descriptionDone
.extend(revstuff
)
802 cmd
= LoggedRemoteCommand("bzr", self
.args
)
803 self
.startCommand(cmd
)
806 class Mercurial(Source
):
807 """Check out a source tree from a mercurial repository 'repourl'."""
811 def __init__(self
, repourl
=None, baseURL
=None, defaultBranch
=None,
814 @type repourl: string
815 @param repourl: the URL which points at the Mercurial repository.
816 This is used as the default branch. Using C{repourl}
817 does not enable builds of alternate branches: use
818 C{baseURL} to enable this. Use either C{repourl} or
819 C{baseURL}, not both.
821 @param baseURL: if branches are enabled, this is the base URL to
822 which a branch name will be appended. It should
823 probably end in a slash. Use exactly one of
824 C{repourl} and C{baseURL}.
826 @param defaultBranch: if branches are enabled, this is the branch
827 to use if the Build does not specify one
828 explicitly. It will simply be appended to
829 C{baseURL} and the result handed to the
832 self
.repourl
= repourl
833 self
.baseURL
= baseURL
834 self
.branch
= defaultBranch
835 Source
.__init
__(self
, **kwargs
)
836 if (not repourl
and not baseURL
) or (repourl
and baseURL
):
837 raise ValueError("you must provide exactly one of repourl and"
840 def startVC(self
, branch
, revision
, patch
):
841 slavever
= self
.slaveVersion("hg")
843 raise BuildSlaveTooOldError("slave is too old, does not know "
847 assert not branch
# we need baseURL= to use branches
848 self
.args
['repourl'] = self
.repourl
850 self
.args
['repourl'] = self
.baseURL
+ branch
851 self
.args
['revision'] = revision
852 self
.args
['patch'] = patch
855 if branch
is not None and branch
!= self
.branch
:
856 revstuff
.append("[branch]")
857 self
.description
.extend(revstuff
)
858 self
.descriptionDone
.extend(revstuff
)
860 cmd
= LoggedRemoteCommand("hg", self
.args
)
861 self
.startCommand(cmd
)
865 """ P4 is a class for accessing perforce revision control"""
868 def __init__(self
, p4base
, defaultBranch
=None, p4port
=None, p4user
=None,
869 p4passwd
=None, p4extra_views
=[],
870 p4client
='buildbot_%(slave)s_%(builder)s', **kwargs
):
873 @param p4base: A view into a perforce depot, typically
876 @type defaultBranch: string
877 @param defaultBranch: Identify a branch to build by default. Perforce
878 is a view based branching system. So, the branch
879 is normally the name after the base. For example,
880 branch=1.0 is view=//depot/proj/1.0/...
881 branch=1.1 is view=//depot/proj/1.1/...
884 @param p4port: Specify the perforce server to connection in the format
885 <host>:<port>. Example "perforce.example.com:1666"
888 @param p4user: The perforce user to run the command as.
890 @type p4passwd: string
891 @param p4passwd: The password for the perforce user.
893 @type p4extra_views: list of tuples
894 @param p4extra_views: Extra views to be added to
895 the client that is being used.
897 @type p4client: string
898 @param p4client: The perforce client to use for this buildslave.
901 self
.branch
= defaultBranch
902 Source
.__init
__(self
, **kwargs
)
903 self
.args
['p4port'] = p4port
904 self
.args
['p4user'] = p4user
905 self
.args
['p4passwd'] = p4passwd
906 self
.args
['p4base'] = p4base
907 self
.args
['p4extra_views'] = p4extra_views
908 self
.args
['p4client'] = p4client
% {
909 'slave': self
.build
.slavename
,
910 'builder': self
.build
.builder
.name
,
913 def computeSourceRevision(self
, changes
):
916 lastChange
= max([int(c
.revision
) for c
in changes
])
919 def startVC(self
, branch
, revision
, patch
):
920 slavever
= self
.slaveVersion("p4")
921 assert slavever
, "slave is too old, does not know about p4"
922 args
= dict(self
.args
)
923 args
['branch'] = branch
or self
.branch
924 args
['revision'] = revision
925 args
['patch'] = patch
926 cmd
= LoggedRemoteCommand("p4", args
)
927 self
.startCommand(cmd
)
929 class P4Sync(Source
):
930 """This is a partial solution for using a P4 source repository. You are
931 required to manually set up each build slave with a useful P4
932 environment, which means setting various per-slave environment variables,
933 and creating a P4 client specification which maps the right files into
934 the slave's working directory. Once you have done that, this step merely
935 performs a 'p4 sync' to update that workspace with the newest files.
937 Each slave needs the following environment:
939 - PATH: the 'p4' binary must be on the slave's PATH
940 - P4USER: each slave needs a distinct user account
941 - P4CLIENT: each slave needs a distinct client specification
943 You should use 'p4 client' (?) to set up a client view spec which maps
944 the desired files into $SLAVEBASE/$BUILDERBASE/source .
949 def __init__(self
, p4port
, p4user
, p4passwd
, p4client
, **kwargs
):
950 assert kwargs
['mode'] == "copy", "P4Sync can only be used in mode=copy"
952 Source
.__init
__(self
, **kwargs
)
953 self
.args
['p4port'] = p4port
954 self
.args
['p4user'] = p4user
955 self
.args
['p4passwd'] = p4passwd
956 self
.args
['p4client'] = p4client
958 def computeSourceRevision(self
, changes
):
961 lastChange
= max([int(c
.revision
) for c
in changes
])
964 def startVC(self
, branch
, revision
, patch
):
965 slavever
= self
.slaveVersion("p4sync")
966 assert slavever
, "slave is too old, does not know about p4"
967 cmd
= LoggedRemoteCommand("p4sync", self
.args
)
968 self
.startCommand(cmd
)
970 class Monotone(Source
):
971 """Check out a revision from a monotone server at 'server_addr',
972 branch 'branch'. 'revision' specifies which revision id to check
975 This step will first create a local database, if necessary, and then pull
976 the contents of the server into the database. Then it will do the
977 checkout/update from this database."""
981 def __init__(self
, server_addr
, branch
, db_path
="monotone.db",
984 Source
.__init
__(self
, **kwargs
)
985 self
.args
.update({"server_addr": server_addr
,
988 "monotone": monotone
})
990 def computeSourceRevision(self
, changes
):
993 return changes
[-1].revision
996 slavever
= self
.slaveVersion("monotone")
997 assert slavever
, "slave is too old, does not know about monotone"
998 cmd
= LoggedRemoteCommand("monotone", self
.args
)
999 self
.startCommand(cmd
)