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

Source Code for Module buildslave.commands.mtn

  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  import os 
 17   
 18  from twisted.python import log 
 19  from twisted.internet import defer 
 20   
 21  from buildslave.commands.base import SourceBaseCommand 
 22  from buildslave import runprocess 
 23  #from buildslave.util import remove_userpassword 
 24   
25 -class MonotoneError(Exception):
26 """Error class for this module."""
27 28
29 -class Monotone(SourceBaseCommand):
30 """Monotone specific VC operation. In addition to the arguments 31 handled by SourceBaseCommand, this command reads the following keys: 32 33 ['repourl'] (required): the Monotone repository string 34 ['branch'] (required): which branch to retrieve. 35 36 ['revision'] (optional): which revision (revision selector) 37 to retrieve. 38 ['progress'] (optional): have mtn output progress markers, 39 avoiding timeouts for long fetches; 40 """ 41 42 header = "monotone operation" 43
44 - def setup(self, args):
45 SourceBaseCommand.setup(self, args) 46 47 self.repourl = args['repourl'] 48 self.branch = args['branch'] 49 50 self.revision = args.get('revision', None) 51 self.progress = args.get('progress', False) 52 53 self._pull_timeout = args.get("timeout") 54 55 self.sourcedata = "%s?%s" % (self.repourl, self.branch) 56 self.stdout = "" 57 self.stderr = "" 58 self.database = os.path.join(self.builder.basedir, 'db.mtn') 59 self.mtn = self.getCommand("mtn")
60
61 - def start(self):
62 def cont(res): 63 # Continue with start() method in superclass. 64 return SourceBaseCommand.start(self)
65 66 d = self._checkDb(); 67 d.addCallback(cont) 68 return d
69
70 - def doVCUpdate(self):
71 return self._dovccmd(self._update, True)
72
73 - def doVCFull(self):
74 return self._dovccmd(self._checkout, True)
75
76 - def _fullSrcdir(self):
77 return os.path.join(self.builder.basedir, self.srcdir)
78
79 - def sourcedirIsUpdateable(self):
80 return os.path.isdir(os.path.join(self._fullSrcdir(), "_MTN"))
81
82 - def _dovccmd(self, fn, dopull, cb=None, **kwargs):
83 if dopull: 84 command = [self.mtn, 'pull', self.sourcedata, 85 '--db', self.database] 86 if self.progress: 87 command.extend(['--ticker=dot']) 88 else: 89 command.extend(['--ticker=none']) 90 c = runprocess.RunProcess(self.builder, command, 91 self.builder.basedir, 92 environ=self.env, sendRC=False, 93 timeout=self.timeout, 94 maxTime=self.maxTime, 95 keepStdout=True, usePTY=False, 96 logEnviron=self.logEnviron) 97 self.sendStatus({"header": "pulling %s from %s\n" 98 % (self.branch, self.sourcedata)}) 99 self.command = c 100 d = c.start() 101 d.addCallback(self._abandonOnFailure) 102 d.addCallback(fn) 103 else: 104 d = fn(None) 105 if cb: 106 d.addCallback(cb) 107 return d
108
109 - def _update(self, res):
110 command = [self.mtn, 'update', 111 '--db', self.database] 112 if self.revision: 113 command.extend(['--revision', self.revision]) 114 else: 115 command.extend(["-r", "h:" + self.branch]) 116 command.extend(["-b", self.branch]) 117 c = runprocess.RunProcess(self.builder, command, self._fullSrcdir(), 118 environ=self.env, sendRC=False, 119 timeout=self.timeout, maxTime=self.maxTime, 120 keepStdout=True, usePTY=False, 121 logEnviron=self.logEnviron) 122 d = c.start() 123 return d
124
125 - def _checkout(self, res):
126 command = [self.mtn, 'checkout', self._fullSrcdir(), 127 '--db', self.database] 128 if self.revision: 129 command.extend(['--revision', self.revision]) 130 command.extend(['--branch', self.branch]) 131 c = runprocess.RunProcess(self.builder, command, self.builder.basedir, 132 environ=self.env, sendRC=False, 133 timeout=self.timeout, maxTime=self.maxTime, 134 keepStdout=True, usePTY=False, 135 logEnviron=self.logEnviron) 136 d = c.start() 137 return d
138
139 - def _checkDb(self):
140 # Don't send stderr. When there is no database, this might confuse 141 # users, as they will see a mtn error message. But having no database 142 # repo is not an error, just an indication that we need to pull one. 143 c = runprocess.RunProcess(self.builder, [self.mtn, 'db', 'info', 144 '--db', self.database], 145 self.builder.basedir, 146 environ=self.env, sendRC=False, 147 keepStdout=True, sendStderr=False, 148 usePTY=False, logEnviron=self.logEnviron) 149 d = c.start() 150 def afterCheckRepo(res, cdi): 151 if type(res) is int and res != 0: 152 log.msg("No database found, creating it") 153 # mtn info fails, try to create shared repo. 154 # We'll be doing an initial pull, so up the timeout to 155 # 3 hours to make sure it will have time to complete. 156 self._pull_timeout = max(self._pull_timeout, 3 * 60 * 60) 157 c = runprocess.RunProcess(self.builder, [self.mtn, 'db', 'init', 158 '--db', self.database], 159 self.builder.basedir, 160 environ=self.env, 161 sendRC=False, usePTY=False, 162 logEnviron=self.logEnviron) 163 self.command = c 164 return c.start() 165 elif cdi.stdout.find("(migration needed)") > 0: 166 log.msg("Older format database found, migrating it") 167 # mtn info fails, try to create shared repo. 168 c = runprocess.RunProcess(self.builder, [self.mtn, 169 'db', 'migrate', 170 '--db', self.database], 171 self.builder.basedir, 172 environ=self.env, 173 sendRC=False, usePTY=False, 174 logEnviron=self.logEnviron) 175 self.command = c 176 return c.start() 177 elif cdi.stdout.find("(too new, cannot use)") > 0: 178 raise MonotoneError, "The database is of a newer format than mtn can handle... Abort!" 179 else: 180 return defer.succeed(res)
181 d.addCallback(afterCheckRepo, c) 182 return d 183
184 - def parseGotRevision(self):
185 def _parse(res): 186 hash = self.command.stdout.strip() 187 if len(hash) != 40: 188 return None 189 return hash
190 return self._dovccmd(self._get_base_revision, False, _parse) 191
192 - def _get_base_revision(self, res):
193 c = runprocess.RunProcess(self.builder, 194 [self.mtn, 'automate', 'select', 'w:'], 195 self._fullSrcdir(), 196 sendRC=False, 197 timeout=self.timeout, maxTime=self.maxTime, 198 keepStdout=True, usePTY=False, 199 logEnviron=self.logEnviron) 200 d = c.start() 201 d.addCallback(self._abandonOnFailure) 202 return d
203