pw_package: Use CIPD packages for arduino cores

Use "pigweed_internal/third_party/teensy/${platform}" CIPD packages
if they are readable by the user.

TEST: pw presubmit --step gn_teensy_build

Change-Id: I73b9fb31e314c71c8cea357e29f7b9e788b6f894
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/30480
Commit-Queue: Anthony DiGirolamo <tonymd@google.com>
Reviewed-by: Rob Mohr <mohrr@google.com>
diff --git a/pw_arduino_build/py/pw_arduino_build/core_installer.py b/pw_arduino_build/py/pw_arduino_build/core_installer.py
index 3428ad7..2d3dbf7 100644
--- a/pw_arduino_build/py/pw_arduino_build/core_installer.py
+++ b/pw_arduino_build/py/pw_arduino_build/core_installer.py
@@ -106,7 +106,9 @@
             },
             "teensyduino": {
                 "url": "https://www.pjrc.com/teensy/td_153/TeensyduinoInstall.exe",
-                "file_name": "TeensyduinoInstall.exe",
+                # The installer should be named 'Teensyduino.exe' instead of
+                # 'TeensyduinoInstall.exe' to trigger a non-admin installation.
+                "file_name": "Teensyduino.exe",
                 "sha256": "88f58681e5c4772c54e462bc88280320e4276e5b316dcab592fe38d96db990a1",
             },
         }
@@ -116,6 +118,7 @@
             "core": {
                 "version": "1.6.2",
                 "url": "https://github.com/adafruit/ArduinoCore-samd/archive/1.6.2.tar.gz",
+                "file_name": "adafruit-samd-1.6.2.tar.gz",
                 "sha256": "5875f5bc05904c10e6313f02653f28f2f716db639d3d43f5a1d8a83d15339d64",
             }
         },
@@ -128,7 +131,7 @@
             "core": {
                 "version": "1.8.8",
                 "url": "http://downloads.arduino.cc/cores/samd-1.8.8.tar.bz2",
-                "file_name": "samd-1.8.8.tar.bz2",
+                "file_name": "arduino-samd-1.8.8.tar.bz2",
                 "sha256": "7b93eb705cba9125d9ee52eba09b51fb5fe34520ada351508f4253abbc9f27fa",
             }
         },
@@ -141,6 +144,7 @@
             "core": {
                 "version": "1.9.0",
                 "url": "https://github.com/stm32duino/Arduino_Core_STM32/archive/1.9.0.tar.gz",
+                "file_name": "stm32duino-1.9.0.tar.gz",
                 "sha256": "4f75ba7a117d90392e8f67c58d31d22393749b9cdd3279bc21e7261ec06c62bf",
             }
         },
@@ -206,13 +210,15 @@
     arduino_zipfile = file_operations.download_to_cache(
         url=arduino_artifact["url"],
         expected_sha256sum=arduino_artifact["sha256"],
-        cache_directory=cache_dir)
+        cache_directory=cache_dir,
+        downloaded_file_name=arduino_artifact["file_name"])
 
     teensyduino_artifact = teensy_artifacts["teensyduino"]
     teensyduino_installer = file_operations.download_to_cache(
         url=teensyduino_artifact["url"],
         expected_sha256sum=teensyduino_artifact["sha256"],
-        cache_directory=cache_dir)
+        cache_directory=cache_dir,
+        downloaded_file_name=teensyduino_artifact["file_name"])
 
     file_operations.extract_archive(arduino_zipfile, install_dir, cache_dir)
 
@@ -282,7 +288,8 @@
     teensyduino_zip = file_operations.download_to_cache(
         url=teensyduino_artifact["url"],
         expected_sha256sum=teensyduino_artifact["sha256"],
-        cache_directory=cache_dir)
+        cache_directory=cache_dir,
+        downloaded_file_name=teensyduino_artifact["file_name"])
 
     extracted_files = file_operations.extract_archive(
         teensyduino_zip,
@@ -303,13 +310,15 @@
     arduino_tarfile = file_operations.download_to_cache(
         url=arduino_artifact["url"],
         expected_sha256sum=arduino_artifact["sha256"],
-        cache_directory=cache_dir)
+        cache_directory=cache_dir,
+        downloaded_file_name=arduino_artifact["file_name"])
 
     teensyduino_artifact = teensy_artifacts["teensyduino"]
     teensyduino_installer = file_operations.download_to_cache(
         url=teensyduino_artifact["url"],
         expected_sha256sum=teensyduino_artifact["sha256"],
-        cache_directory=cache_dir)
+        cache_directory=cache_dir,
+        downloaded_file_name=teensyduino_artifact["file_name"])
 
     file_operations.extract_archive(arduino_tarfile, install_dir, cache_dir)
     os.chmod(teensyduino_installer,
@@ -351,7 +360,8 @@
     core_tarfile = file_operations.download_to_cache(
         url=artifacts["url"],
         expected_sha256sum=artifacts["sha256"],
-        cache_directory=cache_dir)
+        cache_directory=cache_dir,
+        downloaded_file_name=artifacts["file_name"])
 
     package_path = os.path.join(install_dir, "hardware", "samd",
                                 artifacts["version"])
