1   
  2   
  3   
  4   
  5   
  6   
  7   
  8   
  9   
 10   
 11   
 12   
 13   
 14   
 15   
 16   
 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 
 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 
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   
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   
165          command = ['--config', 'extensions.purge=', 'purge'] 
166          d =  self._dovccmd(command) 
167          d.addCallback(self._pullUpdate) 
168          return d 
 169   
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   
181          command = ['--config', 'extensions.purge=', 'purge', '--all'] 
182          d = self._dovccmd(command) 
183          d.addCallback(self._pullUpdate) 
184          return d 
 185   
191          d.addCallback(_gotResults) 
192          d.addCallbacks(self.finished, self.checkDisconnect) 
193          return d 
194   
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 
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   
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): 
 259          d.addCallback(lambda _: evaluateCommand(cmd)) 
260          return d 
261   
263          if not changes: 
264              return None 
265           
266           
267           
268           
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   
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   
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   
302          d.addCallback(_fail) 
303          return d 
304   
311   
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