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

Source Code for Module buildslave.commands.git

  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.internet import defer 
 19   
 20  from buildslave.commands.base import SourceBaseCommand 
 21  from buildslave import runprocess 
 22  from buildslave.commands.base import AbandonChain 
 23   
 24   
25 -class Git(SourceBaseCommand):
26 """Git specific VC operation. In addition to the arguments 27 handled by SourceBaseCommand, this command reads the following keys: 28 29 ['repourl'] (required): the upstream GIT repository string 30 ['branch'] (optional): which version (i.e. branch or tag) 31 to retrieve. Default: "master". 32 ['submodules'] (optional): whether to initialize and update 33 submodules. Default: False. 34 ['ignore_ignores'] (optional): ignore ignores when purging changes. 35 ['reference'] (optional): use this reference repository 36 to fetch objects. 37 ['gerrit_branch'] (optional): which virtual branch to retrieve. 38 ['progress'] (optional): have git output progress markers, 39 avoiding timeouts for long fetches; 40 requires Git 1.7.2 or later. 41 """ 42 43 header = "git operation" 44
45 - def setup(self, args):
46 SourceBaseCommand.setup(self, args) 47 self.repourl = args['repourl'] 48 self.branch = args.get('branch') 49 if not self.branch: 50 self.branch = "master" 51 self.sourcedata = "%s %s\n" % (self.repourl, self.branch) 52 self.submodules = args.get('submodules') 53 self.ignore_ignores = args.get('ignore_ignores', True) 54 self.reference = args.get('reference', None) 55 self.gerrit_branch = args.get('gerrit_branch', None)
56
57 - def _fullSrcdir(self):
58 return os.path.join(self.builder.basedir, self.srcdir)
59
60 - def _commitSpec(self):
61 if self.revision: 62 return self.revision 63 return self.branch
64
65 - def sourcedirIsUpdateable(self):
66 return os.path.isdir(os.path.join(self._fullSrcdir(), ".git"))
67
68 - def _dovccmd(self, command, cb=None, **kwargs):
69 git = self.getCommand("git") 70 c = runprocess.RunProcess(self.builder, [git] + command, self._fullSrcdir(), 71 sendRC=False, timeout=self.timeout, 72 maxTime=self.maxTime, usePTY=False, **kwargs) 73 self.command = c 74 d = c.start() 75 if cb: 76 d.addCallback(self._abandonOnFailure) 77 d.addCallback(cb) 78 return d
79 80 # If the repourl matches the sourcedata file, then 81 # we can say that the sourcedata matches. We can 82 # ignore branch changes, since Git can work with 83 # many branches fetched, and we deal with it properly 84 # in doVCUpdate.
85 - def sourcedataMatches(self):
86 try: 87 olddata = self.readSourcedata() 88 if not olddata.startswith(self.repourl+' '): 89 return False 90 except IOError: 91 return False 92 return True
93
94 - def _cleanSubmodules(self, res):
95 command = ['submodule', 'foreach', 'git', 'clean', '-d', '-f'] 96 if self.ignore_ignores: 97 command.append('-x') 98 return self._dovccmd(command)
99
100 - def _updateSubmodules(self, res):
101 return self._dovccmd(['submodule', 'update'], self._cleanSubmodules)
102
103 - def _initSubmodules(self, res):
104 if self.submodules: 105 return self._dovccmd(['submodule', 'init'], self._updateSubmodules) 106 else: 107 return defer.succeed(0)
108
109 - def _didHeadCheckout(self, res):
110 # Rename branch, so that the repo will have the expected branch name 111 # For further information about this, see the commit message 112 command = ['branch', '-M', self.branch] 113 return self._dovccmd(command, self._initSubmodules)
114
115 - def _didFetch(self, res):
116 if self.revision: 117 head = self.revision 118 else: 119 head = 'FETCH_HEAD' 120 121 # That is not sufficient. git will leave unversioned files and empty 122 # directories. Clean them up manually in _didReset. 123 command = ['reset', '--hard', head] 124 return self._dovccmd(command, self._didHeadCheckout)
125
126 - def maybeNotDoVCFallback(self, res):
127 # If we were unable to find the branch/SHA on the remote, 128 # clobbering the repo won't help any, so just abort the chain 129 if hasattr(self.command, 'stderr'): 130 if "Couldn't find remote ref" in self.command.stderr: 131 raise AbandonChain(-1)
132 133 # Update first runs "git clean", removing local changes, 134 # if the branch to be checked out has changed. This, combined 135 # with the later "git reset" equates clobbering the repo, 136 # but it's much more efficient.
137 - def doVCUpdate(self):
138 try: 139 # Check to see if our branch has changed 140 diffbranch = self.sourcedata != self.readSourcedata() 141 except IOError: 142 diffbranch = False 143 if diffbranch: 144 command = ['clean', '-f', '-d'] 145 if self.ignore_ignores: 146 command.append('-x') 147 return self._dovccmd(command, self._didClean) 148 return self._didClean(None)
149
150 - def _doFetch(self, dummy, branch):
151 # The plus will make sure the repo is moved to the branch's 152 # head even if it is not a simple "fast-forward" 153 command = ['fetch', '-t', self.repourl, '+%s' % branch] 154 # If the 'progress' option is set, tell git fetch to output 155 # progress information to the log. This can solve issues with 156 # long fetches killed due to lack of output, but only works 157 # with Git 1.7.2 or later. 158 if self.args.get('progress'): 159 command.append('--progress') 160 self.sendStatus({"header": "fetching branch %s from %s\n" 161 % (branch, self.repourl)}) 162 return self._dovccmd(command, self._didFetch, keepStderr=True)
163
164 - def _didClean(self, dummy):
165 branch = self.gerrit_branch or self.branch 166 167 # After a clean, try to use the given revision if we have one. 168 if self.revision: 169 # We know what revision we want. See if we have it. 170 d = self._dovccmd(['reset', '--hard', self.revision], 171 self._initSubmodules) 172 # If we are unable to reset to the specified version, we 173 # must do a fetch first and retry. 174 d.addErrback(self._doFetch, branch) 175 return d 176 else: 177 # No known revision, go grab the latest. 178 return self._doFetch(None, branch)
179
180 - def _didInit(self, res):
181 # If we have a reference repository specified, we need to also set that 182 # up after the 'git init'. 183 if self.reference: 184 git_alts_path = os.path.join(self._fullSrcdir(), '.git', 'objects', 'info', 'alternates') 185 git_alts_file = open(git_alts_path, 'w') 186 git_alts_file.write(os.path.join(self.reference, 'objects')) 187 git_alts_file.close() 188 return self.doVCUpdate()
189
190 - def doVCFull(self):
191 git = self.getCommand("git") 192 193 # If they didn't ask for a specific revision, we can get away with a 194 # shallow clone. 195 if not self.args.get('revision') and self.args.get('shallow'): 196 cmd = [git, 'clone', '--depth', '1'] 197 # If we have a reference repository, pass it to the clone command 198 if self.reference: 199 cmd.extend(['--reference', self.reference]) 200 cmd.extend([self.repourl, self._fullSrcdir()]) 201 c = runprocess.RunProcess(self.builder, cmd, self.builder.basedir, 202 sendRC=False, timeout=self.timeout, 203 maxTime=self.maxTime, usePTY=False) 204 self.command = c 205 cmdexec = c.start() 206 cmdexec.addCallback(self._didInit) 207 return cmdexec 208 else: 209 os.makedirs(self._fullSrcdir()) 210 return self._dovccmd(['init'], self._didInit)
211
212 - def parseGotRevision(self):
213 command = ['rev-parse', 'HEAD'] 214 def _parse(res): 215 hash = self.command.stdout.strip() 216 if len(hash) != 40: 217 return None 218 return hash
219 return self._dovccmd(command, _parse, keepStdout=True)
220