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