Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(430)

Side by Side Diff: git_cache.py

Issue 240203005: Implement git-drover. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools
Patch Set: address feedback Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « git-drover ('k') | git_common.py » ('j') | git_drover.py » ('J')
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright 2014 The Chromium Authors. All rights reserved. 2 # Copyright 2014 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 """A git command for managing a local cache of git repositories.""" 6 """A git command for managing a local cache of git repositories."""
7 7
8 from __future__ import print_function 8 from __future__ import print_function
9 import errno 9 import errno
10 import logging 10 import logging
(...skipping 10 matching lines...) Expand all
21 import gclient_utils 21 import gclient_utils
22 import subcommand 22 import subcommand
23 23
24 try: 24 try:
25 # pylint: disable=E0602 25 # pylint: disable=E0602
26 WinErr = WindowsError 26 WinErr = WindowsError
27 except NameError: 27 except NameError:
28 class WinErr(Exception): 28 class WinErr(Exception):
29 pass 29 pass
30 30
31
31 class LockError(Exception): 32 class LockError(Exception):
32 pass 33 pass
33 34
34 35
35 class Lockfile(object): 36 class Lockfile(object):
36 """Class to represent a cross-platform process-specific lockfile.""" 37 """Class to represent a cross-platform process-specific lockfile."""
37 38
38 def __init__(self, path): 39 def __init__(self, path):
39 self.path = os.path.abspath(path) 40 self.path = os.path.abspath(path)
40 self.lockfile = self.path + ".lock" 41 self.lockfile = self.path + ".lock"
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after
144 'third_party', 'gsutil', 'gsutil') 145 'third_party', 'gsutil', 'gsutil')
145 bootstrap_bucket = 'chromium-git-cache' 146 bootstrap_bucket = 'chromium-git-cache'
146 147
147 def __init__(self, url, refs=None, print_func=None): 148 def __init__(self, url, refs=None, print_func=None):
148 self.url = url 149 self.url = url
149 self.refs = refs or [] 150 self.refs = refs or []
150 self.basedir = self.UrlToCacheDir(url) 151 self.basedir = self.UrlToCacheDir(url)
151 self.mirror_path = os.path.join(self.GetCachePath(), self.basedir) 152 self.mirror_path = os.path.join(self.GetCachePath(), self.basedir)
152 self.print = print_func or print 153 self.print = print_func or print
153 154
155 @classmethod
156 def from_repo(cls, path=None):
157 """Returns Mirror if path is in a cached repo, else None."""
158 args = ['-C', path] if path else []
159 args = [cls.git_exe] + args + ['rev-parse', '--git-dir']
160 git_path = subprocess.check_output(args).strip()
161 alt_path = os.path.join(git_path, 'objects', 'info', 'alternates')
162
163 if os.path.exists(alt_path):
164 with open(alt_path, 'rb') as alt:
165 mirror_path = alt.read().strip()
166 mirror_path = os.path.dirname(mirror_path)
167 cache_path = os.path.dirname(mirror_path)
168 if os.path.exists(mirror_path):
169 url = subprocess.check_output(
170 [cls.git_exe, '-C', mirror_path, 'config', 'remote.origin.url']
171 ).strip()
172
173 # TODO(iannucci): cache_path should NOT be a class attribute. Maybe
174 # a `default_cache_path`, but not the actual path.
175 cls.SetCachePath(cache_path)
176
177 return cls(url)
178
154 @staticmethod 179 @staticmethod
155 def UrlToCacheDir(url): 180 def UrlToCacheDir(url):
156 """Convert a git url to a normalized form for the cache dir path.""" 181 """Convert a git url to a normalized form for the cache dir path."""
157 parsed = urlparse.urlparse(url) 182 parsed = urlparse.urlparse(url)
158 norm_url = parsed.netloc + parsed.path 183 norm_url = parsed.netloc + parsed.path
159 if norm_url.endswith('.git'): 184 if norm_url.endswith('.git'):
160 norm_url = norm_url[:-len('.git')] 185 norm_url = norm_url[:-len('.git')]
161 return norm_url.replace('-', '--').replace('/', '-').lower() 186 return norm_url.replace('-', '--').replace('/', '-').lower()
162 187
163 @staticmethod 188 @staticmethod
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
215 self.RunGit(['config', '--replace-all', 'remote.origin.fetch', 240 self.RunGit(['config', '--replace-all', 'remote.origin.fetch',
216 '+refs/heads/*:refs/heads/*'], cwd=cwd) 241 '+refs/heads/*:refs/heads/*'], cwd=cwd)
217 for ref in self.refs: 242 for ref in self.refs:
218 ref = ref.lstrip('+').rstrip('/') 243 ref = ref.lstrip('+').rstrip('/')
219 if ref.startswith('refs/'): 244 if ref.startswith('refs/'):
220 refspec = '+%s:%s' % (ref, ref) 245 refspec = '+%s:%s' % (ref, ref)
221 else: 246 else:
222 refspec = '+refs/%s/*:refs/%s/*' % (ref, ref) 247 refspec = '+refs/%s/*:refs/%s/*' % (ref, ref)
223 self.RunGit(['config', '--add', 'remote.origin.fetch', refspec], cwd=cwd) 248 self.RunGit(['config', '--add', 'remote.origin.fetch', refspec], cwd=cwd)
224 249
225 def bootstrap_repo(self, directory): 250 def bootstrap_repo(self, directory, verbose):
226 """Bootstrap the repo from Google Stroage if possible.""" 251 """Bootstrap the repo from Google Stroage if possible."""
227 252
228 python_fallback = False 253 python_fallback = False
229 if sys.platform.startswith('win') and not self.FindExecutable('7z'): 254 if sys.platform.startswith('win') and not self.FindExecutable('7z'):
230 python_fallback = True 255 python_fallback = True
231 elif sys.platform.startswith('darwin'): 256 elif sys.platform.startswith('darwin'):
232 # The OSX version of unzip doesn't support zip64. 257 # The OSX version of unzip doesn't support zip64.
233 python_fallback = True 258 python_fallback = True
234 elif not self.FindExecutable('unzip'): 259 elif not self.FindExecutable('unzip'):
235 python_fallback = True 260 python_fallback = True
236 261
237 gs_folder = 'gs://%s/%s' % (self.bootstrap_bucket, self.basedir) 262 gs_folder = 'gs://%s/%s' % (self.bootstrap_bucket, self.basedir)
238 gsutil = Gsutil( 263 gsutil = Gsutil(
239 self.gsutil_exe, boto_path=os.devnull, bypass_prodaccess=True) 264 self.gsutil_exe, boto_path=os.devnull, bypass_prodaccess=True)
240 # Get the most recent version of the zipfile. 265 # Get the most recent version of the zipfile.
241 _, ls_out, _ = gsutil.check_call('ls', gs_folder) 266 _, ls_out, _ = gsutil.check_call('ls', gs_folder)
242 ls_out_sorted = sorted(ls_out.splitlines()) 267 ls_out_sorted = sorted(ls_out.splitlines())
243 if not ls_out_sorted: 268 if not ls_out_sorted:
244 # This repo is not on Google Storage. 269 # This repo is not on Google Storage.
245 return False 270 return False
246 latest_checkout = ls_out_sorted[-1] 271 latest_checkout = ls_out_sorted[-1]
247 272
248 # Download zip file to a temporary directory. 273 # Download zip file to a temporary directory.
249 try: 274 try:
250 tempdir = tempfile.mkdtemp() 275 tempdir = tempfile.mkdtemp()
251 self.print('Downloading %s' % latest_checkout) 276 if not verbose:
252 code, out, err = gsutil.check_call('cp', latest_checkout, tempdir) 277 self.print('Downloading %s' % latest_checkout)
278 code, out, err = gsutil.check_call('cp', latest_checkout, tempdir,
279 verbose=verbose)
253 if code: 280 if code:
254 self.print('%s\n%s' % (out, err)) 281 self.print('%s\n%s' % (out, err))
255 return False 282 return False
256 filename = os.path.join(tempdir, latest_checkout.split('/')[-1]) 283 filename = os.path.join(tempdir, latest_checkout.split('/')[-1])
257 284
258 # Unpack the file with 7z on Windows, unzip on linux, or fallback. 285 # Unpack the file with 7z on Windows, unzip on linux, or fallback.
259 if not python_fallback: 286 if not python_fallback:
260 if sys.platform.startswith('win'): 287 if sys.platform.startswith('win'):
261 cmd = ['7z', 'x', '-o%s' % directory, '-tzip', filename] 288 cmd = ['7z', 'x', '-o%s' % directory, '-tzip', filename]
262 else: 289 else:
(...skipping 17 matching lines...) Expand all
280 self.print( 307 self.print(
281 'Extracting bootstrap zipfile %s failed.\n' 308 'Extracting bootstrap zipfile %s failed.\n'
282 'Resuming normal operations.' % filename) 309 'Resuming normal operations.' % filename)
283 return False 310 return False
284 return True 311 return True
285 312
286 def exists(self): 313 def exists(self):
287 return os.path.isfile(os.path.join(self.mirror_path, 'config')) 314 return os.path.isfile(os.path.join(self.mirror_path, 'config'))
288 315
289 def populate(self, depth=None, shallow=False, bootstrap=False, 316 def populate(self, depth=None, shallow=False, bootstrap=False,
290 verbose=False): 317 verbose=False, fetch_specs=()):
291 if shallow and not depth: 318 if shallow and not depth:
292 depth = 10000 319 depth = 10000
293 gclient_utils.safe_makedirs(self.GetCachePath()) 320 gclient_utils.safe_makedirs(self.GetCachePath())
294 321
295 v = [] 322 v = []
296 if verbose: 323 if verbose:
297 v = ['-v', '--progress'] 324 v = ['-v', '--progress']
298 325
299 d = [] 326 d = []
300 if depth: 327 if depth:
301 d = ['--depth', str(depth)] 328 d = ['--depth', str(depth)]
302 329
303 330
304 with Lockfile(self.mirror_path): 331 with Lockfile(self.mirror_path):
305 # Setup from scratch if the repo is new or is in a bad state. 332 # Setup from scratch if the repo is new or is in a bad state.
306 tempdir = None 333 tempdir = None
307 if not os.path.exists(os.path.join(self.mirror_path, 'config')): 334 if not os.path.exists(os.path.join(self.mirror_path, 'config')):
308 gclient_utils.rmtree(self.mirror_path) 335 gclient_utils.rmtree(self.mirror_path)
309 tempdir = tempfile.mkdtemp( 336 tempdir = tempfile.mkdtemp(
310 suffix=self.basedir, dir=self.GetCachePath()) 337 suffix=self.basedir, dir=self.GetCachePath())
311 bootstrapped = not depth and bootstrap and self.bootstrap_repo(tempdir) 338 bootstrapped = (not depth and bootstrap and
339 self.bootstrap_repo(tempdir, verbose))
312 if not bootstrapped: 340 if not bootstrapped:
313 self.RunGit(['init', '--bare'], cwd=tempdir) 341 self.RunGit(['init', '--bare'], cwd=tempdir)
314 else: 342 else:
315 if depth and os.path.exists(os.path.join(self.mirror_path, 'shallow')): 343 if depth and os.path.exists(os.path.join(self.mirror_path, 'shallow')):
316 logging.warn( 344 logging.warn(
317 'Shallow fetch requested, but repo cache already exists.') 345 'Shallow fetch requested, but repo cache already exists.')
318 d = [] 346 d = []
319 347
320 rundir = tempdir or self.mirror_path 348 rundir = tempdir or self.mirror_path
321 self.config(rundir) 349 self.config(rundir)
322 fetch_cmd = ['fetch'] + v + d + ['origin'] 350 fetch_cmd = ['fetch'] + v + d + ['origin']
323 fetch_specs = subprocess.check_output( 351 fetch_specs = fetch_specs or subprocess.check_output(
324 [self.git_exe, 'config', '--get-all', 'remote.origin.fetch'], 352 [self.git_exe, 'config', '--get-all', 'remote.origin.fetch'],
325 cwd=rundir).strip().splitlines() 353 cwd=rundir).strip().splitlines()
326 for spec in fetch_specs: 354 for spec in fetch_specs:
327 try: 355 try:
328 self.RunGit(fetch_cmd + [spec], cwd=rundir, retry=True) 356 self.RunGit(fetch_cmd + [spec], cwd=rundir, retry=True)
329 except subprocess.CalledProcessError: 357 except subprocess.CalledProcessError:
330 logging.warn('Fetch of %s failed' % spec) 358 logging.warn('Fetch of %s failed' % spec)
331 if tempdir: 359 if tempdir:
332 os.rename(tempdir, self.mirror_path) 360 os.rename(tempdir, self.mirror_path)
333 361
(...skipping 173 matching lines...) Expand 10 before | Expand all | Expand 10 after
507 return options, args 535 return options, args
508 536
509 537
510 def main(argv): 538 def main(argv):
511 dispatcher = subcommand.CommandDispatcher(__name__) 539 dispatcher = subcommand.CommandDispatcher(__name__)
512 return dispatcher.execute(OptionParser(), argv) 540 return dispatcher.execute(OptionParser(), argv)
513 541
514 542
515 if __name__ == '__main__': 543 if __name__ == '__main__':
516 sys.exit(main(sys.argv[1:])) 544 sys.exit(main(sys.argv[1:]))
OLDNEW
« no previous file with comments | « git-drover ('k') | git_common.py » ('j') | git_drover.py » ('J')

Powered by Google App Engine
This is Rietveld 408576698