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