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