pw_env_setup: Run cipd auth-login if necessary
If the user is not logged in and does not have access to a required path
in CIPD, run 'cipd auth-login' inside bootstrap.
Since the spinner made this awkward, enable pausing the spinner and
pause it while running 'cipd auth-login'.
Tested by running 'cipd auth-logout' followed by bootstrap of a project
with ACL-d packages.
Change-Id: I217c6a97a5161e9a5894a76a35321466ecb672d0
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/36900
Pigweed-Auto-Submit: Rob Mohr <mohrr@google.com>
Reviewed-by: Joe Ethier <jethier@google.com>
Reviewed-by: Keir Mierle <keir@google.com>
Commit-Queue: Auto-Submit <auto-submit@pigweed.google.com.iam.gserviceaccount.com>
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 84c48c8..c4301ec 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
@@ -61,7 +61,7 @@
return parser.parse_args(argv)
-def check_auth(cipd, package_files):
+def check_auth(cipd, package_files, spin):
"""Check have access to CIPD pigweed directory."""
paths = []
@@ -77,12 +77,12 @@
parts.pop(-1)
paths.append('/'.join(parts))
+ username = None
try:
output = subprocess.check_output([cipd, 'auth-info'],
stderr=subprocess.STDOUT).decode()
logged_in = True
- username = None
match = re.search(r'Logged in as (\S*)\.', output)
if match:
username = match.group(1)
@@ -90,38 +90,60 @@
except subprocess.CalledProcessError:
logged_in = False
- for path in paths:
- # 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()
- if 'No matching packages' not in output:
- continue
+ def _check_all_paths():
+ inaccessible_paths = []
- # 'cipd ls' only lists sub-packages but ignores any packages at the
- # given path. 'cipd instances' will give versions of that package.
- # '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)
- except subprocess.CalledProcessError:
+ for path in paths:
+ # 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()
+ if 'No matching packages' not in output:
+ continue
+
+ # 'cipd ls' only lists sub-packages but ignores any packages at the
+ # given path. 'cipd instances' will give versions of that package.
+ # '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)
+ except subprocess.CalledProcessError:
+ inaccessible_paths.append(path)
+
+ return inaccessible_paths
+
+ inaccessible_paths = _check_all_paths()
+
+ if inaccessible_paths and not logged_in:
+ with spin.pause():
stderr = lambda *args: print(*args, file=sys.stderr)
stderr()
- stderr('=' * 60)
- stderr('ERROR: no access to CIPD path "{}"'.format(path))
- if logged_in:
- username_part = ''
- if username:
- username_part = '({}) '.format(username)
- stderr('Your account {}does not have access to this '
- 'path'.format(username_part))
- else:
- stderr('Try logging in with this command:')
- stderr()
- stderr(' {} auth-login'.format(cipd))
- stderr('=' * 60)
- return False
+ stderr('No 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'])
+ except subprocess.CalledProcessError:
+ stderr('CIPD login failed')
+ return False
+
+ inaccessible_paths = _check_all_paths()
+
+ if inaccessible_paths:
+ stderr = lambda *args: print(*args, file=sys.stderr)
+ stderr('=' * 60)
+ username_part = ''
+ if username:
+ username_part = '({}) '.format(username)
+ stderr('Your account {}does not have access to the following '
+ 'paths'.format(username_part))
+ for path in inaccessible_paths:
+ stderr(' {}'.format(path))
+ stderr('=' * 60)
+ return False
return True
@@ -151,10 +173,11 @@
root_install_dir,
cache_dir,
env_vars=None,
+ spin=None,
):
"""Grab the tools listed in ensure_files."""
- if not check_auth(cipd, package_files):
+ if not check_auth(cipd, package_files, spin):
return False
# TODO(mohrr) use os.makedirs(..., exist_ok=True).
diff --git a/pw_env_setup/py/pw_env_setup/env_setup.py b/pw_env_setup/py/pw_env_setup/env_setup.py
index 42470c8..b3f1fdd 100755
--- a/pw_env_setup/py/pw_env_setup/env_setup.py
+++ b/pw_env_setup/py/pw_env_setup/env_setup.py
@@ -339,7 +339,7 @@
spin = spinner.Spinner()
with spin():
- result = step()
+ result = step(spin)
self._log(result.status_str())
@@ -394,7 +394,7 @@
return 0
- def cipd(self):
+ def cipd(self, spin):
install_dir = os.path.join(self._install_dir, 'cipd')
cipd_client = cipd_wrapper.init(install_dir, silent=True)
@@ -409,12 +409,13 @@
root_install_dir=install_dir,
package_files=package_files,
cache_dir=self._cipd_cache_dir,
- env_vars=self._env):
+ env_vars=self._env,
+ spin=spin):
return result(_Result.Status.FAILED)
return result(_Result.Status.DONE)
- def virtualenv(self):
+ def virtualenv(self, unused_spin):
"""Setup virtualenv."""
requirements, req_glob_warnings = _process_globs(
@@ -453,7 +454,7 @@
return result(_Result.Status.DONE)
- def host_tools(self):
+ def host_tools(self, unused_spin):
# The host tools are grabbed from CIPD, at least initially. If the
# user has a current host build, that build will be used instead.
# TODO(mohrr) find a way to do stuff like this for all projects.
@@ -461,14 +462,14 @@
self._env.prepend('PATH', os.path.join(host_dir, 'host_tools'))
return _Result(_Result.Status.DONE)
- def win_scripts(self):
+ def win_scripts(self, unused_spin):
# These scripts act as a compatibility layer for windows.
env_setup_dir = os.path.join(self._pw_root, 'pw_env_setup')
self._env.prepend('PATH', os.path.join(env_setup_dir,
'windows_scripts'))
return _Result(_Result.Status.DONE)
- def cargo(self):
+ def cargo(self, unused_spin):
install_dir = os.path.join(self._install_dir, 'cargo')
package_files, glob_warnings = _process_globs(self._cargo_package_file)
diff --git a/pw_env_setup/py/pw_env_setup/spinner.py b/pw_env_setup/py/pw_env_setup/spinner.py
index 44990be..63d577ea 100644
--- a/pw_env_setup/py/pw_env_setup/spinner.py
+++ b/pw_env_setup/py/pw_env_setup/spinner.py
@@ -65,3 +65,11 @@
yield self
finally:
self.stop()
+
+ @contextlib.contextmanager
+ def pause(self):
+ try:
+ self.stop()
+ yield self
+ finally:
+ self.start()