Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(179)

Side by Side Diff: third_party/buildbot_7_12/buildbot/steps/source.py

Issue 12207158: Bye bye buildbot 0.7.12. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/build
Patch Set: Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 # -*- test-case-name: buildbot.test.test_vc -*-
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 # if the checkout fails, there's no point in doing anything else
20 haltOnFailure = True
21 flunkOnFailure = True
22 notReally = False
23
24 branch = None # the default branch, should be set in __init__
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. When used with a patched checkout, from a
46 previous buildbot try for instance, it will try to "revert"
47 the changes first and will do a clobber if it is unable to
48 get a clean checkout. The behavior is SCM-dependent.
49
50 - 'copy': specifies that the source-controlled workspace
51 should be maintained in a separate directory (called the
52 'copydir'), using checkout or update as necessary. For each
53 build, a new workdir is created with a copy of the source
54 tree (rm -rf workdir; cp -R -P -p copydir workdir). This
55 doubles the disk space required, but keeps the bandwidth low
56 (update instead of a full checkout). A full 'clean' build
57 is performed each time. This avoids any generated-file
58 build problems, but is still occasionally vulnerable to
59 problems such as a CVS repository being manually rearranged
60 (causing CVS errors on update) which are not an issue with
61 a full checkout.
62
63 - 'clobber': specifies that the working directory should be
64 deleted each time, necessitating a full checkout for each
65 build. This insures a clean build off a complete checkout,
66 avoiding any of the problems described above, but is
67 bandwidth intensive, as the whole source tree must be
68 pulled down for each build.
69
70 - 'export': is like 'clobber', except that e.g. the 'cvs
71 export' command is used to create the working directory.
72 This command removes all VC metadata files (the
73 CVS/.svn/{arch} directories) from the tree, which is
74 sometimes useful for creating source tarballs (to avoid
75 including the metadata in the tar file). Not all VC systems
76 support export.
77
78 @type alwaysUseLatest: boolean
79 @param alwaysUseLatest: whether to always update to the most
80 recent available sources for this build.
81
82 Normally the Source step asks its Build for a list of all
83 Changes that are supposed to go into the build, then computes a
84 'source stamp' (revision number or timestamp) that will cause
85 exactly that set of changes to be present in the checked out
86 tree. This is turned into, e.g., 'cvs update -D timestamp', or
87 'svn update -r revnum'. If alwaysUseLatest=True, bypass this
88 computation and always update to the latest available sources
89 for each build.
90
91 The source stamp helps avoid a race condition in which someone
92 commits a change after the master has decided to start a build
93 but before the slave finishes checking out the sources. At best
94 this results in a build which contains more changes than the
95 buildmaster thinks it has (possibly resulting in the wrong
96 person taking the blame for any problems that result), at worst
97 is can result in an incoherent set of sources (splitting a
98 non-atomic commit) which may not build at all.
99
100 @type retry: tuple of ints (delay, repeats) (or None)
101 @param retry: if provided, VC update failures are re-attempted up
102 to REPEATS times, with DELAY seconds between each
103 attempt. Some users have slaves with poor connectivity
104 to their VC repository, and they say that up to 80% of
105 their build failures are due to transient network
106 failures that could be handled by simply retrying a
107 couple times.
108
109 """
110
111 LoggingBuildStep.__init__(self, **kwargs)
112 self.addFactoryArguments(workdir=workdir,
113 mode=mode,
114 alwaysUseLatest=alwaysUseLatest,
115 timeout=timeout,
116 retry=retry,
117 )
118
119 assert mode in ("update", "copy", "clobber", "export")
120 if retry:
121 delay, repeats = retry
122 assert isinstance(repeats, int)
123 assert repeats > 0
124 self.args = {'mode': mode,
125 'workdir': workdir,
126 'timeout': timeout,
127 'retry': retry,
128 'patch': None, # set during .start
129 }
130 self.alwaysUseLatest = alwaysUseLatest
131
132 # Compute defaults for descriptions:
133 description = ["updating"]
134 descriptionDone = ["update"]
135 if mode == "clobber":
136 description = ["checkout"]
137 # because checkingouting takes too much space
138 descriptionDone = ["checkout"]
139 elif mode == "export":
140 description = ["exporting"]
141 descriptionDone = ["export"]
142 self.description = description
143 self.descriptionDone = descriptionDone
144
145 def setStepStatus(self, step_status):
146 LoggingBuildStep.setStepStatus(self, step_status)
147
148 # start doesn't set text soon enough to capture our description in
149 # the stepStarted status notification. Set text here so it's included.
150 self.step_status.setText(self.describe(False))
151
152 def setDefaultWorkdir(self, workdir):
153 self.args['workdir'] = self.args['workdir'] or workdir
154
155 def describe(self, done=False):
156 if done:
157 return self.descriptionDone
158 return self.description
159
160 def computeSourceRevision(self, changes):
161 """Each subclass must implement this method to do something more
162 precise than -rHEAD every time. For version control systems that use
163 repository-wide change numbers (SVN, P4), this can simply take the
164 maximum such number from all the changes involved in this build. For
165 systems that do not (CVS), it needs to create a timestamp based upon
166 the latest Change, the Build's treeStableTimer, and an optional
167 self.checkoutDelay value."""
168 return None
169
170 def start(self):
171 if self.notReally:
172 log.msg("faking %s checkout/update" % self.name)
173 self.step_status.setText(["fake", self.name, "successful"])
174 self.addCompleteLog("log",
175 "Faked %s checkout/update 'successful'\n" \
176 % self.name)
177 return SKIPPED
178
179 # what source stamp would this build like to use?
180 s = self.build.getSourceStamp()
181 # if branch is None, then use the Step's "default" branch
182 branch = s.branch or self.branch
183 # if revision is None, use the latest sources (-rHEAD)
184 revision = s.revision
185 if not revision and not self.alwaysUseLatest:
186 revision = self.computeSourceRevision(s.changes)
187 # if patch is None, then do not patch the tree after checkout
188
189 # 'patch' is None or a tuple of (patchlevel, diff, root)
190 # root is optional.
191 patch = s.patch
192 if patch:
193 self.addCompleteLog("patch", patch[1])
194
195 if self.alwaysUseLatest:
196 revision = None
197 self.startVC(branch, revision, patch)
198
199 def commandComplete(self, cmd):
200 if cmd.updates.has_key("got_revision"):
201 got_revision = cmd.updates["got_revision"][-1]
202 if got_revision is not None:
203 self.setProperty("got_revision", str(got_revision), "Source")
204
205
206
207 class CVS(Source):
208 """I do CVS checkout/update operations.
209
210 Note: if you are doing anonymous/pserver CVS operations, you will need
211 to manually do a 'cvs login' on each buildslave before the slave has any
212 hope of success. XXX: fix then, take a cvs password as an argument and
213 figure out how to do a 'cvs login' on each build
214 """
215
216 name = "cvs"
217
218 #progressMetrics = ('output',)
219 #
220 # additional things to track: update gives one stderr line per directory
221 # (starting with 'cvs server: Updating ') (and is fairly stable if files
222 # is empty), export gives one line per directory (starting with 'cvs
223 # export: Updating ') and another line per file (starting with U). Would
224 # be nice to track these, requires grepping LogFile data for lines,
225 # parsing each line. Might be handy to have a hook in LogFile that gets
226 # called with each complete line.
227
228 def __init__(self, cvsroot, cvsmodule,
229 global_options=[], branch=None, checkoutDelay=None,
230 checkout_options=[],
231 login=None,
232 **kwargs):
233
234 """
235 @type cvsroot: string
236 @param cvsroot: CVS Repository from which the source tree should
237 be obtained. '/home/warner/Repository' for local
238 or NFS-reachable repositories,
239 ':pserver:anon@foo.com:/cvs' for anonymous CVS,
240 'user@host.com:/cvs' for non-anonymous CVS or
241 CVS over ssh. Lots of possibilities, check the
242 CVS documentation for more.
243
244 @type cvsmodule: string
245 @param cvsmodule: subdirectory of CVS repository that should be
246 retrieved
247
248 @type login: string or None
249 @param login: if not None, a string which will be provided as a
250 password to the 'cvs login' command, used when a
251 :pserver: method is used to access the repository.
252 This login is only needed once, but must be run
253 each time (just before the CVS operation) because
254 there is no way for the buildslave to tell whether
255 it was previously performed or not.
256
257 @type branch: string
258 @param branch: the default branch name, will be used in a '-r'
259 argument to specify which branch of the source tree
260 should be used for this checkout. Defaults to None,
261 which means to use 'HEAD'.
262
263 @type checkoutDelay: int or None
264 @param checkoutDelay: if not None, the number of seconds to put
265 between the last known Change and the
266 timestamp given to the -D argument. This
267 defaults to exactly half of the parent
268 Build's .treeStableTimer, but it could be
269 set to something else if your CVS change
270 notification has particularly weird
271 latency characteristics.
272
273 @type global_options: list of strings
274 @param global_options: these arguments are inserted in the cvs
275 command line, before the
276 'checkout'/'update' command word. See
277 'cvs --help-options' for a list of what
278 may be accepted here. ['-r'] will make
279 the checked out files read only. ['-r',
280 '-R'] will also assume the repository is
281 read-only (I assume this means it won't
282 use locks to insure atomic access to the
283 ,v files).
284
285 @type checkout_options: list of strings
286 @param checkout_options: these arguments are inserted in the cvs
287 command line, after 'checkout' but before
288 branch or revision specifiers.
289 """
290
291 self.checkoutDelay = checkoutDelay
292 self.branch = branch
293
294 Source.__init__(self, **kwargs)
295 self.addFactoryArguments(cvsroot=cvsroot,
296 cvsmodule=cvsmodule,
297 global_options=global_options,
298 checkout_options=checkout_options,
299 branch=branch,
300 checkoutDelay=checkoutDelay,
301 login=login,
302 )
303
304 self.args.update({'cvsroot': cvsroot,
305 'cvsmodule': cvsmodule,
306 'global_options': global_options,
307 'checkout_options':checkout_options,
308 'login': login,
309 })
310
311 def computeSourceRevision(self, changes):
312 if not changes:
313 return None
314 lastChange = max([c.when for c in changes])
315 if self.checkoutDelay is not None:
316 when = lastChange + self.checkoutDelay
317 else:
318 lastSubmit = max([r.submittedAt for r in self.build.requests])
319 when = (lastChange + lastSubmit) / 2
320 return formatdate(when)
321
322 def startVC(self, branch, revision, patch):
323 if self.slaveVersionIsOlderThan("cvs", "1.39"):
324 # the slave doesn't know to avoid re-using the same sourcedir
325 # when the branch changes. We have no way of knowing which branch
326 # the last build used, so if we're using a non-default branch and
327 # either 'update' or 'copy' modes, it is safer to refuse to
328 # build, and tell the user they need to upgrade the buildslave.
329 if (branch != self.branch
330 and self.args['mode'] in ("update", "copy")):
331 m = ("This buildslave (%s) does not know about multiple "
332 "branches, and using mode=%s would probably build the "
333 "wrong tree. "
334 "Refusing to build. Please upgrade the buildslave to "
335 "buildbot-0.7.0 or newer." % (self.build.slavename,
336 self.args['mode']))
337 log.msg(m)
338 raise BuildSlaveTooOldError(m)
339
340 if branch is None:
341 branch = "HEAD"
342 self.args['branch'] = branch
343 self.args['revision'] = revision
344 self.args['patch'] = patch
345
346 if self.args['branch'] == "HEAD" and self.args['revision']:
347 # special case. 'cvs update -r HEAD -D today' gives no files
348 # TODO: figure out why, see if it applies to -r BRANCH
349 self.args['branch'] = None
350
351 # deal with old slaves
352 warnings = []
353 slavever = self.slaveVersion("cvs", "old")
354
355 if slavever == "old":
356 # 0.5.0
357 if self.args['mode'] == "export":
358 self.args['export'] = 1
359 elif self.args['mode'] == "clobber":
360 self.args['clobber'] = 1
361 elif self.args['mode'] == "copy":
362 self.args['copydir'] = "source"
363 self.args['tag'] = self.args['branch']
364 assert not self.args['patch'] # 0.5.0 slave can't do patch
365
366 cmd = LoggedRemoteCommand("cvs", self.args)
367 self.startCommand(cmd, warnings)
368
369
370 class SVN(Source):
371 """I perform Subversion checkout/update operations."""
372
373 name = 'svn'
374
375 def __init__(self, svnurl=None, baseURL=None, defaultBranch=None,
376 directory=None, username=None, password=None,
377 extra_args=None, keep_on_purge=None, ignore_ignores=None,
378 always_purge=None, depth=None, **kwargs):
379 """
380 @type svnurl: string
381 @param svnurl: the URL which points to the Subversion server,
382 combining the access method (HTTP, ssh, local file),
383 the repository host/port, the repository path, the
384 sub-tree within the repository, and the branch to
385 check out. Using C{svnurl} does not enable builds of
386 alternate branches: use C{baseURL} to enable this.
387 Use exactly one of C{svnurl} and C{baseURL}.
388
389 @param baseURL: if branches are enabled, this is the base URL to
390 which a branch name will be appended. It should
391 probably end in a slash. Use exactly one of
392 C{svnurl} and C{baseURL}.
393
394 @param defaultBranch: if branches are enabled, this is the branch
395 to use if the Build does not specify one
396 explicitly. It will simply be appended
397 to C{baseURL} and the result handed to
398 the SVN command.
399
400 @param username: username to pass to svn's --username
401 @param password: username to pass to svn's --password
402 """
403
404 if not kwargs.has_key('workdir') and directory is not None:
405 # deal with old configs
406 warn("Please use workdir=, not directory=", DeprecationWarning)
407 kwargs['workdir'] = directory
408
409 self.svnurl = svnurl
410 self.baseURL = baseURL
411 self.branch = defaultBranch
412 self.username = username
413 self.password = password
414 self.extra_args = extra_args
415 self.keep_on_purge = keep_on_purge
416 self.ignore_ignores = ignore_ignores
417 self.always_purge = always_purge
418 self.depth = depth
419
420 Source.__init__(self, **kwargs)
421 self.addFactoryArguments(svnurl=svnurl,
422 baseURL=baseURL,
423 defaultBranch=defaultBranch,
424 directory=directory,
425 username=username,
426 password=password,
427 extra_args=extra_args,
428 keep_on_purge=keep_on_purge,
429 ignore_ignores=ignore_ignores,
430 always_purge=always_purge,
431 depth=depth,
432 )
433
434 if not svnurl and not baseURL:
435 raise ValueError("you must use exactly one of svnurl and baseURL")
436
437
438 def computeSourceRevision(self, changes):
439 if not changes or None in [c.revision for c in changes]:
440 return None
441 lastChange = max([int(c.revision) for c in changes])
442 return lastChange
443
444 def startVC(self, branch, revision, patch):
445
446 # handle old slaves
447 warnings = []
448 slavever = self.slaveVersion("svn", "old")
449 if not slavever:
450 m = "slave does not have the 'svn' command"
451 raise BuildSlaveTooOldError(m)
452
453 if self.slaveVersionIsOlderThan("svn", "1.39"):
454 # the slave doesn't know to avoid re-using the same sourcedir
455 # when the branch changes. We have no way of knowing which branch
456 # the last build used, so if we're using a non-default branch and
457 # either 'update' or 'copy' modes, it is safer to refuse to
458 # build, and tell the user they need to upgrade the buildslave.
459 if (branch != self.branch
460 and self.args['mode'] in ("update", "copy")):
461 m = ("This buildslave (%s) does not know about multiple "
462 "branches, and using mode=%s would probably build the "
463 "wrong tree. "
464 "Refusing to build. Please upgrade the buildslave to "
465 "buildbot-0.7.0 or newer." % (self.build.slavename,
466 self.args['mode']))
467 raise BuildSlaveTooOldError(m)
468
469 if slavever == "old":
470 # 0.5.0 compatibility
471 if self.args['mode'] in ("clobber", "copy"):
472 # TODO: use some shell commands to make up for the
473 # deficiency, by blowing away the old directory first (thus
474 # forcing a full checkout)
475 warnings.append("WARNING: this slave can only do SVN updates"
476 ", not mode=%s\n" % self.args['mode'])
477 log.msg("WARNING: this slave only does mode=update")
478 if self.args['mode'] == "export":
479 raise BuildSlaveTooOldError("old slave does not have "
480 "mode=export")
481 self.args['directory'] = self.args['workdir']
482 if revision is not None:
483 # 0.5.0 can only do HEAD. We have no way of knowing whether
484 # the requested revision is HEAD or not, and for
485 # slowly-changing trees this will probably do the right
486 # thing, so let it pass with a warning
487 m = ("WARNING: old slave can only update to HEAD, not "
488 "revision=%s" % revision)
489 log.msg(m)
490 warnings.append(m + "\n")
491 revision = "HEAD" # interprets this key differently
492 if patch:
493 raise BuildSlaveTooOldError("old slave can't do patch")
494
495 if self.svnurl:
496 assert not branch # we need baseURL= to use branches
497 self.args['svnurl'] = self.svnurl
498 else:
499 self.args['svnurl'] = self.baseURL + branch
500 self.args['revision'] = revision
501 self.args['patch'] = patch
502
503 #Set up depth if specified
504 if self.depth is not None:
505 if self.slaveVersionIsOlderThan("svn","2.9"):
506 m = ("This buildslave (%s) does not support svn depth "
507 "arguments. "
508 "Refusing to build. "
509 "Please upgrade the buildslave." % (self.build.slav ename))
510 raise BuildSlaveTooOldError(m)
511 else:
512 self.args['depth'] = self.depth
513
514 if self.username is not None or self.password is not None:
515 if self.slaveVersionIsOlderThan("svn", "2.8"):
516 m = ("This buildslave (%s) does not support svn usernames "
517 "and passwords. "
518 "Refusing to build. Please upgrade the buildslave to "
519 "buildbot-0.7.10 or newer." % (self.build.slavename,))
520 raise BuildSlaveTooOldError(m)
521 if self.username is not None: self.args['username'] = self.username
522 if self.password is not None: self.args['password'] = self.password
523
524 if self.extra_args is not None:
525 self.args['extra_args'] = self.extra_args
526
527 revstuff = []
528 if branch is not None and branch != self.branch:
529 revstuff.append("[branch]")
530 if revision is not None:
531 revstuff.append("r%s" % revision)
532 if patch is not None:
533 revstuff.append("[patch]")
534 self.description.extend(revstuff)
535 self.descriptionDone.extend(revstuff)
536
537 cmd = LoggedRemoteCommand("svn", self.args)
538 self.startCommand(cmd, warnings)
539
540
541 class Darcs(Source):
542 """Check out a source tree from a Darcs repository at 'repourl'.
543
544 Darcs has no concept of file modes. This means the eXecute-bit will be
545 cleared on all source files. As a result, you may need to invoke
546 configuration scripts with something like:
547
548 C{s(step.Configure, command=['/bin/sh', './configure'])}
549 """
550
551 name = "darcs"
552
553 def __init__(self, repourl=None, baseURL=None, defaultBranch=None,
554 **kwargs):
555 """
556 @type repourl: string
557 @param repourl: the URL which points at the Darcs repository. This
558 is used as the default branch. Using C{repourl} does
559 not enable builds of alternate branches: use
560 C{baseURL} to enable this. Use either C{repourl} or
561 C{baseURL}, not both.
562
563 @param baseURL: if branches are enabled, this is the base URL to
564 which a branch name will be appended. It should
565 probably end in a slash. Use exactly one of
566 C{repourl} and C{baseURL}.
567
568 @param defaultBranch: if branches are enabled, this is the branch
569 to use if the Build does not specify one
570 explicitly. It will simply be appended to
571 C{baseURL} and the result handed to the
572 'darcs pull' command.
573 """
574 self.repourl = repourl
575 self.baseURL = baseURL
576 self.branch = defaultBranch
577 Source.__init__(self, **kwargs)
578 self.addFactoryArguments(repourl=repourl,
579 baseURL=baseURL,
580 defaultBranch=defaultBranch,
581 )
582 assert self.args['mode'] != "export", \
583 "Darcs does not have an 'export' mode"
584 if (not repourl and not baseURL) or (repourl and baseURL):
585 raise ValueError("you must provide exactly one of repourl and"
586 " baseURL")
587
588 def startVC(self, branch, revision, patch):
589 slavever = self.slaveVersion("darcs")
590 if not slavever:
591 m = "slave is too old, does not know about darcs"
592 raise BuildSlaveTooOldError(m)
593
594 if self.slaveVersionIsOlderThan("darcs", "1.39"):
595 if revision:
596 # TODO: revisit this once we implement computeSourceRevision
597 m = "0.6.6 slaves can't handle args['revision']"
598 raise BuildSlaveTooOldError(m)
599
600 # the slave doesn't know to avoid re-using the same sourcedir
601 # when the branch changes. We have no way of knowing which branch
602 # the last build used, so if we're using a non-default branch and
603 # either 'update' or 'copy' modes, it is safer to refuse to
604 # build, and tell the user they need to upgrade the buildslave.
605 if (branch != self.branch
606 and self.args['mode'] in ("update", "copy")):
607 m = ("This buildslave (%s) does not know about multiple "
608 "branches, and using mode=%s would probably build the "
609 "wrong tree. "
610 "Refusing to build. Please upgrade the buildslave to "
611 "buildbot-0.7.0 or newer." % (self.build.slavename,
612 self.args['mode']))
613 raise BuildSlaveTooOldError(m)
614
615 if self.repourl:
616 assert not branch # we need baseURL= to use branches
617 self.args['repourl'] = self.repourl
618 else:
619 self.args['repourl'] = self.baseURL + branch
620 self.args['revision'] = revision
621 self.args['patch'] = patch
622
623 revstuff = []
624 if branch is not None and branch != self.branch:
625 revstuff.append("[branch]")
626 self.description.extend(revstuff)
627 self.descriptionDone.extend(revstuff)
628
629 cmd = LoggedRemoteCommand("darcs", self.args)
630 self.startCommand(cmd)
631
632
633 class Git(Source):
634 """Check out a source tree from a git repository 'repourl'."""
635
636 name = "git"
637
638 def __init__(self, repourl,
639 branch="master",
640 submodules=False,
641 ignore_ignores=None,
642 **kwargs):
643 """
644 @type repourl: string
645 @param repourl: the URL which points at the git repository
646
647 @type branch: string
648 @param branch: The branch or tag to check out by default. If
649 a build specifies a different branch, it will
650 be used instead of this.
651
652 @type submodules: boolean
653 @param submodules: Whether or not to update (and initialize)
654 git submodules.
655
656 """
657 Source.__init__(self, **kwargs)
658 self.addFactoryArguments(repourl=repourl,
659 branch=branch,
660 submodules=submodules,
661 ignore_ignores=ignore_ignores,
662 )
663 self.args.update({'repourl': repourl,
664 'branch': branch,
665 'submodules': submodules,
666 'ignore_ignores': ignore_ignores,
667 })
668
669 def computeSourceRevision(self, changes):
670 if not changes:
671 return None
672 return changes[-1].revision
673
674 def startVC(self, branch, revision, patch):
675 if branch is not None:
676 self.args['branch'] = branch
677
678 self.args['revision'] = revision
679 self.args['patch'] = patch
680 slavever = self.slaveVersion("git")
681 if not slavever:
682 raise BuildSlaveTooOldError("slave is too old, does not know "
683 "about git")
684 cmd = LoggedRemoteCommand("git", self.args)
685 self.startCommand(cmd)
686
687
688 class Arch(Source):
689 """Check out a source tree from an Arch repository named 'archive'
690 available at 'url'. 'version' specifies which version number (development
691 line) will be used for the checkout: this is mostly equivalent to a
692 branch name. This version uses the 'tla' tool to do the checkout, to use
693 'baz' see L{Bazaar} instead.
694 """
695
696 name = "arch"
697 # TODO: slaves >0.6.6 will accept args['build-config'], so use it
698
699 def __init__(self, url, version, archive=None, **kwargs):
700 """
701 @type url: string
702 @param url: the Arch coordinates of the repository. This is
703 typically an http:// URL, but could also be the absolute
704 pathname of a local directory instead.
705
706 @type version: string
707 @param version: the category--branch--version to check out. This is
708 the default branch. If a build specifies a different
709 branch, it will be used instead of this.
710
711 @type archive: string
712 @param archive: The archive name. If provided, it must match the one
713 that comes from the repository. If not, the
714 repository's default will be used.
715 """
716 self.branch = version
717 Source.__init__(self, **kwargs)
718 self.addFactoryArguments(url=url,
719 version=version,
720 archive=archive,
721 )
722 self.args.update({'url': url,
723 'archive': archive,
724 })
725
726 def computeSourceRevision(self, changes):
727 # in Arch, fully-qualified revision numbers look like:
728 # arch@buildbot.sourceforge.net--2004/buildbot--dev--0--patch-104
729 # For any given builder, all of this is fixed except the patch-104.
730 # The Change might have any part of the fully-qualified string, so we
731 # just look for the last part. We return the "patch-NN" string.
732 if not changes:
733 return None
734 lastChange = None
735 for c in changes:
736 if not c.revision:
737 continue
738 if c.revision.endswith("--base-0"):
739 rev = 0
740 else:
741 i = c.revision.rindex("patch")
742 rev = int(c.revision[i+len("patch-"):])
743 lastChange = max(lastChange, rev)
744 if lastChange is None:
745 return None
746 if lastChange == 0:
747 return "base-0"
748 return "patch-%d" % lastChange
749
750 def checkSlaveVersion(self, cmd, branch):
751 warnings = []
752 slavever = self.slaveVersion(cmd)
753 if not slavever:
754 m = "slave is too old, does not know about %s" % cmd
755 raise BuildSlaveTooOldError(m)
756
757 # slave 1.28 and later understand 'revision'
758 if self.slaveVersionIsOlderThan(cmd, "1.28"):
759 if not self.alwaysUseLatest:
760 # we don't know whether our requested revision is the latest
761 # or not. If the tree does not change very quickly, this will
762 # probably build the right thing, so emit a warning rather
763 # than refuse to build at all
764 m = "WARNING, buildslave is too old to use a revision"
765 log.msg(m)
766 warnings.append(m + "\n")
767
768 if self.slaveVersionIsOlderThan(cmd, "1.39"):
769 # the slave doesn't know to avoid re-using the same sourcedir
770 # when the branch changes. We have no way of knowing which branch
771 # the last build used, so if we're using a non-default branch and
772 # either 'update' or 'copy' modes, it is safer to refuse to
773 # build, and tell the user they need to upgrade the buildslave.
774 if (branch != self.branch
775 and self.args['mode'] in ("update", "copy")):
776 m = ("This buildslave (%s) does not know about multiple "
777 "branches, and using mode=%s would probably build the "
778 "wrong tree. "
779 "Refusing to build. Please upgrade the buildslave to "
780 "buildbot-0.7.0 or newer." % (self.build.slavename,
781 self.args['mode']))
782 log.msg(m)
783 raise BuildSlaveTooOldError(m)
784
785 return warnings
786
787 def startVC(self, branch, revision, patch):
788 self.args['version'] = branch
789 self.args['revision'] = revision
790 self.args['patch'] = patch
791 warnings = self.checkSlaveVersion("arch", branch)
792
793 revstuff = []
794 if branch is not None and branch != self.branch:
795 revstuff.append("[branch]")
796 if revision is not None:
797 revstuff.append("patch%s" % revision)
798 self.description.extend(revstuff)
799 self.descriptionDone.extend(revstuff)
800
801 cmd = LoggedRemoteCommand("arch", self.args)
802 self.startCommand(cmd, warnings)
803
804
805 class Bazaar(Arch):
806 """Bazaar is an alternative client for Arch repositories. baz is mostly
807 compatible with tla, but archive registration is slightly different."""
808
809 # TODO: slaves >0.6.6 will accept args['build-config'], so use it
810
811 def __init__(self, url, version, archive, **kwargs):
812 """
813 @type url: string
814 @param url: the Arch coordinates of the repository. This is
815 typically an http:// URL, but could also be the absolute
816 pathname of a local directory instead.
817
818 @type version: string
819 @param version: the category--branch--version to check out
820
821 @type archive: string
822 @param archive: The archive name (required). This must always match
823 the one that comes from the repository, otherwise the
824 buildslave will attempt to get sources from the wrong
825 archive.
826 """
827 self.branch = version
828 Source.__init__(self, **kwargs)
829 self.addFactoryArguments(url=url,
830 version=version,
831 archive=archive,
832 )
833 self.args.update({'url': url,
834 'archive': archive,
835 })
836
837 def startVC(self, branch, revision, patch):
838 self.args['version'] = branch
839 self.args['revision'] = revision
840 self.args['patch'] = patch
841 warnings = self.checkSlaveVersion("bazaar", branch)
842
843 revstuff = []
844 if branch is not None and branch != self.branch:
845 revstuff.append("[branch]")
846 if revision is not None:
847 revstuff.append("patch%s" % revision)
848 self.description.extend(revstuff)
849 self.descriptionDone.extend(revstuff)
850
851 cmd = LoggedRemoteCommand("bazaar", self.args)
852 self.startCommand(cmd, warnings)
853
854 class Bzr(Source):
855 """Check out a source tree from a bzr (Bazaar) repository at 'repourl'.
856
857 """
858
859 name = "bzr"
860
861 def __init__(self, repourl=None, baseURL=None, defaultBranch=None,
862 forceSharedRepo=None,
863 **kwargs):
864 """
865 @type repourl: string
866 @param repourl: the URL which points at the bzr repository. This
867 is used as the default branch. Using C{repourl} does
868 not enable builds of alternate branches: use
869 C{baseURL} to enable this. Use either C{repourl} or
870 C{baseURL}, not both.
871
872 @param baseURL: if branches are enabled, this is the base URL to
873 which a branch name will be appended. It should
874 probably end in a slash. Use exactly one of
875 C{repourl} and C{baseURL}.
876
877 @param defaultBranch: if branches are enabled, this is the branch
878 to use if the Build does not specify one
879 explicitly. It will simply be appended to
880 C{baseURL} and the result handed to the
881 'bzr checkout pull' command.
882
883
884 @param forceSharedRepo: Boolean, defaults to False. If set to True,
885 the working directory will be made into a
886 bzr shared repository if it is not already.
887 Shared repository greatly reduces the amount
888 of history data that needs to be downloaded
889 if not using update/copy mode, or if using
890 update/copy mode with multiple branches.
891 """
892 self.repourl = repourl
893 self.baseURL = baseURL
894 self.branch = defaultBranch
895 Source.__init__(self, **kwargs)
896 self.addFactoryArguments(repourl=repourl,
897 baseURL=baseURL,
898 defaultBranch=defaultBranch,
899 forceSharedRepo=forceSharedRepo
900 )
901 self.args.update({'forceSharedRepo': forceSharedRepo})
902 if (not repourl and not baseURL) or (repourl and baseURL):
903 raise ValueError("you must provide exactly one of repourl and"
904 " baseURL")
905
906 def computeSourceRevision(self, changes):
907 if not changes:
908 return None
909 lastChange = max([int(c.revision) for c in changes])
910 return lastChange
911
912 def startVC(self, branch, revision, patch):
913 slavever = self.slaveVersion("bzr")
914 if not slavever:
915 m = "slave is too old, does not know about bzr"
916 raise BuildSlaveTooOldError(m)
917
918 if self.repourl:
919 assert not branch # we need baseURL= to use branches
920 self.args['repourl'] = self.repourl
921 else:
922 self.args['repourl'] = self.baseURL + branch
923 self.args['revision'] = revision
924 self.args['patch'] = patch
925
926 revstuff = []
927 if branch is not None and branch != self.branch:
928 revstuff.append("[" + branch + "]")
929 if revision is not None:
930 revstuff.append("r%s" % revision)
931 self.description.extend(revstuff)
932 self.descriptionDone.extend(revstuff)
933
934 cmd = LoggedRemoteCommand("bzr", self.args)
935 self.startCommand(cmd)
936
937
938 class Mercurial(Source):
939 """Check out a source tree from a mercurial repository 'repourl'."""
940
941 name = "hg"
942
943 def __init__(self, repourl=None, baseURL=None, defaultBranch=None,
944 branchType='dirname', clobberOnBranchChange=True, **kwargs):
945 """
946 @type repourl: string
947 @param repourl: the URL which points at the Mercurial repository.
948 This uses the 'default' branch unless defaultBranch is
949 specified below and the C{branchType} is set to
950 'inrepo'. It is an error to specify a branch without
951 setting the C{branchType} to 'inrepo'.
952
953 @param baseURL: if 'dirname' branches are enabled, this is the base URL
954 to which a branch name will be appended. It should
955 probably end in a slash. Use exactly one of C{repourl}
956 and C{baseURL}.
957
958 @param defaultBranch: if branches are enabled, this is the branch
959 to use if the Build does not specify one
960 explicitly.
961 For 'dirname' branches, It will simply be
962 appended to C{baseURL} and the result handed to
963 the 'hg update' command.
964 For 'inrepo' branches, this specifies the named
965 revision to which the tree will update after a
966 clone.
967
968 @param branchType: either 'dirname' or 'inrepo' depending on whether
969 the branch name should be appended to the C{baseURL}
970 or the branch is a mercurial named branch and can be
971 found within the C{repourl}
972
973 @param clobberOnBranchChange: boolean, defaults to True. If set and
974 using inrepos branches, clobber the tree
975 at each branch change. Otherwise, just
976 update to the branch.
977 """
978 self.repourl = repourl
979 self.baseURL = baseURL
980 self.branch = defaultBranch
981 self.branchType = branchType
982 self.clobberOnBranchChange = clobberOnBranchChange
983 Source.__init__(self, **kwargs)
984 self.addFactoryArguments(repourl=repourl,
985 baseURL=baseURL,
986 defaultBranch=defaultBranch,
987 branchType=branchType,
988 clobberOnBranchChange=clobberOnBranchChange,
989 )
990 if (not repourl and not baseURL) or (repourl and baseURL):
991 raise ValueError("you must provide exactly one of repourl and"
992 " baseURL")
993
994 def startVC(self, branch, revision, patch):
995 slavever = self.slaveVersion("hg")
996 if not slavever:
997 raise BuildSlaveTooOldError("slave is too old, does not know "
998 "about hg")
999
1000 if self.repourl:
1001 # we need baseURL= to use dirname branches
1002 assert self.branchType == 'inrepo' or not branch
1003 self.args['repourl'] = self.repourl
1004 if branch:
1005 self.args['branch'] = branch
1006 else:
1007 self.args['repourl'] = self.baseURL + (branch or '')
1008 self.args['revision'] = revision
1009 self.args['patch'] = patch
1010 self.args['clobberOnBranchChange'] = self.clobberOnBranchChange
1011 self.args['branchType'] = self.branchType
1012
1013 revstuff = []
1014 if branch is not None and branch != self.branch:
1015 revstuff.append("[branch]")
1016 self.description.extend(revstuff)
1017 self.descriptionDone.extend(revstuff)
1018
1019 cmd = LoggedRemoteCommand("hg", self.args)
1020 self.startCommand(cmd)
1021
1022 def computeSourceRevision(self, changes):
1023 if not changes:
1024 return None
1025 # without knowing the revision ancestry graph, we can't sort the
1026 # changes at all. So for now, assume they were given to us in sorted
1027 # order, and just pay attention to the last one. See ticket #103 for
1028 # more details.
1029 if len(changes) > 1:
1030 log.msg("Mercurial.computeSourceRevision: warning: "
1031 "there are %d changes here, assuming the last one is "
1032 "the most recent" % len(changes))
1033 return changes[-1].revision
1034
1035
1036 class P4(Source):
1037 """ P4 is a class for accessing perforce revision control"""
1038 name = "p4"
1039
1040 def __init__(self, p4base, defaultBranch=None, p4port=None, p4user=None,
1041 p4passwd=None, p4extra_views=[],
1042 p4client='buildbot_%(slave)s_%(builder)s', **kwargs):
1043 """
1044 @type p4base: string
1045 @param p4base: A view into a perforce depot, typically
1046 "//depot/proj/"
1047
1048 @type defaultBranch: string
1049 @param defaultBranch: Identify a branch to build by default. Perforce
1050 is a view based branching system. So, the branch
1051 is normally the name after the base. For example,
1052 branch=1.0 is view=//depot/proj/1.0/...
1053 branch=1.1 is view=//depot/proj/1.1/...
1054
1055 @type p4port: string
1056 @param p4port: Specify the perforce server to connection in the format
1057 <host>:<port>. Example "perforce.example.com:1666"
1058
1059 @type p4user: string
1060 @param p4user: The perforce user to run the command as.
1061
1062 @type p4passwd: string
1063 @param p4passwd: The password for the perforce user.
1064
1065 @type p4extra_views: list of tuples
1066 @param p4extra_views: Extra views to be added to
1067 the client that is being used.
1068
1069 @type p4client: string
1070 @param p4client: The perforce client to use for this buildslave.
1071 """
1072
1073 self.branch = defaultBranch
1074 Source.__init__(self, **kwargs)
1075 self.addFactoryArguments(p4base=p4base,
1076 defaultBranch=defaultBranch,
1077 p4port=p4port,
1078 p4user=p4user,
1079 p4passwd=p4passwd,
1080 p4extra_views=p4extra_views,
1081 p4client=p4client,
1082 )
1083 self.args['p4port'] = p4port
1084 self.args['p4user'] = p4user
1085 self.args['p4passwd'] = p4passwd
1086 self.args['p4base'] = p4base
1087 self.args['p4extra_views'] = p4extra_views
1088 self.p4client = p4client
1089
1090 def setBuild(self, build):
1091 Source.setBuild(self, build)
1092 self.args['p4client'] = self.p4client % {
1093 'slave': build.slavename,
1094 'builder': build.builder.name,
1095 }
1096
1097 def computeSourceRevision(self, changes):
1098 if not changes:
1099 return None
1100 lastChange = max([int(c.revision) for c in changes])
1101 return lastChange
1102
1103 def startVC(self, branch, revision, patch):
1104 slavever = self.slaveVersion("p4")
1105 assert slavever, "slave is too old, does not know about p4"
1106 args = dict(self.args)
1107 args['branch'] = branch or self.branch
1108 args['revision'] = revision
1109 args['patch'] = patch
1110 cmd = LoggedRemoteCommand("p4", args)
1111 self.startCommand(cmd)
1112
1113 class P4Sync(Source):
1114 """This is a partial solution for using a P4 source repository. You are
1115 required to manually set up each build slave with a useful P4
1116 environment, which means setting various per-slave environment variables,
1117 and creating a P4 client specification which maps the right files into
1118 the slave's working directory. Once you have done that, this step merely
1119 performs a 'p4 sync' to update that workspace with the newest files.
1120
1121 Each slave needs the following environment:
1122
1123 - PATH: the 'p4' binary must be on the slave's PATH
1124 - P4USER: each slave needs a distinct user account
1125 - P4CLIENT: each slave needs a distinct client specification
1126
1127 You should use 'p4 client' (?) to set up a client view spec which maps
1128 the desired files into $SLAVEBASE/$BUILDERBASE/source .
1129 """
1130
1131 name = "p4sync"
1132
1133 def __init__(self, p4port, p4user, p4passwd, p4client, **kwargs):
1134 assert kwargs['mode'] == "copy", "P4Sync can only be used in mode=copy"
1135 self.branch = None
1136 Source.__init__(self, **kwargs)
1137 self.addFactoryArguments(p4port=p4port,
1138 p4user=p4user,
1139 p4passwd=p4passwd,
1140 p4client=p4client,
1141 )
1142 self.args['p4port'] = p4port
1143 self.args['p4user'] = p4user
1144 self.args['p4passwd'] = p4passwd
1145 self.args['p4client'] = p4client
1146
1147 def computeSourceRevision(self, changes):
1148 if not changes:
1149 return None
1150 lastChange = max([int(c.revision) for c in changes])
1151 return lastChange
1152
1153 def startVC(self, branch, revision, patch):
1154 slavever = self.slaveVersion("p4sync")
1155 assert slavever, "slave is too old, does not know about p4"
1156 cmd = LoggedRemoteCommand("p4sync", self.args)
1157 self.startCommand(cmd)
1158
1159 class Monotone(Source):
1160 """Check out a revision from a monotone server at 'server_addr',
1161 branch 'branch'. 'revision' specifies which revision id to check
1162 out.
1163
1164 This step will first create a local database, if necessary, and then pull
1165 the contents of the server into the database. Then it will do the
1166 checkout/update from this database."""
1167
1168 name = "monotone"
1169
1170 def __init__(self, server_addr, branch, db_path="monotone.db",
1171 monotone="monotone",
1172 **kwargs):
1173 Source.__init__(self, **kwargs)
1174 self.addFactoryArguments(server_addr=server_addr,
1175 branch=branch,
1176 db_path=db_path,
1177 monotone=monotone,
1178 )
1179 self.args.update({"server_addr": server_addr,
1180 "branch": branch,
1181 "db_path": db_path,
1182 "monotone": monotone})
1183
1184 def computeSourceRevision(self, changes):
1185 if not changes:
1186 return None
1187 return changes[-1].revision
1188
1189 def startVC(self):
1190 slavever = self.slaveVersion("monotone")
1191 assert slavever, "slave is too old, does not know about monotone"
1192 cmd = LoggedRemoteCommand("monotone", self.args)
1193 self.startCommand(cmd)
OLDNEW
« no previous file with comments | « third_party/buildbot_7_12/buildbot/steps/shell.py ('k') | third_party/buildbot_7_12/buildbot/steps/transfer.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698