aboutsummaryrefslogtreecommitdiff
path: root/pw_env_setup/py/pw_env_setup/cipd_setup/update.py
diff options
context:
space:
mode:
Diffstat (limited to 'pw_env_setup/py/pw_env_setup/cipd_setup/update.py')
-rwxr-xr-xpw_env_setup/py/pw_env_setup/cipd_setup/update.py138
1 files changed, 96 insertions, 42 deletions
diff --git a/pw_env_setup/py/pw_env_setup/cipd_setup/update.py b/pw_env_setup/py/pw_env_setup/cipd_setup/update.py
index eb2b835b1..deac4b0df 100755
--- a/pw_env_setup/py/pw_env_setup/cipd_setup/update.py
+++ b/pw_env_setup/py/pw_env_setup/cipd_setup/update.py
@@ -21,6 +21,7 @@ The stdout of this script is meant to be executed by the invoking shell.
from __future__ import print_function
+import collections
import hashlib
import json
import os
@@ -30,8 +31,12 @@ import subprocess
import sys
-def check_auth(cipd, package_files, spin):
+def check_auth(cipd, package_files, cipd_service_account, spin):
"""Check have access to CIPD pigweed directory."""
+ cmd = [cipd]
+ extra_args = []
+ if cipd_service_account:
+ extra_args.extend(['-service-account-json', cipd_service_account])
paths = []
for package_file in package_files:
@@ -48,8 +53,9 @@ def check_auth(cipd, package_files, spin):
username = None
try:
- output = subprocess.check_output([cipd, 'auth-info'],
- stderr=subprocess.STDOUT).decode()
+ output = subprocess.check_output(
+ cmd + ['auth-info'] + extra_args, stderr=subprocess.STDOUT
+ ).decode()
logged_in = True
match = re.search(r'Logged in as (\S*)\.', output)
@@ -66,7 +72,8 @@ def check_auth(cipd, package_files, spin):
# Not catching CalledProcessError because 'cipd ls' seems to never
# return an error code unless it can't reach the CIPD server.
output = subprocess.check_output(
- [cipd, 'ls', path], stderr=subprocess.STDOUT).decode()
+ cmd + ['ls', path] + extra_args, stderr=subprocess.STDOUT
+ ).decode()
if 'No matching packages' not in output:
continue
@@ -75,8 +82,10 @@ def check_auth(cipd, package_files, spin):
# 'cipd instances' does use an error code if there's no such package
# or that package is inaccessible.
try:
- subprocess.check_output([cipd, 'instances', path],
- stderr=subprocess.STDOUT)
+ subprocess.check_output(
+ cmd + ['instances', path] + extra_args,
+ stderr=subprocess.STDOUT,
+ )
except subprocess.CalledProcessError:
inaccessible_paths.append(path)
@@ -88,14 +97,17 @@ def check_auth(cipd, package_files, spin):
with spin.pause():
stderr = lambda *args: print(*args, file=sys.stderr)
stderr()
- stderr('Not logged in to CIPD and no anonymous access to the '
- 'following CIPD paths:')
+ stderr(
+ 'Not logged in to CIPD and no anonymous access to the '
+ 'following CIPD paths:'
+ )
for path in inaccessible_paths:
stderr(' {}'.format(path))
stderr()
stderr('Attempting CIPD login')
try:
- subprocess.check_call([cipd, 'auth-login'])
+ # Note that with -service-account-json, auth-login is a no-op.
+ subprocess.check_call(cmd + ['auth-login'] + extra_args)
except subprocess.CalledProcessError:
stderr('CIPD login failed')
return False
@@ -108,8 +120,11 @@ def check_auth(cipd, package_files, spin):
username_part = ''
if username:
username_part = '({}) '.format(username)
- stderr('Your account {}does not have access to the following '
- 'paths'.format(username_part))
+ stderr(
+ 'Your account {}does not have access to the following '
+ 'paths'.format(username_part)
+ )
+ stderr('(or they do not exist)')
for path in inaccessible_paths:
stderr(' {}'.format(path))
stderr('=' * 60)
@@ -118,7 +133,8 @@ def check_auth(cipd, package_files, spin):
return True
-def platform():
+def platform(rosetta=False):
+ """Return the CIPD platform string of the current system."""
osname = {
'darwin': 'mac',
'linux': 'linux',
@@ -137,7 +153,7 @@ def platform():
platform_arch = '{}-{}'.format(osname, arch).lower()
# Support `mac-arm64` through Rosetta until `mac-arm64` binaries are ready
- if platform_arch == 'mac-arm64':
+ if platform_arch == 'mac-arm64' and rosetta:
return 'mac-amd64'
return platform_arch
@@ -178,9 +194,8 @@ def all_package_files(env_vars, package_files):
return result
-def write_ensure_file(package_files, ensure_file):
+def all_packages(package_files):
packages = []
-
for package_file in package_files:
name = package_file_name(package_file)
with open(package_file, 'r') as ins:
@@ -191,16 +206,35 @@ def write_ensure_file(package_files, ensure_file):
else:
package['subdir'] = name
packages.extend(file_packages)
+ return packages
+
+
+def deduplicate_packages(packages):
+ deduped = collections.OrderedDict()
+ for package in reversed(packages):
+ if package['path'] in deduped:
+ del deduped[package['path']]
+ deduped[package['path']] = package
+ return reversed(list(deduped.values()))
+
+
+def write_ensure_file(
+ package_files, ensure_file, platform
+): # pylint: disable=redefined-outer-name
+ packages = all_packages(package_files)
+ deduped_packages = deduplicate_packages(packages)
with open(ensure_file, 'w') as outs:
- outs.write('$VerifiedPlatform linux-amd64\n'
- '$VerifiedPlatform mac-amd64\n'
- '$ParanoidMode CheckPresence\n')
+ outs.write(
+ '$VerifiedPlatform linux-amd64\n'
+ '$VerifiedPlatform mac-amd64\n'
+ '$ParanoidMode CheckPresence\n'
+ )
- for pkg in packages:
+ for pkg in deduped_packages:
# If this is a new-style package manifest platform handling must
# be done here instead of by the cipd executable.
- if 'platforms' in pkg and platform() not in pkg['platforms']:
+ if 'platforms' in pkg and platform not in pkg['platforms']:
continue
outs.write('@Subdir {}\n'.format(pkg.get('subdir', '')))
@@ -218,15 +252,17 @@ def package_installation_path(root_install_dir, package_file):
root_install_dir: The CIPD installation directory.
package_file: The path to the .json package definition file.
"""
- return os.path.join(root_install_dir, 'packages',
- package_file_name(package_file))
+ return os.path.join(
+ root_install_dir, 'packages', package_file_name(package_file)
+ )
-def update(
+def update( # pylint: disable=too-many-locals
cipd,
package_files,
root_install_dir,
cache_dir,
+ rosetta=False,
env_vars=None,
spin=None,
trust_hash=False,
@@ -241,8 +277,9 @@ def update(
# This file is read by 'pw doctor' which needs to know which package files
# were used in the environment.
- package_files_file = os.path.join(root_install_dir,
- '_all_package_files.json')
+ package_files_file = os.path.join(
+ root_install_dir, '_all_package_files.json'
+ )
with open(package_files_file, 'w') as outs:
json.dump(package_files, outs, indent=2)
@@ -258,21 +295,37 @@ def update(
if not pw_root:
pw_root = os.environ['PW_ROOT']
+ plat = platform(rosetta)
+
ensure_file = os.path.join(root_install_dir, 'packages.ensure')
- write_ensure_file(package_files, ensure_file)
+ write_ensure_file(package_files, ensure_file, plat)
install_dir = os.path.join(root_install_dir, 'packages')
cmd = [
cipd,
'ensure',
- '-ensure-file', ensure_file,
- '-root', install_dir,
- '-log-level', 'debug',
- '-json-output', os.path.join(root_install_dir, 'packages.json'),
- '-cache-dir', cache_dir,
- '-max-threads', '0', # 0 means use CPU count.
- ] # yapf: disable
+ '-ensure-file',
+ ensure_file,
+ '-root',
+ install_dir,
+ '-log-level',
+ 'debug',
+ '-json-output',
+ os.path.join(root_install_dir, 'packages.json'),
+ '-cache-dir',
+ cache_dir,
+ '-max-threads',
+ '0', # 0 means use CPU count.
+ ]
+
+ cipd_service_account = None
+ if env_vars:
+ cipd_service_account = env_vars.get('PW_CIPD_SERVICE_ACCOUNT_JSON')
+ if not cipd_service_account:
+ cipd_service_account = os.environ.get('PW_CIPD_SERVICE_ACCOUNT_JSON')
+ if cipd_service_account:
+ cmd.extend(['-service-account-json', cipd_service_account])
hasher = hashlib.sha256()
encoded = '\0'.join(cmd)
@@ -298,10 +351,9 @@ def update(
if digest == digest_file:
return True
- if not check_auth(cipd, package_files, spin):
+ if not check_auth(cipd, package_files, cipd_service_account, spin):
return False
- # TODO(pwbug/135) Use function from common utility module.
log = os.path.join(root_install_dir, 'packages.log')
try:
with open(log, 'w') as outs:
@@ -318,25 +370,27 @@ def update(
# Set environment variables so tools can later find things under, for
# example, 'share'.
if env_vars:
- for package_file in package_files:
+ for package_file in reversed(package_files):
name = package_file_name(package_file)
file_install_dir = os.path.join(install_dir, name)
# Some executables get installed at top-level and some get
# installed under 'bin'. A small number of old packages prefix the
# entire tree with the platform (e.g., chromium/third_party/tcl).
for bin_dir in (
- file_install_dir,
- os.path.join(file_install_dir, 'bin'),
- os.path.join(file_install_dir, platform(), 'bin'),
+ file_install_dir,
+ os.path.join(file_install_dir, 'bin'),
+ os.path.join(file_install_dir, plat, 'bin'),
):
if os.path.isdir(bin_dir):
env_vars.prepend('PATH', bin_dir)
- env_vars.set('PW_{}_CIPD_INSTALL_DIR'.format(name.upper()),
- file_install_dir)
+ env_vars.set(
+ 'PW_{}_CIPD_INSTALL_DIR'.format(name.upper()), file_install_dir
+ )
# Windows has its own special toolchain.
if os.name == 'nt':
env_vars.prepend(
- 'PATH', os.path.join(file_install_dir, 'mingw64', 'bin'))
+ 'PATH', os.path.join(file_install_dir, 'mingw64', 'bin')
+ )
return True