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