1   
  2   
  3   
  4   
  5   
  6   
  7   
  8   
  9   
 10   
 11   
 12   
 13   
 14   
 15   
 16   
 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 
 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 
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   
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   
169          command = ['--config', 'extensions.purge=', 'purge'] 
170          d =  self._dovccmd(command) 
171          d.addCallback(self._pullUpdate) 
172          return d 
 173   
182   
184          command = ['--config', 'extensions.purge=', 'purge', '--all'] 
185          d = self._dovccmd(command) 
186          d.addCallback(self._pullUpdate) 
187          return d 
 188   
194          d.addCallback(_gotResults) 
195          d.addCallbacks(self.finished, self.checkDisconnect) 
196          return d 
197   
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 
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   
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   
255          d.addCallback(lambda _: evaluateCommand(cmd)) 
256          return d 
257   
259          if not changes: 
260              return None 
261           
262           
263           
264           
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   
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   
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   
298          d.addCallback(_fail) 
299          return d 
300   
307   
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