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

Source Code for Module buildbot.steps.source.mercurial

  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  ## Source step code for mercurial 
 17   
 18  from twisted.python import log 
 19  from twisted.internet import defer 
 20   
 21  from buildbot.process import buildstep 
 22  from buildbot.steps.source import Source 
 23  from buildbot.interfaces import BuildSlaveTooOldError 
 24  from buildbot.config import ConfigErrors 
25 26 -class Mercurial(Source):
27 """ Class for Mercurial with all the smarts """ 28 name = "hg" 29 30 renderables = [ "repourl" ] 31 possible_modes = ('incremental', 'full') 32 possible_methods = (None, 'clean', 'fresh', 'clobber') 33 possible_branchTypes = ('inrepo', 'dirname') 34
35 - def __init__(self, repourl=None, mode='incremental', 36 method=None, defaultBranch=None, branchType='dirname', 37 clobberOnBranchChange=True, **kwargs):
38 39 """ 40 @type repourl: string 41 @param repourl: the URL which points at the Mercurial repository. 42 if 'dirname' branches are enabled, this is the base URL 43 to which a branch name will be appended. It should 44 probably end in a slash. 45 46 @param defaultBranch: if branches are enabled, this is the branch 47 to use if the Build does not specify one 48 explicitly. 49 For 'dirname' branches, It will simply be 50 appended to C{repourl} and the result handed to 51 the 'hg update' command. 52 For 'inrepo' branches, this specifies the named 53 revision to which the tree will update after a 54 clone. 55 56 @param branchType: either 'dirname' or 'inrepo' depending on whether 57 the branch name should be appended to the C{repourl} 58 or the branch is a mercurial named branch and can be 59 found within the C{repourl} 60 61 @param clobberOnBranchChange: boolean, defaults to True. If set and 62 using inrepos branches, clobber the tree 63 at each branch change. Otherwise, just 64 update to the branch. 65 """ 66 67 self.repourl = repourl 68 self.defaultBranch = self.branch = defaultBranch 69 self.branchType = branchType 70 self.method = method 71 self.clobberOnBranchChange = clobberOnBranchChange 72 self.mode = mode 73 Source.__init__(self, **kwargs) 74 self.addFactoryArguments(repourl=repourl, 75 mode=mode, 76 method=method, 77 defaultBranch=defaultBranch, 78 branchType=branchType, 79 clobberOnBranchChange= 80 clobberOnBranchChange, 81 ) 82 83 errors = [] 84 if self.mode not in self.possible_modes: 85 errors.append("mode %s is not one of %s" % 86 (self.mode, self.possible_modes)) 87 if self.method not in self.possible_methods: 88 errors.append("method %s is not one of %s" % 89 (self.method, self.possible_methods)) 90 if self.branchType not in self.possible_branchTypes: 91 errors.append("branchType %s is not one of %s" % 92 (self.branchType, self.possible_branchTypes)) 93 94 if repourl is None: 95 errors.append("you must privide a repourl") 96 97 if errors: 98 raise ConfigErrors(errors)
99
100 - def startVC(self, branch, revision, patch):
101 self.revision = revision 102 self.method = self._getMethod() 103 self.stdio_log = self.addLog("stdio") 104 d = self.checkHg() 105 def checkInstall(hgInstalled): 106 if not hgInstalled: 107 raise BuildSlaveTooOldError("Mercurial is not installed on slave") 108 return 0
109 110 if self.branchType == 'dirname': 111 self.repourl = self.repourl + (branch or '') 112 self.branch = self.defaultBranch 113 self.update_branch = branch 114 elif self.branchType == 'inrepo': 115 self.update_branch = (branch or 'default') 116 117 if self.mode == 'full': 118 d.addCallback(lambda _: self.full()) 119 elif self.mode == 'incremental': 120 d.addCallback(lambda _: self.incremental()) 121 d.addCallback(self.parseGotRevision) 122 d.addCallback(self.finish) 123 d.addErrback(self.failed)
124 125 @defer.deferredGenerator
126 - def full(self):
127 if self.method == 'clobber': 128 d = self.clobber(None) 129 wfd = defer.waitForDeferred(d) 130 yield wfd 131 wfd.getResult() 132 return 133 134 wfd = defer.waitForDeferred(self._sourcedirIsUpdatable()) 135 yield wfd 136 updatable = wfd.getResult() 137 if not updatable: 138 d = self._dovccmd(['clone', self.repourl, '.']) 139 elif self.method == 'clean': 140 d = self.clean(None) 141 elif self.method == 'fresh': 142 d = self.fresh(None) 143 wfd = defer.waitForDeferred(d) 144 yield wfd 145 wfd.getResult()
146
147 - def incremental(self):
148 if self.method is not None: 149 raise ValueError(self.method) 150 151 d = self._sourcedirIsUpdatable() 152 def _cmd(updatable): 153 if updatable: 154 command = ['pull', self.repourl, '--update'] 155 else: 156 command = ['clone', self.repourl, '.', '--noupdate'] 157 return command
158 159 d.addCallback(_cmd) 160 d.addCallback(self._dovccmd) 161 d.addCallback(self._checkBranchChange) 162 return d 163
164 - def clean(self, _):
165 command = ['--config', 'extensions.purge=', 'purge'] 166 d = self._dovccmd(command) 167 d.addCallback(self._pullUpdate) 168 return d
169
170 - def clobber(self, _):
171 cmd = buildstep.RemoteCommand('rmdir', {'dir': self.workdir, 172 'logEnviron':self.logEnviron}) 173 cmd.useLog(self.stdio_log, False) 174 d = self.runCommand(cmd) 175 d.addCallback(lambda _: self._dovccmd(['clone', '--noupdate' 176 , self.repourl, "."])) 177 d.addCallback(self._update) 178 return d
179
180 - def fresh(self, _):
181 command = ['--config', 'extensions.purge=', 'purge', '--all'] 182 d = self._dovccmd(command) 183 d.addCallback(self._pullUpdate) 184 return d
185
186 - def finish(self, res):
187 d = defer.succeed(res) 188 def _gotResults(results): 189 self.setStatus(self.cmd, results) 190 return results
191 d.addCallback(_gotResults) 192 d.addCallbacks(self.finished, self.checkDisconnect) 193 return d 194
195 - def parseGotRevision(self, _):
196 d = self._dovccmd(['identify', '--id', '--debug'], collectStdout=True) 197 def _setrev(stdout): 198 revision = stdout.strip() 199 if len(revision) != 40: 200 raise ValueError("Incorrect revision id") 201 log.msg("Got Mercurial revision %s" % (revision, )) 202 self.setProperty('got_revision', revision, 'Source') 203 return 0
204 d.addCallback(_setrev) 205 return d 206 207 @defer.deferredGenerator
208 - def _checkBranchChange(self, _):
209 d = self._getCurrentBranch() 210 wfd = defer.waitForDeferred(d) 211 yield wfd 212 current_branch = wfd.getResult() 213 msg = "Working dir is on in-repo branch '%s' and build needs '%s'." % \ 214 (current_branch, self.update_branch) 215 if current_branch != self.update_branch: 216 if self.clobberOnBranchChange: 217 msg += ' Clobbering.' 218 log.msg(msg) 219 d = self.clobber(None) 220 else: 221 msg += ' Updating.' 222 log.msg(msg) 223 d = self._update(None) 224 else: 225 msg += ' Updating.' 226 log.msg(msg) 227 d = self._update(None) 228 229 wfd = defer.waitForDeferred(d) 230 yield wfd 231 wfd.getResult()
232
233 - def _pullUpdate(self, res):
234 command = ['pull' , self.repourl] 235 if self.revision: 236 command.extend(['--rev', self.revision]) 237 d = self._dovccmd(command) 238 d.addCallback(self._checkBranchChange) 239 return d
240
241 - def _dovccmd(self, command, collectStdout=False):
242 if not command: 243 raise ValueError("No command specified") 244 cmd = buildstep.RemoteShellCommand(self.workdir, ['hg', '--verbose'] + command, 245 env=self.env, 246 logEnviron=self.logEnviron, 247 collectStdout=collectStdout) 248 cmd.useLog(self.stdio_log, False) 249 log.msg("Starting mercurial command : hg %s" % (" ".join(command), )) 250 d = self.runCommand(cmd) 251 def evaluateCommand(cmd): 252 if cmd.rc != 0: 253 log.msg("Source step failed while running command %s" % cmd) 254 raise buildstep.BuildStepFailed() 255 if collectStdout: 256 return cmd.stdout 257 else: 258 return cmd.rc
259 d.addCallback(lambda _: evaluateCommand(cmd)) 260 return d 261
262 - def computeSourceRevision(self, changes):
263 if not changes: 264 return None 265 # without knowing the revision ancestry graph, we can't sort the 266 # changes at all. So for now, assume they were given to us in sorted 267 # order, and just pay attention to the last one. See ticket #103 for 268 # more details. 269 if len(changes) > 1: 270 log.msg("Mercurial.computeSourceRevision: warning: " 271 "there are %d changes here, assuming the last one is " 272 "the most recent" % len(changes)) 273 return changes[-1].revision
274
275 - def _getCurrentBranch(self):
276 if self.branchType == 'dirname': 277 return defer.succeed(self.branch) 278 else: 279 d = self._dovccmd(['identify', '--branch'], collectStdout=True) 280 def _getbranch(stdout): 281 return stdout.strip()
282 d.addCallback(_getbranch).addErrback 283 return d 284
285 - def _getMethod(self):
286 if self.method is not None and self.mode != 'incremental': 287 return self.method 288 elif self.mode == 'incremental': 289 return None 290 elif self.method is None and self.mode == 'full': 291 return 'fresh'
292
293 - def _sourcedirIsUpdatable(self):
294 cmd = buildstep.RemoteCommand('stat', {'file': self.workdir + '/.hg', 295 'logEnviron': self.logEnviron}) 296 cmd.useLog(self.stdio_log, False) 297 d = self.runCommand(cmd) 298 def _fail(tmp): 299 if cmd.rc != 0: 300 return False 301 return True
302 d.addCallback(_fail) 303 return d 304
305 - def _update(self, _):
306 command = ['update', '--clean'] 307 if self.revision: 308 command += ['--rev', self.revision] 309 d = self._dovccmd(command) 310 return d
311
312 - def checkHg(self):
313 d = self._dovccmd(['--version']) 314 def check(res): 315 if res == 0: 316 return True 317 return False
318 d.addCallback(check) 319 return d 320