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

Source Code for Module buildslave.commands.git

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