|
@@ -1553,6 +1553,7 @@ class CipdRoot(object):
|
|
|
self._packages_by_subdir = collections.defaultdict(list)
|
|
|
self._root_dir = root_dir
|
|
|
self._service_url = service_url
|
|
|
+ self._resolved_packages = None
|
|
|
|
|
|
def add_package(self, subdir, package, version):
|
|
|
"""Adds a package to this CIPD root.
|
|
@@ -1582,6 +1583,11 @@ class CipdRoot(object):
|
|
|
"""Get the list of configured packages for the given subdir."""
|
|
|
return list(self._packages_by_subdir[subdir])
|
|
|
|
|
|
+ def resolved_packages(self):
|
|
|
+ if not self._resolved_packages:
|
|
|
+ self._resolved_packages = self.ensure_file_resolve()
|
|
|
+ return self._resolved_packages
|
|
|
+
|
|
|
def clobber(self):
|
|
|
"""Remove the .cipd directory.
|
|
|
|
|
@@ -1644,6 +1650,52 @@ class CipdRoot(object):
|
|
|
gclient_utils.CheckCallAndFilter(
|
|
|
cmd, print_stdout=True, show_header=True)
|
|
|
|
|
|
+ @contextlib.contextmanager
|
|
|
+ def _create_ensure_file_for_resolve(self):
|
|
|
+ try:
|
|
|
+ contents = '$ResolvedVersions %s\n' % os.devnull
|
|
|
+ for subdir, packages in sorted(self._packages_by_subdir.items()):
|
|
|
+ contents += '@Subdir %s\n' % subdir
|
|
|
+ for package in sorted(packages, key=lambda p: p.name):
|
|
|
+ contents += '%s %s\n' % (package.name, package.version)
|
|
|
+ contents += '\n'
|
|
|
+ ensure_file = None
|
|
|
+ with tempfile.NamedTemporaryFile(suffix='.ensure',
|
|
|
+ delete=False,
|
|
|
+ mode='wb') as ensure_file:
|
|
|
+ ensure_file.write(contents.encode('utf-8', 'replace'))
|
|
|
+ yield ensure_file.name
|
|
|
+ finally:
|
|
|
+ if ensure_file is not None and os.path.exists(ensure_file.name):
|
|
|
+ os.remove(ensure_file.name)
|
|
|
+
|
|
|
+ def _create_resolved_file(self):
|
|
|
+ return tempfile.NamedTemporaryFile(suffix='.resolved',
|
|
|
+ delete=False,
|
|
|
+ mode='wb')
|
|
|
+
|
|
|
+ def ensure_file_resolve(self):
|
|
|
+ """Run `cipd ensure-file-resolve`."""
|
|
|
+ with self._mutator_lock:
|
|
|
+ with self._create_resolved_file() as output_file:
|
|
|
+ with self._create_ensure_file_for_resolve() as ensure_file:
|
|
|
+ cmd = [
|
|
|
+ 'cipd',
|
|
|
+ 'ensure-file-resolve',
|
|
|
+ '-log-level',
|
|
|
+ 'error',
|
|
|
+ '-ensure-file',
|
|
|
+ ensure_file,
|
|
|
+ '-json-output',
|
|
|
+ output_file.name,
|
|
|
+ ]
|
|
|
+ gclient_utils.CheckCallAndFilter(cmd,
|
|
|
+ print_stdout=False,
|
|
|
+ show_header=False)
|
|
|
+ with open(output_file.name) as f:
|
|
|
+ output_json = json.load(f)
|
|
|
+ return output_json.get('result', {})
|
|
|
+
|
|
|
def run(self, command):
|
|
|
if command == 'update':
|
|
|
self.ensure()
|
|
@@ -1716,6 +1768,22 @@ class CipdWrapper(SCMWrapper):
|
|
|
"""Grab the instance ID."""
|
|
|
try:
|
|
|
tmpdir = tempfile.mkdtemp()
|
|
|
+ # Attempt to get instance_id from the root resolved cache.
|
|
|
+ # Resolved cache will not match on any CIPD packages with
|
|
|
+ # variables such as ${platform}, they will fall back to
|
|
|
+ # the slower method below.
|
|
|
+ resolved = self._root.resolved_packages()
|
|
|
+ if resolved:
|
|
|
+ # CIPD uses POSIX separators across all platforms, so
|
|
|
+ # replace any Windows separators.
|
|
|
+ path_split = self.relpath.replace(os.sep, "/").split(":")
|
|
|
+ if len(path_split) > 1:
|
|
|
+ src_path, package = path_split
|
|
|
+ if src_path in resolved:
|
|
|
+ for resolved_package in resolved[src_path]:
|
|
|
+ if package == resolved_package.get('pin', {}).get('package'):
|
|
|
+ return resolved_package.get('pin', {}).get('instance_id')
|
|
|
+
|
|
|
describe_json_path = os.path.join(tmpdir, 'describe.json')
|
|
|
cmd = [
|
|
|
'cipd', 'describe',
|