Package buildslave :: Package commands :: Module hg
[frames] | no frames]

Source Code for Module buildslave.commands.hg

  1  import os, re 
  2   
  3  from twisted.python import log, runtime 
  4   
  5  from buildslave.commands.base import SourceBaseCommand, AbandonChain 
  6  from buildslave import runprocess 
  7  from buildslave.commands import utils 
  8  from buildslave.util import remove_userpassword 
  9   
 10   
11 -class Mercurial(SourceBaseCommand):
12 """Mercurial specific VC operation. In addition to the arguments 13 handled by SourceBaseCommand, this command reads the following keys: 14 15 ['repourl'] (required): the Mercurial repository string 16 ['clobberOnBranchChange']: Document me. See ticket #462. 17 """ 18 19 header = "mercurial operation" 20
21 - def setup(self, args):
22 SourceBaseCommand.setup(self, args) 23 self.repourl = args['repourl'] 24 self.clobberOnBranchChange = args.get('clobberOnBranchChange', True) 25 self.sourcedata = "%s\n" % self.repourl 26 self.branchType = args.get('branchType', 'dirname') 27 self.stdout = "" 28 self.stderr = "" 29 self.clobbercount = 0 # n times we've clobbered
30
31 - def sourcedirIsUpdateable(self):
32 return os.path.isdir(os.path.join(self.builder.basedir, 33 self.srcdir, ".hg"))
34
35 - def doVCUpdate(self):
36 hg = self.getCommand('hg') 37 d = os.path.join(self.builder.basedir, self.srcdir) 38 command = [hg, 'pull', '--verbose', self.repourl] 39 c = runprocess.RunProcess(self.builder, command, d, 40 sendRC=False, timeout=self.timeout, 41 maxTime=self.maxTime, keepStdout=True, usePTY=False) 42 self.command = c 43 d = c.start() 44 d.addCallback(self._handleEmptyUpdate) 45 d.addCallback(self._update) 46 return d
47
48 - def _handleEmptyUpdate(self, res):
49 if type(res) is int and res == 1: 50 if self.command.stdout.find("no changes found") != -1: 51 # 'hg pull', when it doesn't have anything to do, exits with 52 # rc=1, and there appears to be no way to shut this off. It 53 # emits a distinctive message to stdout, though. So catch 54 # this and pretend that it completed successfully. 55 return 0 56 return res
57
58 - def doVCFull(self):
59 hg = self.getCommand('hg') 60 command = [hg, 'clone', '--verbose', '--noupdate'] 61 62 # if got revision, clobbering and in dirname, only clone to specific revision 63 # (otherwise, do full clone to re-use .hg dir for subsequent builds) 64 if self.args.get('revision') and self.mode == 'clobber' and self.branchType == 'dirname': 65 command.extend(['--rev', self.args.get('revision')]) 66 command.extend([self.repourl, self.srcdir]) 67 68 c = runprocess.RunProcess(self.builder, command, self.builder.basedir, 69 sendRC=False, timeout=self.timeout, 70 maxTime=self.maxTime, usePTY=False) 71 self.command = c 72 cmd1 = c.start() 73 cmd1.addCallback(self._update) 74 return cmd1
75
76 - def _clobber(self, dummy, dirname):
77 self.clobbercount += 1 78 79 if self.clobbercount > 3: 80 raise Exception, "Too many clobber attempts. Aborting step" 81 82 def _vcfull(res): 83 return self.doVCFull()
84 85 c = self.doClobber(dummy, dirname) 86 c.addCallback(_vcfull) 87 88 return c
89
90 - def _purge(self, dummy, dirname):
91 hg = self.getCommand('hg') 92 d = os.path.join(self.builder.basedir, self.srcdir) 93 purge = [hg, 'purge', '--all'] 94 purgeCmd = runprocess.RunProcess(self.builder, purge, d, 95 keepStdout=True, keepStderr=True, usePTY=False) 96 97 def _clobber(res): 98 if res != 0: 99 # purge failed, we need to switch to a classic clobber 100 msg = "'hg purge' failed: %s\n%s. Clobbering." % (purgeCmd.stdout, purgeCmd.stderr) 101 self.sendStatus({'header': msg + "\n"}) 102 log.msg(msg) 103 104 return self._clobber(dummy, dirname) 105 106 # Purge was a success, then we need to update 107 return self._update2(res)
108 109 p = purgeCmd.start() 110 p.addCallback(_clobber) 111 return p 112
113 - def _update(self, res):
114 hg = self.getCommand('hg') 115 if res != 0: 116 return res 117 118 # compare current branch to update 119 self.update_branch = self.args.get('branch', 'default') 120 121 d = os.path.join(self.builder.basedir, self.srcdir) 122 parentscmd = [hg, 'identify', '--num', '--branch'] 123 cmd = runprocess.RunProcess(self.builder, parentscmd, d, 124 sendRC=False, timeout=self.timeout, keepStdout=True, 125 keepStderr=True, usePTY=False) 126 127 self.clobber = None 128 129 def _parseIdentify(res): 130 if res != 0: 131 msg = "'hg identify' failed." 132 self.sendStatus({'header': msg + "\n"}) 133 log.msg(msg) 134 raise AbandonChain(-1) 135 136 log.msg('Output: %s' % cmd.stdout) 137 138 match = re.search(r'^(.+) (.+)$', cmd.stdout) 139 if not match: 140 msg = "'hg identify' did not give a recognizable output" 141 self.sendStatus({'header': msg + "\n"}) 142 log.msg(msg) 143 raise AbandonChain(-1) 144 145 rev = match.group(1) 146 current_branch = match.group(2) 147 148 if rev == '-1': 149 msg = "Fresh hg repo, don't worry about in-repo branch name" 150 log.msg(msg) 151 152 elif self.sourcedirIsPatched(): 153 self.clobber = self._purge 154 155 elif self.update_branch != current_branch: 156 msg = "Working dir is on in-repo branch '%s' and build needs '%s'." % (current_branch, self.update_branch) 157 if self.clobberOnBranchChange: 158 msg += ' Cloberring.' 159 else: 160 msg += ' Updating.' 161 162 self.sendStatus({'header': msg + "\n"}) 163 log.msg(msg) 164 165 # Clobbers only if clobberOnBranchChange is set 166 if self.clobberOnBranchChange: 167 self.clobber = self._purge 168 169 else: 170 msg = "Working dir on same in-repo branch as build (%s)." % (current_branch) 171 log.msg(msg) 172 173 return 0
174 175 def _checkRepoURL(res): 176 hg = self.getCommand('hg') 177 parentscmd = [hg, 'paths', 'default'] 178 cmd2 = runprocess.RunProcess(self.builder, parentscmd, d, 179 keepStdout=True, keepStderr=True, usePTY=False, 180 timeout=self.timeout, sendRC=False) 181 182 def _parseRepoURL(res): 183 if res == 1: 184 if "not found!" == cmd2.stderr.strip(): 185 msg = "hg default path not set. Not checking repo url for clobber test" 186 log.msg(msg) 187 return 0 188 else: 189 msg = "'hg paths default' failed." 190 log.msg(msg) 191 return 1 192 193 oldurl = cmd2.stdout.strip() 194 195 log.msg("Repo cloned from: '%s'" % oldurl) 196 197 if runtime.platformType == 'win32': 198 oldurl = oldurl.lower().replace('\\', '/') 199 repourl = self.repourl.lower().replace('\\', '/') 200 else: 201 repourl = self.repourl 202 203 if repourl.startswith('file://'): 204 repourl = repourl.split('file://')[1] 205 if oldurl.startswith('file://'): 206 oldurl = oldurl.split('file://')[1] 207 208 oldurl = remove_userpassword(oldurl) 209 repourl = remove_userpassword(repourl) 210 211 if oldurl.rstrip('/') != repourl.rstrip('/'): 212 self.clobber = self._clobber 213 msg = "RepoURL changed from '%s' in wc to '%s' in update. Clobbering" % (oldurl, repourl) 214 log.msg(msg) 215 216 return 0 217 218 c = cmd2.start() 219 c.addCallback(_parseRepoURL) 220 return c 221 222 def _maybeClobber(res): 223 if self.clobber: 224 msg = "Clobber flag set. Doing clobbering" 225 log.msg(msg) 226 227 def _vcfull(res): 228 return self.doVCFull() 229 230 return self.clobber(None, self.srcdir) 231 232 return 0 233 234 c = cmd.start() 235 c.addCallback(_parseIdentify) 236 c.addCallback(_checkRepoURL) 237 c.addCallback(_maybeClobber) 238 c.addCallback(self._update2) 239 return c 240
241 - def _update2(self, res):
242 hg = self.getCommand('hg') 243 updatecmd=[hg, 'update', '--clean', '--repository', self.srcdir] 244 if self.args.get('revision'): 245 updatecmd.extend(['--rev', self.args['revision']]) 246 else: 247 updatecmd.extend(['--rev', self.args.get('branch', 'default')]) 248 self.command = runprocess.RunProcess(self.builder, updatecmd, 249 self.builder.basedir, sendRC=False, 250 timeout=self.timeout, maxTime=self.maxTime, usePTY=False) 251 return self.command.start()
252
253 - def parseGotRevision(self):
254 hg = self.getCommand('hg') 255 # we use 'hg identify' to find out what we wound up with 256 command = [hg, "identify", "--id", "--debug"] # get full rev id 257 c = runprocess.RunProcess(self.builder, command, 258 os.path.join(self.builder.basedir, self.srcdir), 259 environ=self.env, timeout=self.timeout, 260 sendRC=False, 261 keepStdout=True, usePTY=False) 262 d = c.start() 263 def _parse(res): 264 m = re.search(r'^(\w+)', c.stdout) 265 return m.group(1)
266 d.addCallback(_parse) 267 return d 268