@@ -371,7 +381,8 @@
     core_tarfile = file_operations.download_to_cache(
         url=artifacts["url"],
         expected_sha256sum=artifacts["sha256"],
-        cache_directory=cache_dir)
+        cache_directory=cache_dir,
+        downloaded_file_name=artifacts["file_name"])
 
     package_path = os.path.join(install_dir, "hardware", "samd",
                                 artifacts["version"])
@@ -393,7 +404,8 @@
     core_tarfile = file_operations.download_to_cache(
         url=artifacts["url"],
         expected_sha256sum=artifacts["sha256"],
-        cache_directory=cache_dir)
+        cache_directory=cache_dir,
+        downloaded_file_name=artifacts["file_name"])
 
     package_path = os.path.join(install_dir, "hardware", "stm32",
                                 artifacts["version"])
diff --git a/pw_arduino_build/py/pw_arduino_build/file_operations.py b/pw_arduino_build/py/pw_arduino_build/file_operations.py
index e534373..eb5fa41 100644
--- a/pw_arduino_build/py/pw_arduino_build/file_operations.py
+++ b/pw_arduino_build/py/pw_arduino_build/file_operations.py
@@ -99,11 +99,15 @@
 def download_to_cache(url: str,
                       expected_md5sum=None,
                       expected_sha256sum=None,
-                      cache_directory=".cache") -> str:
+                      cache_directory=".cache",
+                      downloaded_file_name=None) -> str:
 
     cache_dir = os.path.realpath(
         os.path.expanduser(os.path.expandvars(cache_directory)))
-    downloaded_file = os.path.join(cache_dir, url.split("/")[-1])
+    if not downloaded_file_name:
+        # Use the last part of the URL as the file name.
+        downloaded_file_name = url.split("/")[-1]
+    downloaded_file = os.path.join(cache_dir, downloaded_file_name)
 
     if not os.path.exists(downloaded_file):
         _LOG.info("Downloading: %s", url)
diff --git a/pw_package/py/pw_package/packages/arduino_core.py b/pw_package/py/pw_package/packages/arduino_core.py
index 65c0c91..994c95c 100644
--- a/pw_package/py/pw_package/packages/arduino_core.py
+++ b/pw_package/py/pw_package/packages/arduino_core.py
@@ -13,8 +13,11 @@
 # the License.
 """Install and check status of teensy-core."""
 
+import json
 import logging
 import re
+import subprocess
+import tempfile
 from pathlib import Path
 from typing import Sequence
 
@@ -33,12 +36,58 @@
     def status(self, path: Path) -> bool:
         return (path / 'hardware').is_dir()
 
+    def populate_download_cache_from_cipd(self, path: Path) -> None:
+        """Check for arduino core availability in pigweed_internal cipd."""
+        package_path = path.parent.resolve()
+        core_name = self.name
+        core_cache_path = package_path / ".cache" / core_name
+        core_cache_path.mkdir(parents=True, exist_ok=True)
+
+        cipd_package_subpath = "pigweed_internal/third_party/"
+        cipd_package_subpath += core_name
+        cipd_package_subpath += "/${platform}"
+
+        # Check if teensy cipd package is readable
+
+        with tempfile.NamedTemporaryFile(prefix='cipd',
+                                         delete=True) as temp_json:
+            cipd_acl_check_command = [
+                "cipd",
+                "acl-check",
+                cipd_package_subpath,
+                "-reader",
+                "-json-output",
+                temp_json.name,
+            ]
+            subprocess.run(cipd_acl_check_command, capture_output=True)
+            # Return if no packages are readable.
+            if not json.load(temp_json)['result']:
+                return
+
+        def _run_command(command):
+            _LOG.debug("Running: `%s`", " ".join(command))
+            result = subprocess.run(command, capture_output=True)
+            _LOG.debug("Output:\n%s",
+                       result.stdout.decode() + result.stderr.decode())
+
+        _run_command(["cipd", "init", "-force", core_cache_path.as_posix()])
+        _run_command([
+            "cipd", "install", cipd_package_subpath, "-root",
+            core_cache_path.as_posix(), "-force"
+        ])
+
+        _LOG.debug(
+            "Available Cache Files:\n%s",
+            "\n".join([p.as_posix() for p in core_cache_path.glob("*")]))
+
     def install(self, path: Path) -> None:
+        self.populate_download_cache_from_cipd(path)
+
         if self.status(path):
             return
         # Otherwise delete current version and reinstall
         core_installer.install_core(path.parent.resolve().as_posix(),
-                                    path.name)
+                                    self.name)
 
     def info(self, path: Path) -> Sequence[str]:
         packages_root = path.parent.resolve()