Chromium Code Reviews| Index: gsutil.py |
| diff --git a/gsutil.py b/gsutil.py |
| index 53589a27bc48e97261d47f3d22a18b9ea1a4d88c..20d87686717fa2ae782665e572ab1cfae3eb5523 100755 |
| --- a/gsutil.py |
| +++ b/gsutil.py |
| @@ -8,12 +8,15 @@ |
| import argparse |
| import base64 |
| +import contextlib |
| import hashlib |
| import json |
| import os |
| import shutil |
| import subprocess |
| import sys |
| +import tempfile |
| +import time |
| import urllib2 |
| import zipfile |
| @@ -26,7 +29,6 @@ DEFAULT_BIN_DIR = os.path.join(THIS_DIR, 'external_bin', 'gsutil') |
| DEFAULT_FALLBACK_GSUTIL = os.path.join( |
| THIS_DIR, 'third_party', 'gsutil', 'gsutil') |
| - |
| class InvalidGsutilError(Exception): |
| pass |
| @@ -73,33 +75,52 @@ def check_gsutil(gsutil_bin): |
| [sys.executable, gsutil_bin, 'version'], |
| stdout=subprocess.PIPE, stderr=subprocess.STDOUT) == 0 |
| -def ensure_gsutil(version, target): |
| +@contextlib.contextmanager |
| +def temporary_directory(base): |
| + tmpdir = tempfile.mkdtemp(prefix='gsutil_py', dir=base) |
| + try: |
| + yield tmpdir |
| + finally: |
| + if os.path.isdir(tmpdir): |
| + shutil.rmtree(tmpdir) |
| + |
| +def ensure_gsutil(version, target, clean): |
| bin_dir = os.path.join(target, 'gsutil_%s' % version) |
| gsutil_bin = os.path.join(bin_dir, 'gsutil', 'gsutil') |
| - if os.path.isfile(gsutil_bin) and check_gsutil(gsutil_bin): |
| + if not clean and os.path.isfile(gsutil_bin) and check_gsutil(gsutil_bin): |
| # Everything is awesome! we're all done here. |
| return gsutil_bin |
| - if os.path.isdir(bin_dir): |
| + with temporary_directory(target) as instance_dir: |
| # Clean up if we're redownloading a corrupted gsutil. |
| - shutil.rmtree(bin_dir) |
| - cache_dir = os.path.join(target, '.cache_dir') |
| - if not os.path.isdir(cache_dir): |
| - os.makedirs(cache_dir) |
| - target_zip_filename = download_gsutil(version, cache_dir) |
| - with zipfile.ZipFile(target_zip_filename, 'r') as target_zip: |
| - target_zip.extractall(bin_dir) |
| + cleanup_path = os.path.join(instance_dir, 'clean') |
| + try: |
| + os.rename(bin_dir, cleanup_path) |
| + except OSError: |
|
David James
2015/09/17 21:56:50
Per https://www.python.org/dev/peps/pep-3151/#conf
dnj (Google)
2015/09/18 00:20:22
Done.
|
| + cleanup_path = None |
| + if cleanup_path: |
| + shutil.rmtree(cleanup_path) |
| + |
| + download_dir = os.path.join(instance_dir, 'download') |
| + target_zip_filename = download_gsutil(version, instance_dir) |
| + with zipfile.ZipFile(target_zip_filename, 'r') as target_zip: |
| + target_zip.extractall(download_dir) |
| + |
| + try: |
| + os.rename(download_dir, bin_dir) |
| + except OSError: |
| + # Something else did this in parallel. |
| + pass |
| # Final check that the gsutil bin is okay. This should never fail. |
| if not check_gsutil(gsutil_bin): |
| raise InvalidGsutilError() |
| - |
| return gsutil_bin |
| -def run_gsutil(force_version, fallback, target, args): |
| +def run_gsutil(force_version, fallback, target, args, clean=False): |
| if force_version: |
| - gsutil_bin = ensure_gsutil(force_version, target) |
| + gsutil_bin = ensure_gsutil(force_version, target, clean) |
| else: |
| gsutil_bin = fallback |
| cmd = [sys.executable, gsutil_bin] + args |
| @@ -107,10 +128,16 @@ def run_gsutil(force_version, fallback, target, args): |
| def parse_args(): |
| + bin_dir = os.environ.get('DEPOT_TOOLS_GSUTIL_BIN_DIR', DEFAULT_BIN_DIR) |
| + |
| parser = argparse.ArgumentParser() |
| parser.add_argument('--force-version', default='4.13') |
| + parser.add_argument('--clean', action='store_true', |
| + help='Clear any existing gsutil package, forcing a new download.') |
| parser.add_argument('--fallback', default=DEFAULT_FALLBACK_GSUTIL) |
| - parser.add_argument('--target', default=DEFAULT_BIN_DIR) |
| + parser.add_argument('--target', default=bin_dir, |
| + help='The target directory to download/store a gsutil version in. ' |
| + '(default is %(default)s).') |
| parser.add_argument('args', nargs=argparse.REMAINDER) |
| args, extras = parser.parse_known_args() |
| @@ -118,12 +145,13 @@ def parse_args(): |
| args.args.pop(0) |
| if extras: |
| args.args = extras + args.args |
| - return args.force_version, args.fallback, args.target, args.args |
| + return args |
| def main(): |
| - force_version, fallback, target, args = parse_args() |
| - return run_gsutil(force_version, fallback, target, args) |
| + args = parse_args() |
| + return run_gsutil(args.force_version, args.fallback, args.target, args.args, |
| + clean=args.clean) |
| if __name__ == '__main__': |
| sys.exit(main()) |