Package buildbot :: Package steps :: Package source :: Module base
[frames] | no frames]

Source Code for Module buildbot.steps.source.base

  1  # This file is part of Buildbot.  Buildbot is free software: you can 
  2  # redistribute it and/or modify it under the terms of the GNU General Public 
  3  # License as published by the Free Software Foundation, version 2. 
  4  # 
  5  # This program is distributed in the hope that it will be useful, but WITHOUT 
  6  # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 
  7  # FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more 
  8  # details. 
  9  # 
 10  # You should have received a copy of the GNU General Public License along with 
 11  # this program; if not, write to the Free Software Foundation, Inc., 51 
 12  # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 
 13  # 
 14  # Copyright Buildbot Team Members 
 15   
 16   
 17  from twisted.python import log 
 18  from buildbot.process.buildstep import LoggingBuildStep 
 19  from buildbot.status.builder import SKIPPED, FAILURE 
 20  from buildbot.steps.slave import CompositeStepMixin 
 21   
22 -class Source(LoggingBuildStep, CompositeStepMixin):
23 """This is a base class to generate a source tree in the buildslave. 24 Each version control system has a specialized subclass, and is expected 25 to override __init__ and implement computeSourceRevision() and 26 startVC(). The class as a whole builds up the self.args dictionary, then 27 starts a RemoteCommand with those arguments. 28 """ 29 30 renderables = LoggingBuildStep.renderables + [ 31 'description', 'descriptionDone', 'descriptionSuffix', 32 'workdir' ] 33 34 description = None # set this to a list of short strings to override 35 descriptionDone = None # alternate description when the step is complete 36 descriptionSuffix = None # extra information to append to suffix 37 38 # if the checkout fails, there's no point in doing anything else 39 haltOnFailure = True 40 flunkOnFailure = True 41 notReally = False 42 43 branch = None # the default branch, should be set in __init__ 44
45 - def __init__(self, workdir=None, mode='update', alwaysUseLatest=False, 46 timeout=20*60, retry=None, env=None, logEnviron=True, 47 description=None, descriptionDone=None, descriptionSuffix=None, 48 codebase='', **kwargs):
49 """ 50 @type workdir: string 51 @param workdir: local directory (relative to the Builder's root) 52 where the tree should be placed 53 54 @type alwaysUseLatest: boolean 55 @param alwaysUseLatest: whether to always update to the most 56 recent available sources for this build. 57 58 Normally the Source step asks its Build for a list of all 59 Changes that are supposed to go into the build, then computes a 60 'source stamp' (revision number or timestamp) that will cause 61 exactly that set of changes to be present in the checked out 62 tree. This is turned into, e.g., 'cvs update -D timestamp', or 63 'svn update -r revnum'. If alwaysUseLatest=True, bypass this 64 computation and always update to the latest available sources 65 for each build. 66 67 The source stamp helps avoid a race condition in which someone 68 commits a change after the master has decided to start a build 69 but before the slave finishes checking out the sources. At best 70 this results in a build which contains more changes than the 71 buildmaster thinks it has (possibly resulting in the wrong 72 person taking the blame for any problems that result), at worst 73 is can result in an incoherent set of sources (splitting a 74 non-atomic commit) which may not build at all. 75 76 @type logEnviron: boolean 77 @param logEnviron: If this option is true (the default), then the 78 step's logfile will describe the environment 79 variables on the slave. In situations where the 80 environment is not relevant and is long, it may 81 be easier to set logEnviron=False. 82 83 @type codebase: string 84 @param codebase: Specifies which changes in a build are processed by 85 the step. The default codebase value is ''. The codebase must correspond 86 to a codebase assigned by the codebaseGenerator. If no codebaseGenerator 87 is defined in the master then codebase doesn't need to be set, the 88 default value will then match all changes. 89 """ 90 91 LoggingBuildStep.__init__(self, **kwargs) 92 93 # This will get added to args later, after properties are rendered 94 self.workdir = workdir 95 96 self.sourcestamp = None 97 98 self.codebase = codebase 99 if self.codebase: 100 self.name = ' '.join((self.name, self.codebase)) 101 102 self.alwaysUseLatest = alwaysUseLatest 103 104 self.logEnviron = logEnviron 105 self.env = env 106 self.timeout = timeout 107 108 descriptions_for_mode = { 109 "clobber": "checkout", 110 "export": "exporting"} 111 descriptionDones_for_mode = { 112 "clobber": "checkout", 113 "export": "export"} 114 if description: 115 self.description = description 116 else: 117 self.description = [ 118 descriptions_for_mode.get(mode, "updating")] 119 if isinstance(self.description, str): 120 self.description = [self.description] 121 122 if descriptionDone: 123 self.descriptionDone = descriptionDone 124 else: 125 self.descriptionDone = [ 126 descriptionDones_for_mode.get(mode, "update")] 127 if isinstance(self.descriptionDone, str): 128 self.descriptionDone = [self.descriptionDone] 129 130 if descriptionSuffix: 131 self.descriptionSuffix = descriptionSuffix 132 else: 133 self.descriptionSuffix = self.codebase or None # want None in lieu of '' 134 if isinstance(self.descriptionSuffix, str): 135 self.descriptionSuffix = [self.descriptionSuffix]
136
137 - def updateSourceProperty(self, name, value, source=''):
138 """ 139 Update a property, indexing the property by codebase if codebase is not 140 ''. Source steps should generally use this instead of setProperty. 141 """ 142 # pick a decent source name 143 if source == '': 144 source = self.__class__.__name__ 145 146 if self.codebase != '': 147 assert not isinstance(self.getProperty(name, None), str), \ 148 "Sourcestep %s has a codebase, other sourcesteps don't" \ 149 % self.name 150 property_dict = self.getProperty(name, {}) 151 property_dict[self.codebase] = value 152 LoggingBuildStep.setProperty(self, name, property_dict, source) 153 else: 154 assert not isinstance(self.getProperty(name, None), dict), \ 155 "Sourcestep %s does not have a codebase, other sourcesteps do" \ 156 % self.name 157 LoggingBuildStep.setProperty(self, name, value, source)
158
159 - def setStepStatus(self, step_status):
161
162 - def setDefaultWorkdir(self, workdir):
163 self.workdir = self.workdir or workdir
164
165 - def describe(self, done=False):
166 desc = self.descriptionDone if done else self.description 167 if self.descriptionSuffix: 168 desc = desc[:] 169 desc.extend(self.descriptionSuffix) 170 return desc
171
172 - def computeSourceRevision(self, changes):
173 """Each subclass must implement this method to do something more 174 precise than -rHEAD every time. For version control systems that use 175 repository-wide change numbers (SVN, P4), this can simply take the 176 maximum such number from all the changes involved in this build. For 177 systems that do not (CVS), it needs to create a timestamp based upon 178 the latest Change, the Build's treeStableTimer, and an optional 179 self.checkoutDelay value.""" 180 return None
181
182 - def start(self):
183 if self.notReally: 184 log.msg("faking %s checkout/update" % self.name) 185 self.step_status.setText(["fake", self.name, "successful"]) 186 self.addCompleteLog("log", 187 "Faked %s checkout/update 'successful'\n" \ 188 % self.name) 189 return SKIPPED 190 191 if not self.alwaysUseLatest: 192 # what source stamp would this step like to use? 193 s = self.build.getSourceStamp(self.codebase) 194 self.sourcestamp = s 195 196 if self.sourcestamp: 197 # if branch is None, then use the Step's "default" branch 198 branch = s.branch or self.branch 199 # if revision is None, use the latest sources (-rHEAD) 200 revision = s.revision 201 if not revision: 202 revision = self.computeSourceRevision(s.changes) 203 # the revision property is currently None, so set it to something 204 # more interesting 205 if revision is not None: 206 self.updateSourceProperty('revision', str(revision)) 207 208 # if patch is None, then do not patch the tree after checkout 209 210 # 'patch' is None or a tuple of (patchlevel, diff, root) 211 # root is optional. 212 patch = s.patch 213 if patch: 214 self.addCompleteLog("patch", patch[1]) 215 else: 216 log.msg("No sourcestamp found in build for codebase '%s'" % self.codebase) 217 self.step_status.setText(["Codebase", '%s' % self.codebase ,"not", "in", "build" ]) 218 self.addCompleteLog("log", 219 "No sourcestamp found in build for codebase '%s'" \ 220 % self.codebase) 221 self.finished(FAILURE) 222 return FAILURE 223 224 else: 225 revision = None 226 branch = self.branch 227 patch = None 228 229 self.startVC(branch, revision, patch)
230