1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 from warnings import warn
18 from email.Utils import formatdate
19 from twisted.python import log
20 from twisted.internet import defer
21 from zope.interface import implements
22 from buildbot.process.buildstep import LoggingBuildStep, RemoteCommand
23 from buildbot.interfaces import BuildSlaveTooOldError, IRenderable
24 from buildbot.status.builder import SKIPPED
25
59
60 -class Source(LoggingBuildStep):
61 """This is a base class to generate a source tree in the buildslave.
62 Each version control system has a specialized subclass, and is expected
63 to override __init__ and implement computeSourceRevision() and
64 startVC(). The class as a whole builds up the self.args dictionary, then
65 starts a RemoteCommand with those arguments.
66 """
67
68 renderables = [ 'workdir', 'description', 'descriptionDone' ]
69 description = None
70 descriptionDone = None
71
72
73 haltOnFailure = True
74 flunkOnFailure = True
75 notReally = False
76
77 branch = None
78
79 - def __init__(self, workdir=None, mode='update', alwaysUseLatest=False,
80 timeout=20*60, retry=None, env=None, logEnviron=True,
81 description=None, descriptionDone=None, **kwargs):
82 """
83 @type workdir: string
84 @param workdir: local directory (relative to the Builder's root)
85 where the tree should be placed
86
87 @type mode: string
88 @param mode: the kind of VC operation that is desired:
89 - 'update': specifies that the checkout/update should be
90 performed directly into the workdir. Each build is performed
91 in the same directory, allowing for incremental builds. This
92 minimizes disk space, bandwidth, and CPU time. However, it
93 may encounter problems if the build process does not handle
94 dependencies properly (if you must sometimes do a 'clean
95 build' to make sure everything gets compiled), or if source
96 files are deleted but generated files can influence test
97 behavior (e.g. python's .pyc files), or when source
98 directories are deleted but generated files prevent CVS from
99 removing them. When used with a patched checkout, from a
100 previous buildbot try for instance, it will try to "revert"
101 the changes first and will do a clobber if it is unable to
102 get a clean checkout. The behavior is SCM-dependent.
103
104 - 'copy': specifies that the source-controlled workspace
105 should be maintained in a separate directory (called the
106 'copydir'), using checkout or update as necessary. For each
107 build, a new workdir is created with a copy of the source
108 tree (rm -rf workdir; cp -R -P -p copydir workdir). This
109 doubles the disk space required, but keeps the bandwidth low
110 (update instead of a full checkout). A full 'clean' build
111 is performed each time. This avoids any generated-file
112 build problems, but is still occasionally vulnerable to
113 problems such as a CVS repository being manually rearranged
114 (causing CVS errors on update) which are not an issue with
115 a full checkout.
116
117 - 'clobber': specifies that the working directory should be
118 deleted each time, necessitating a full checkout for each
119 build. This insures a clean build off a complete checkout,
120 avoiding any of the problems described above, but is
121 bandwidth intensive, as the whole source tree must be
122 pulled down for each build.
123
124 - 'export': is like 'clobber', except that e.g. the 'cvs
125 export' command is used to create the working directory.
126 This command removes all VC metadata files (the
127 CVS/.svn/{arch} directories) from the tree, which is
128 sometimes useful for creating source tarballs (to avoid
129 including the metadata in the tar file). Not all VC systems
130 support export.
131
132 @type alwaysUseLatest: boolean
133 @param alwaysUseLatest: whether to always update to the most
134 recent available sources for this build.
135
136 Normally the Source step asks its Build for a list of all
137 Changes that are supposed to go into the build, then computes a
138 'source stamp' (revision number or timestamp) that will cause
139 exactly that set of changes to be present in the checked out
140 tree. This is turned into, e.g., 'cvs update -D timestamp', or
141 'svn update -r revnum'. If alwaysUseLatest=True, bypass this
142 computation and always update to the latest available sources
143 for each build.
144
145 The source stamp helps avoid a race condition in which someone
146 commits a change after the master has decided to start a build
147 but before the slave finishes checking out the sources. At best
148 this results in a build which contains more changes than the
149 buildmaster thinks it has (possibly resulting in the wrong
150 person taking the blame for any problems that result), at worst
151 is can result in an incoherent set of sources (splitting a
152 non-atomic commit) which may not build at all.
153
154 @type retry: tuple of ints (delay, repeats) (or None)
155 @param retry: if provided, VC update failures are re-attempted up
156 to REPEATS times, with DELAY seconds between each
157 attempt. Some users have slaves with poor connectivity
158 to their VC repository, and they say that up to 80% of
159 their build failures are due to transient network
160 failures that could be handled by simply retrying a
161 couple times.
162
163 @type logEnviron: boolean
164 @param logEnviron: If this option is true (the default), then the
165 step's logfile will describe the environment
166 variables on the slave. In situations where the
167 environment is not relevant and is long, it may
168 be easier to set logEnviron=False.
169
170 """
171
172 LoggingBuildStep.__init__(self, **kwargs)
173 self.addFactoryArguments(workdir=workdir,
174 mode=mode,
175 alwaysUseLatest=alwaysUseLatest,
176 timeout=timeout,
177 retry=retry,
178 logEnviron=logEnviron,
179 env=env,
180 description=description,
181 descriptionDone=descriptionDone
182 )
183
184 assert mode in ("update", "copy", "clobber", "export")
185 if retry:
186 delay, repeats = retry
187 assert isinstance(repeats, int)
188 assert repeats > 0
189 self.args = {'mode': mode,
190 'timeout': timeout,
191 'retry': retry,
192 'patch': None,
193 }
194
195 self.workdir = workdir
196
197 self.alwaysUseLatest = alwaysUseLatest
198
199 self.logEnviron = logEnviron
200 self.env = env
201
202 descriptions_for_mode = {
203 "clobber": "checkout",
204 "export": "exporting"}
205 descriptionDones_for_mode = {
206 "clobber": "checkout",
207 "export": "export"}
208 if description:
209 self.description = description
210 else:
211 self.description = [
212 descriptions_for_mode.get(mode, "updating")]
213 if isinstance(self.description, str):
214 self.description = [self.description]
215
216 if descriptionDone:
217 self.descriptionDone = descriptionDone
218 else:
219 self.descriptionDone = [
220 descriptionDones_for_mode.get(mode, "update")]
221 if isinstance(self.descriptionDone, str):
222 self.descriptionDone = [self.descriptionDone]
223
226
229
234
236 """Each subclass must implement this method to do something more
237 precise than -rHEAD every time. For version control systems that use
238 repository-wide change numbers (SVN, P4), this can simply take the
239 maximum such number from all the changes involved in this build. For
240 systems that do not (CVS), it needs to create a timestamp based upon
241 the latest Change, the Build's treeStableTimer, and an optional
242 self.checkoutDelay value."""
243 return None
244
246 """ Identify the unique repository for this step """
247 return None
248
292
294 if cmd.updates.has_key("got_revision"):
295 got_revision = cmd.updates["got_revision"][-1]
296 if got_revision is not None:
297 self.setProperty("got_revision", str(got_revision), "Source")
298
299
300
302 """I perform BitKeeper checkout/update operations."""
303
304 name = 'bk'
305
306 renderables = [ 'bkurl', 'baseURL' ]
307
308 - def __init__(self, bkurl=None, baseURL=None,
309 directory=None, extra_args=None, **kwargs):
310 """
311 @type bkurl: string
312 @param bkurl: the URL which points to the BitKeeper server.
313
314 @type baseURL: string
315 @param baseURL: if branches are enabled, this is the base URL to
316 which a branch name will be appended. It should
317 probably end in a slash. Use exactly one of
318 C{bkurl} and C{baseURL}.
319 """
320
321 self.bkurl = _ComputeRepositoryURL(bkurl)
322 self.baseURL = _ComputeRepositoryURL(baseURL)
323 self.extra_args = extra_args
324
325 Source.__init__(self, **kwargs)
326 self.addFactoryArguments(bkurl=bkurl,
327 baseURL=baseURL,
328 directory=directory,
329 extra_args=extra_args,
330 )
331
332 if bkurl and baseURL:
333 raise ValueError("you must use exactly one of bkurl and baseURL")
334
335
338
339
340 - def startVC(self, branch, revision, patch):
370
371
372
374 """I do CVS checkout/update operations.
375
376 Note: if you are doing anonymous/pserver CVS operations, you will need
377 to manually do a 'cvs login' on each buildslave before the slave has any
378 hope of success. XXX: fix then, take a cvs password as an argument and
379 figure out how to do a 'cvs login' on each build
380 """
381
382 name = "cvs"
383
384 renderables = [ "cvsroot" ]
385
386
387
388
389
390
391
392
393
394
395
396 - def __init__(self, cvsroot=None, cvsmodule="",
397 global_options=[], branch=None, checkoutDelay=None,
398 checkout_options=[], export_options=[], extra_options=[],
399 login=None,
400 **kwargs):
401
402 """
403 @type cvsroot: string
404 @param cvsroot: CVS Repository from which the source tree should
405 be obtained. '/home/warner/Repository' for local
406 or NFS-reachable repositories,
407 ':pserver:anon@foo.com:/cvs' for anonymous CVS,
408 'user@host.com:/cvs' for non-anonymous CVS or
409 CVS over ssh. Lots of possibilities, check the
410 CVS documentation for more.
411
412 @type cvsmodule: string
413 @param cvsmodule: subdirectory of CVS repository that should be
414 retrieved
415
416 @type login: string or None
417 @param login: if not None, a string which will be provided as a
418 password to the 'cvs login' command, used when a
419 :pserver: method is used to access the repository.
420 This login is only needed once, but must be run
421 each time (just before the CVS operation) because
422 there is no way for the buildslave to tell whether
423 it was previously performed or not.
424
425 @type branch: string
426 @param branch: the default branch name, will be used in a '-r'
427 argument to specify which branch of the source tree
428 should be used for this checkout. Defaults to None,
429 which means to use 'HEAD'.
430
431 @type checkoutDelay: int or None
432 @param checkoutDelay: if not None, the number of seconds to put
433 between the last known Change and the
434 timestamp given to the -D argument. This
435 defaults to exactly half of the parent
436 Build's .treeStableTimer, but it could be
437 set to something else if your CVS change
438 notification has particularly weird
439 latency characteristics.
440
441 @type global_options: list of strings
442 @param global_options: these arguments are inserted in the cvs
443 command line, before the
444 'checkout'/'update' command word. See
445 'cvs --help-options' for a list of what
446 may be accepted here. ['-r'] will make
447 the checked out files read only. ['-r',
448 '-R'] will also assume the repository is
449 read-only (I assume this means it won't
450 use locks to insure atomic access to the
451 ,v files).
452
453 @type checkout_options: list of strings
454 @param checkout_options: these arguments are inserted in the cvs
455 command line, after 'checkout' but before
456 branch or revision specifiers.
457
458 @type export_options: list of strings
459 @param export_options: these arguments are inserted in the cvs
460 command line, after 'export' but before
461 branch or revision specifiers.
462
463 @type extra_options: list of strings
464 @param extra_options: these arguments are inserted in the cvs
465 command line, after 'checkout' or 'export' but before
466 branch or revision specifiers.
467 """
468
469 self.checkoutDelay = checkoutDelay
470 self.branch = branch
471 self.cvsroot = _ComputeRepositoryURL(cvsroot)
472
473 Source.__init__(self, **kwargs)
474 self.addFactoryArguments(cvsroot=cvsroot,
475 cvsmodule=cvsmodule,
476 global_options=global_options,
477 checkout_options=checkout_options,
478 export_options=export_options,
479 extra_options=extra_options,
480 branch=branch,
481 checkoutDelay=checkoutDelay,
482 login=login,
483 )
484
485 self.args.update({'cvsmodule': cvsmodule,
486 'global_options': global_options,
487 'checkout_options':checkout_options,
488 'export_options':export_options,
489 'extra_options':extra_options,
490 'login': login,
491 })
492
494 if not changes:
495 return None
496 lastChange = max([c.when for c in changes])
497 if self.checkoutDelay is not None:
498 when = lastChange + self.checkoutDelay
499 else:
500 lastSubmit = max([br.submittedAt for br in self.build.requests])
501 when = (lastChange + lastSubmit) / 2
502 return formatdate(when)
503
504 - def startVC(self, branch, revision, patch):
505 if self.slaveVersionIsOlderThan("cvs", "1.39"):
506
507
508
509
510
511 if (branch != self.branch
512 and self.args['mode'] in ("update", "copy")):
513 m = ("This buildslave (%s) does not know about multiple "
514 "branches, and using mode=%s would probably build the "
515 "wrong tree. "
516 "Refusing to build. Please upgrade the buildslave to "
517 "buildbot-0.7.0 or newer." % (self.build.slavename,
518 self.args['mode']))
519 log.msg(m)
520 raise BuildSlaveTooOldError(m)
521
522 if self.slaveVersionIsOlderThan("cvs", "2.10"):
523 if self.args['extra_options'] or self.args['export_options']:
524 m = ("This buildslave (%s) does not support export_options "
525 "or extra_options arguments to the CVS step."
526 % (self.build.slavename))
527 log.msg(m)
528 raise BuildSlaveTooOldError(m)
529
530
531 del self.args['export_options']
532 del self.args['extra_options']
533
534 if branch is None:
535 branch = "HEAD"
536 self.args['cvsroot'] = self.cvsroot
537 self.args['branch'] = branch
538 self.args['revision'] = revision
539 self.args['patch'] = patch
540
541 if self.args['branch'] == "HEAD" and self.args['revision']:
542
543
544 self.args['branch'] = None
545
546
547 warnings = []
548 slavever = self.slaveVersion("cvs", "old")
549
550 if slavever == "old":
551
552 if self.args['mode'] == "export":
553 self.args['export'] = 1
554 elif self.args['mode'] == "clobber":
555 self.args['clobber'] = 1
556 elif self.args['mode'] == "copy":
557 self.args['copydir'] = "source"
558 self.args['tag'] = self.args['branch']
559 assert not self.args['patch']
560
561 cmd = RemoteCommand("cvs", self.args)
562 self.startCommand(cmd, warnings)
563
564
566 """I perform Subversion checkout/update operations."""
567
568 name = 'svn'
569 branch_placeholder = '%%BRANCH%%'
570
571 renderables = [ 'svnurl', 'baseURL' ]
572
573 - def __init__(self, svnurl=None, baseURL=None, defaultBranch=None,
574 directory=None, username=None, password=None,
575 extra_args=None, keep_on_purge=None, ignore_ignores=None,
576 always_purge=None, depth=None, **kwargs):
577 """
578 @type svnurl: string
579 @param svnurl: the URL which points to the Subversion server,
580 combining the access method (HTTP, ssh, local file),
581 the repository host/port, the repository path, the
582 sub-tree within the repository, and the branch to
583 check out. Use exactly one of C{svnurl} and C{baseURL}.
584
585 @param baseURL: if branches are enabled, this is the base URL to
586 which a branch name will be appended. It should
587 probably end in a slash. Use exactly one of
588 C{svnurl} and C{baseURL}.
589
590 @param defaultBranch: if branches are enabled, this is the branch
591 to use if the Build does not specify one
592 explicitly. It will simply be appended
593 to C{baseURL} and the result handed to
594 the SVN command.
595
596 @type username: string
597 @param username: username to pass to svn's --username
598
599 @type password: string
600 @param password: password to pass to svn's --password
601 """
602
603 if not 'workdir' in kwargs and directory is not None:
604
605 warn("Please use workdir=, not directory=", DeprecationWarning)
606 kwargs['workdir'] = directory
607
608 self.svnurl = svnurl and _ComputeRepositoryURL(svnurl)
609 self.baseURL = _ComputeRepositoryURL(baseURL)
610 self.branch = defaultBranch
611 self.username = username
612 self.password = password
613 self.extra_args = extra_args
614 self.keep_on_purge = keep_on_purge
615 self.ignore_ignores = ignore_ignores
616 self.always_purge = always_purge
617 self.depth = depth
618
619 Source.__init__(self, **kwargs)
620 self.addFactoryArguments(svnurl=svnurl,
621 baseURL=baseURL,
622 defaultBranch=defaultBranch,
623 directory=directory,
624 username=username,
625 password=password,
626 extra_args=extra_args,
627 keep_on_purge=keep_on_purge,
628 ignore_ignores=ignore_ignores,
629 always_purge=always_purge,
630 depth=depth,
631 )
632
633 if svnurl and baseURL:
634 raise ValueError("you must use either svnurl OR baseURL")
635
641
643 ''' Handle compatibility between old slaves/svn clients '''
644
645 slavever = self.slaveVersion("svn", "old")
646
647 if not slavever:
648 m = "slave does not have the 'svn' command"
649 raise BuildSlaveTooOldError(m)
650
651 if self.slaveVersionIsOlderThan("svn", "1.39"):
652
653
654
655
656
657 if (self.args['branch'] != self.branch
658 and self.args['mode'] in ("update", "copy")):
659 m = ("This buildslave (%s) does not know about multiple "
660 "branches, and using mode=%s would probably build the "
661 "wrong tree. "
662 "Refusing to build. Please upgrade the buildslave to "
663 "buildbot-0.7.0 or newer." % (self.build.slavename,
664 self.args['mode']))
665 raise BuildSlaveTooOldError(m)
666
667 if (self.depth is not None) and self.slaveVersionIsOlderThan("svn","2.9"):
668 m = ("This buildslave (%s) does not support svn depth "
669 "arguments. Refusing to build. "
670 "Please upgrade the buildslave." % (self.build.slavename))
671 raise BuildSlaveTooOldError(m)
672
673 if (self.username is not None or self.password is not None) \
674 and self.slaveVersionIsOlderThan("svn", "2.8"):
675 m = ("This buildslave (%s) does not support svn usernames "
676 "and passwords. "
677 "Refusing to build. Please upgrade the buildslave to "
678 "buildbot-0.7.10 or newer." % (self.build.slavename,))
679 raise BuildSlaveTooOldError(m)
680
682 ''' Compute the svn url that will be passed to the svn remote command '''
683 if self.svnurl:
684 return self.svnurl
685 else:
686 if branch is None:
687 m = ("The SVN source step belonging to builder '%s' does not know "
688 "which branch to work with. This means that the change source "
689 "did not specify a branch and that defaultBranch is None." \
690 % self.build.builder.name)
691 raise RuntimeError(m)
692
693 computed = self.baseURL
694
695 if self.branch_placeholder in self.baseURL:
696 return computed.replace(self.branch_placeholder, branch)
697 else:
698 return computed + branch
699
700 - def startVC(self, branch, revision, patch):
701 warnings = []
702
703 self.checkCompatibility()
704
705 self.args['svnurl'] = self.getSvnUrl(branch)
706 self.args['revision'] = revision
707 self.args['patch'] = patch
708 self.args['always_purge'] = self.always_purge
709
710
711 if self.depth is not None:
712 self.args['depth'] = self.depth
713
714 if self.username is not None:
715 self.args['username'] = self.username
716 if self.password is not None:
717 self.args['password'] = self.password
718
719 if self.extra_args is not None:
720 self.args['extra_args'] = self.extra_args
721
722 revstuff = []
723
724 if self.args['svnurl'].find('trunk') == -1:
725 revstuff.append("[branch]")
726 if revision is not None:
727 revstuff.append("r%s" % revision)
728 if patch is not None:
729 revstuff.append("[patch]")
730 self.description.extend(revstuff)
731 self.descriptionDone.extend(revstuff)
732
733 cmd = RemoteCommand("svn", self.args)
734 self.startCommand(cmd, warnings)
735
736
738 """Check out a source tree from a Darcs repository at 'repourl'.
739
740 Darcs has no concept of file modes. This means the eXecute-bit will be
741 cleared on all source files. As a result, you may need to invoke
742 configuration scripts with something like:
743
744 C{s(step.Configure, command=['/bin/sh', './configure'])}
745 """
746
747 name = "darcs"
748
749 renderables = [ 'repourl', 'baseURL' ]
750
751 - def __init__(self, repourl=None, baseURL=None, defaultBranch=None,
752 **kwargs):
753 """
754 @type repourl: string
755 @param repourl: the URL which points at the Darcs repository. This
756 is used as the default branch. Using C{repourl} does
757 not enable builds of alternate branches: use
758 C{baseURL} to enable this. Use either C{repourl} or
759 C{baseURL}, not both.
760
761 @param baseURL: if branches are enabled, this is the base URL to
762 which a branch name will be appended. It should
763 probably end in a slash. Use exactly one of
764 C{repourl} and C{baseURL}.
765
766 @param defaultBranch: if branches are enabled, this is the branch
767 to use if the Build does not specify one
768 explicitly. It will simply be appended to
769 C{baseURL} and the result handed to the
770 'darcs pull' command.
771 """
772 self.repourl = _ComputeRepositoryURL(repourl)
773 self.baseURL = _ComputeRepositoryURL(baseURL)
774 self.branch = defaultBranch
775 Source.__init__(self, **kwargs)
776 self.addFactoryArguments(repourl=repourl,
777 baseURL=baseURL,
778 defaultBranch=defaultBranch,
779 )
780 assert self.args['mode'] != "export", \
781 "Darcs does not have an 'export' mode"
782 if repourl and baseURL:
783 raise ValueError("you must provide exactly one of repourl and"
784 " baseURL")
785
786 - def startVC(self, branch, revision, patch):
787 slavever = self.slaveVersion("darcs")
788 if not slavever:
789 m = "slave is too old, does not know about darcs"
790 raise BuildSlaveTooOldError(m)
791
792 if self.slaveVersionIsOlderThan("darcs", "1.39"):
793 if revision:
794
795 m = "0.6.6 slaves can't handle args['revision']"
796 raise BuildSlaveTooOldError(m)
797
798
799
800
801
802
803 if (branch != self.branch
804 and self.args['mode'] in ("update", "copy")):
805 m = ("This buildslave (%s) does not know about multiple "
806 "branches, and using mode=%s would probably build the "
807 "wrong tree. "
808 "Refusing to build. Please upgrade the buildslave to "
809 "buildbot-0.7.0 or newer." % (self.build.slavename,
810 self.args['mode']))
811 raise BuildSlaveTooOldError(m)
812
813 if self.repourl:
814 assert not branch
815 self.args['repourl'] = self.repourl
816 else:
817 self.args['repourl'] = self.baseURL + branch
818 self.args['revision'] = revision
819 self.args['patch'] = patch
820
821 revstuff = []
822 if branch is not None and branch != self.branch:
823 revstuff.append("[branch]")
824 self.description.extend(revstuff)
825 self.descriptionDone.extend(revstuff)
826
827 cmd = RemoteCommand("darcs", self.args)
828 self.startCommand(cmd)
829
830
832 """Check out a source tree from a git repository 'repourl'."""
833
834 name = "git"
835
836 renderables = [ 'repourl' ]
837
838 - def __init__(self, repourl=None,
839 branch="master",
840 submodules=False,
841 ignore_ignores=None,
842 reference=None,
843 shallow=False,
844 progress=False,
845 **kwargs):
846 """
847 @type repourl: string
848 @param repourl: the URL which points at the git repository
849
850 @type branch: string
851 @param branch: The branch or tag to check out by default. If
852 a build specifies a different branch, it will
853 be used instead of this.
854
855 @type submodules: boolean
856 @param submodules: Whether or not to update (and initialize)
857 git submodules.
858
859 @type reference: string
860 @param reference: The path to a reference repository to obtain
861 objects from, if any.
862
863 @type shallow: boolean
864 @param shallow: Use a shallow or clone, if possible
865
866 @type progress: boolean
867 @param progress: Pass the --progress option when fetching. This
868 can solve long fetches getting killed due to
869 lack of output, but requires Git 1.7.2+.
870 """
871 Source.__init__(self, **kwargs)
872 self.repourl = _ComputeRepositoryURL(repourl)
873 self.branch = branch
874 self.addFactoryArguments(repourl=repourl,
875 branch=branch,
876 submodules=submodules,
877 ignore_ignores=ignore_ignores,
878 reference=reference,
879 shallow=shallow,
880 progress=progress,
881 )
882 self.args.update({'submodules': submodules,
883 'ignore_ignores': ignore_ignores,
884 'reference': reference,
885 'shallow': shallow,
886 'progress': progress,
887 })
888
893
894 - def startVC(self, branch, revision, patch):
895 self.args['branch'] = branch
896 self.args['repourl'] = self.repourl
897 self.args['revision'] = revision
898 self.args['patch'] = patch
899
900
901 if self.build.hasProperty("event.patchSet.ref"):
902
903 self.args['gerrit_branch'] = self.build.getProperty("event.patchSet.ref")
904 self.setProperty("gerrit_branch", self.args['gerrit_branch'])
905 else:
906 try:
907
908 change = self.build.getProperty("gerrit_change", '').split('/')
909 if len(change) == 2:
910 self.args['gerrit_branch'] = "refs/changes/%2.2d/%d/%d" \
911 % (int(change[0]) % 100, int(change[0]), int(change[1]))
912 self.setProperty("gerrit_branch", self.args['gerrit_branch'])
913 except:
914 pass
915
916 slavever = self.slaveVersion("git")
917 if not slavever:
918 raise BuildSlaveTooOldError("slave is too old, does not know "
919 "about git")
920 cmd = RemoteCommand("git", self.args)
921 self.startCommand(cmd)
922
923
925 """Check out a source tree from a repo repository described by manifest."""
926
927 name = "repo"
928
929 renderables = [ "manifest_url" ]
930
931 - def __init__(self,
932 manifest_url=None,
933 manifest_branch="master",
934 manifest_file="default.xml",
935 tarball=None,
936 **kwargs):
937 """
938 @type manifest_url: string
939 @param manifest_url: The URL which points at the repo manifests repository.
940
941 @type manifest_branch: string
942 @param manifest_branch: The manifest branch to check out by default.
943
944 @type manifest_file: string
945 @param manifest_file: The manifest to use for sync.
946
947 """
948 Source.__init__(self, **kwargs)
949 self.manifest_url = _ComputeRepositoryURL(manifest_url)
950 self.addFactoryArguments(manifest_url=manifest_url,
951 manifest_branch=manifest_branch,
952 manifest_file=manifest_file,
953 tarball=tarball,
954 )
955 self.args.update({'manifest_branch': manifest_branch,
956 'manifest_file': manifest_file,
957 'tarball': tarball,
958 'manifest_override_url': None
959 })
960
965
967 """
968 lets try to be nice in the format we want
969 can support several instances of "repo download proj number/patch" (direct copy paste from gerrit web site)
970 or several instances of "proj number/patch" (simpler version)
971 This feature allows integrator to build with several pending interdependant changes.
972 returns list of repo downloads sent to the buildslave
973 """
974 import re
975 if s == None:
976 return []
977 re1 = re.compile("repo download ([^ ]+) ([0-9]+/[0-9]+)")
978 re2 = re.compile("([^ ]+) ([0-9]+/[0-9]+)")
979 re3 = re.compile("([^ ]+)/([0-9]+/[0-9]+)")
980 ret = []
981 for cur_re in [re1, re2, re3]:
982 res = cur_re.search(s)
983 while res:
984 ret.append("%s %s" % (res.group(1), res.group(2)))
985 s = s[:res.start(0)] + s[res.end(0):]
986 res = cur_re.search(s)
987 return ret
988
990 """taken the changesource and forcebuild property,
991 build the repo download command to send to the slave
992 making this a defereable allow config to tweak this
993 in order to e.g. manage dependancies
994 """
995 downloads = self.build.getProperty("repo_downloads", [])
996
997
998 for change in self.build.allChanges():
999 if (change.properties.has_key("event.type") and
1000 change.properties["event.type"] == "patchset-created"):
1001 downloads.append("%s %s/%s"% (change.properties["event.change.project"],
1002 change.properties["event.change.number"],
1003 change.properties["event.patchSet.number"]))
1004
1005
1006
1007
1008 for propName in ["repo_d"] + ["repo_d%d" % i for i in xrange(0,10)] + \
1009 ["repo_download"] + ["repo_download%d" % i for i in xrange(0,10)]:
1010 s = self.build.getProperty(propName)
1011 if s is not None:
1012 downloads.extend(self.parseDownloadProperty(s))
1013
1014 if downloads:
1015 self.args["repo_downloads"] = downloads
1016 self.setProperty("repo_downloads", downloads)
1017 return defer.succeed(None)
1018
1019 - def startVC(self, branch, revision, patch):
1020 self.args['manifest_url'] = self.manifest_url
1021
1022
1023 self.args['manifest_override_url'] = None
1024 try:
1025 self.args['manifest_override_url'] = self.build.getProperty("manifest_override_url")
1026 except KeyError:
1027 pass
1028
1029 d = self.buildDownloadList()
1030 d.addCallback(self.continueStartVC, branch, revision, patch)
1031 d.addErrback(self.failed)
1032
1040
1042 repo_downloaded = []
1043 if cmd.updates.has_key("repo_downloaded"):
1044 repo_downloaded = cmd.updates["repo_downloaded"][-1]
1045 if repo_downloaded:
1046 self.setProperty("repo_downloaded", str(repo_downloaded), "Source")
1047 else:
1048 repo_downloaded = []
1049 orig_downloads = self.getProperty("repo_downloads") or []
1050 if len(orig_downloads) != len(repo_downloaded):
1051 self.step_status.setText(["repo download issues"])
1052
1053
1055 """Check out a source tree from a bzr (Bazaar) repository at 'repourl'.
1056
1057 """
1058
1059 name = "bzr"
1060
1061 renderables = [ 'repourl', 'baseURL' ]
1062
1063 - def __init__(self, repourl=None, baseURL=None, defaultBranch=None,
1064 forceSharedRepo=None,
1065 **kwargs):
1066 """
1067 @type repourl: string
1068 @param repourl: the URL which points at the bzr repository. This
1069 is used as the default branch. Using C{repourl} does
1070 not enable builds of alternate branches: use
1071 C{baseURL} to enable this. Use either C{repourl} or
1072 C{baseURL}, not both.
1073
1074 @param baseURL: if branches are enabled, this is the base URL to
1075 which a branch name will be appended. It should
1076 probably end in a slash. Use exactly one of
1077 C{repourl} and C{baseURL}.
1078
1079 @param defaultBranch: if branches are enabled, this is the branch
1080 to use if the Build does not specify one
1081 explicitly. It will simply be appended to
1082 C{baseURL} and the result handed to the
1083 'bzr checkout pull' command.
1084
1085
1086 @param forceSharedRepo: Boolean, defaults to False. If set to True,
1087 the working directory will be made into a
1088 bzr shared repository if it is not already.
1089 Shared repository greatly reduces the amount
1090 of history data that needs to be downloaded
1091 if not using update/copy mode, or if using
1092 update/copy mode with multiple branches.
1093 """
1094 self.repourl = _ComputeRepositoryURL(repourl)
1095 self.baseURL = _ComputeRepositoryURL(baseURL)
1096 self.branch = defaultBranch
1097 Source.__init__(self, **kwargs)
1098 self.addFactoryArguments(repourl=repourl,
1099 baseURL=baseURL,
1100 defaultBranch=defaultBranch,
1101 forceSharedRepo=forceSharedRepo
1102 )
1103 self.args.update({'forceSharedRepo': forceSharedRepo})
1104 if repourl and baseURL:
1105 raise ValueError("you must provide exactly one of repourl and"
1106 " baseURL")
1107
1113
1114 - def startVC(self, branch, revision, patch):
1138
1139
1141 """Check out a source tree from a mercurial repository 'repourl'."""
1142
1143 name = "hg"
1144
1145 renderables = [ 'repourl', 'baseURL' ]
1146
1147 - def __init__(self, repourl=None, baseURL=None, defaultBranch=None,
1148 branchType='dirname', clobberOnBranchChange=True, **kwargs):
1149 """
1150 @type repourl: string
1151 @param repourl: the URL which points at the Mercurial repository.
1152 This uses the 'default' branch unless defaultBranch is
1153 specified below and the C{branchType} is set to
1154 'inrepo'. It is an error to specify a branch without
1155 setting the C{branchType} to 'inrepo'.
1156
1157 @param baseURL: if 'dirname' branches are enabled, this is the base URL
1158 to which a branch name will be appended. It should
1159 probably end in a slash. Use exactly one of C{repourl}
1160 and C{baseURL}.
1161
1162 @param defaultBranch: if branches are enabled, this is the branch
1163 to use if the Build does not specify one
1164 explicitly.
1165 For 'dirname' branches, It will simply be
1166 appended to C{baseURL} and the result handed to
1167 the 'hg update' command.
1168 For 'inrepo' branches, this specifies the named
1169 revision to which the tree will update after a
1170 clone.
1171
1172 @param branchType: either 'dirname' or 'inrepo' depending on whether
1173 the branch name should be appended to the C{baseURL}
1174 or the branch is a mercurial named branch and can be
1175 found within the C{repourl}
1176
1177 @param clobberOnBranchChange: boolean, defaults to True. If set and
1178 using inrepos branches, clobber the tree
1179 at each branch change. Otherwise, just
1180 update to the branch.
1181 """
1182 self.repourl = _ComputeRepositoryURL(repourl)
1183 self.baseURL = _ComputeRepositoryURL(baseURL)
1184 self.branch = defaultBranch
1185 self.branchType = branchType
1186 self.clobberOnBranchChange = clobberOnBranchChange
1187 Source.__init__(self, **kwargs)
1188 self.addFactoryArguments(repourl=repourl,
1189 baseURL=baseURL,
1190 defaultBranch=defaultBranch,
1191 branchType=branchType,
1192 clobberOnBranchChange=clobberOnBranchChange,
1193 )
1194 if repourl and baseURL:
1195 raise ValueError("you must provide exactly one of repourl and"
1196 " baseURL")
1197
1198 - def startVC(self, branch, revision, patch):
1199 slavever = self.slaveVersion("hg")
1200 if not slavever:
1201 raise BuildSlaveTooOldError("slave is too old, does not know "
1202 "about hg")
1203
1204 if self.repourl:
1205
1206 assert self.branchType == 'inrepo' or not branch
1207 self.args['repourl'] = self.repourl
1208 if branch:
1209 self.args['branch'] = branch
1210 else:
1211 self.args['repourl'] = self.baseURL + (branch or '')
1212 self.args['revision'] = revision
1213 self.args['patch'] = patch
1214 self.args['clobberOnBranchChange'] = self.clobberOnBranchChange
1215 self.args['branchType'] = self.branchType
1216
1217 revstuff = []
1218 if branch is not None and branch != self.branch:
1219 revstuff.append("[branch]")
1220 self.description.extend(revstuff)
1221 self.descriptionDone.extend(revstuff)
1222
1223 cmd = RemoteCommand("hg", self.args)
1224 self.startCommand(cmd)
1225
1227 if not changes:
1228 return None
1229
1230
1231
1232
1233 if len(changes) > 1:
1234 log.msg("Mercurial.computeSourceRevision: warning: "
1235 "there are %d changes here, assuming the last one is "
1236 "the most recent" % len(changes))
1237 return changes[-1].revision
1238
1239
1241 """ P4 is a class for accessing perforce revision control"""
1242 name = "p4"
1243
1244 renderables = [ 'p4base' ]
1245
1246 - def __init__(self, p4base=None, defaultBranch=None, p4port=None, p4user=None,
1247 p4passwd=None, p4extra_views=[], p4line_end='local',
1248 p4client='buildbot_%(slave)s_%(builder)s', **kwargs):
1249 """
1250 @type p4base: string
1251 @param p4base: A view into a perforce depot, typically
1252 "//depot/proj/"
1253
1254 @type defaultBranch: string
1255 @param defaultBranch: Identify a branch to build by default. Perforce
1256 is a view based branching system. So, the branch
1257 is normally the name after the base. For example,
1258 branch=1.0 is view=//depot/proj/1.0/...
1259 branch=1.1 is view=//depot/proj/1.1/...
1260
1261 @type p4port: string
1262 @param p4port: Specify the perforce server to connection in the format
1263 <host>:<port>. Example "perforce.example.com:1666"
1264
1265 @type p4user: string
1266 @param p4user: The perforce user to run the command as.
1267
1268 @type p4passwd: string
1269 @param p4passwd: The password for the perforce user.
1270
1271 @type p4extra_views: list of tuples
1272 @param p4extra_views: Extra views to be added to
1273 the client that is being used.
1274
1275 @type p4line_end: string
1276 @param p4line_end: value of the LineEnd client specification property
1277
1278 @type p4client: string
1279 @param p4client: The perforce client to use for this buildslave.
1280 """
1281
1282 self.p4base = _ComputeRepositoryURL(p4base)
1283 self.branch = defaultBranch
1284 Source.__init__(self, **kwargs)
1285 self.addFactoryArguments(p4base=p4base,
1286 defaultBranch=defaultBranch,
1287 p4port=p4port,
1288 p4user=p4user,
1289 p4passwd=p4passwd,
1290 p4extra_views=p4extra_views,
1291 p4line_end=p4line_end,
1292 p4client=p4client,
1293 )
1294 self.args['p4port'] = p4port
1295 self.args['p4user'] = p4user
1296 self.args['p4passwd'] = p4passwd
1297 self.args['p4extra_views'] = p4extra_views
1298 self.args['p4line_end'] = p4line_end
1299 self.p4client = p4client
1300
1307
1313
1314 - def startVC(self, branch, revision, patch):
1324
1326 """
1327 DEPRECATED - will be removed in 0.8.5.
1328
1329 This is a partial solution for using a P4 source repository. You are
1330 required to manually set up each build slave with a useful P4
1331 environment, which means setting various per-slave environment variables,
1332 and creating a P4 client specification which maps the right files into
1333 the slave's working directory. Once you have done that, this step merely
1334 performs a 'p4 sync' to update that workspace with the newest files.
1335
1336 Each slave needs the following environment:
1337
1338 - PATH: the 'p4' binary must be on the slave's PATH
1339 - P4USER: each slave needs a distinct user account
1340 - P4CLIENT: each slave needs a distinct client specification
1341
1342 You should use 'p4 client' (?) to set up a client view spec which maps
1343 the desired files into $SLAVEBASE/$BUILDERBASE/source .
1344 """
1345
1346 name = "p4sync"
1347
1348 - def __init__(self, p4port, p4user, p4passwd, p4client, **kwargs):
1349 assert kwargs['mode'] == "copy", "P4Sync can only be used in mode=copy"
1350 self.branch = None
1351 Source.__init__(self, **kwargs)
1352 self.addFactoryArguments(p4port=p4port,
1353 p4user=p4user,
1354 p4passwd=p4passwd,
1355 p4client=p4client,
1356 )
1357 self.args['p4port'] = p4port
1358 self.args['p4user'] = p4user
1359 self.args['p4passwd'] = p4passwd
1360 self.args['p4client'] = p4client
1361
1367
1368 - def startVC(self, branch, revision, patch):
1373
1374
1376 """Check out a source tree from a monotone repository 'repourl'."""
1377
1378 name = "mtn"
1379
1380 renderables = [ 'repourl' ]
1381
1382 - def __init__(self, repourl=None, branch=None, progress=False, **kwargs):
1383 """
1384 @type repourl: string
1385 @param repourl: the URI which points at the monotone repository.
1386
1387 @type branch: string
1388 @param branch: The branch or tag to check out by default. If
1389 a build specifies a different branch, it will
1390 be used instead of this.
1391
1392 @type progress: boolean
1393 @param progress: Pass the --ticker=dot option when pulling. This
1394 can solve long fetches getting killed due to
1395 lack of output.
1396 """
1397 Source.__init__(self, **kwargs)
1398 self.repourl = _ComputeRepositoryURL(repourl)
1399 if (not repourl):
1400 raise ValueError("you must provide a repository uri in 'repourl'")
1401 if (not branch):
1402 raise ValueError("you must provide a default branch in 'branch'")
1403 self.addFactoryArguments(repourl=repourl,
1404 branch=branch,
1405 progress=progress,
1406 )
1407 self.args.update({'branch': branch,
1408 'progress': progress,
1409 })
1410
1411 - def startVC(self, branch, revision, patch):
1425
1427 if not changes:
1428 return None
1429
1430
1431
1432
1433 if len(changes) > 1:
1434 log.msg("Monotone.computeSourceRevision: warning: "
1435 "there are %d changes here, assuming the last one is "
1436 "the most recent" % len(changes))
1437 return changes[-1].revision
1438