|
1 | | -# TODO: #3559 |
| 1 | +import requests |
| 2 | +from pathlib import Path |
| 3 | +from software.logger.logger import create_logger |
| 4 | +import zipfile |
| 5 | +import tarfile |
| 6 | +import shutil |
| 7 | +from software.thunderscope.constants import RuntimeManagerConstants |
| 8 | +import platform |
| 9 | + |
| 10 | +logger = create_logger(__name__) |
| 11 | + |
| 12 | + |
2 | 13 | class RuntimeInstaller: |
3 | 14 | """Delegate class for handling runtime installation and remote interfacing""" |
4 | 15 |
|
5 | 16 | def __init__(self): |
6 | | - pass |
| 17 | + self.download_urls = [] |
7 | 18 |
|
8 | 19 | def fetch_remote_runtimes(self) -> list[str]: |
9 | 20 | """Requests a list of available runtimes from the remote. This is an expensive operation |
10 | 21 | and should only be called when necessary. |
11 | 22 | :return: A unique list of names for available runtimes |
12 | 23 | """ |
13 | | - return [] |
| 24 | + url = RuntimeManagerConstants.INSTALL_URL |
| 25 | + headers = {"Accept": "application/vnd.github+json"} |
| 26 | + |
| 27 | + response = requests.get(url, headers=headers) |
| 28 | + logger.warning(response) |
| 29 | + |
| 30 | + releases = response.json() |
| 31 | + |
| 32 | + PREFIX = RuntimeManagerConstants.DOWNLOAD_PREFIX |
| 33 | + |
| 34 | + # I'm going to assume you are trying to reload the assets so reset the download_urls |
| 35 | + if len(self.download_urls) != 0: |
| 36 | + self.download_urls = [] |
| 37 | + download_names = [] |
| 38 | + os_name = platform.system() |
| 39 | + |
| 40 | + for release in releases: |
| 41 | + tag = release.get("tag_name", "") |
| 42 | + |
| 43 | + # ✅ Only keep tags that include the OS name |
| 44 | + if os_name not in tag: |
| 45 | + continue |
| 46 | + for asset in release.get("assets", []): |
| 47 | + url = asset["browser_download_url"] |
| 48 | + if "fullsystem" not in asset_name: |
| 49 | + continue |
| 50 | + self.download_urls.append(url) |
| 51 | + trimmed = url.removeprefix(PREFIX) |
| 52 | + download_names.append(trimmed) |
| 53 | + |
| 54 | + return download_names[:5] |
14 | 55 |
|
15 | 56 | def install_runtime(self, version: str) -> None: |
16 | 57 | """Installs the runtime of the specified version or throws an error upon failure. |
17 | 58 | Ensures that the runtime is compatible with the current platform |
18 | 59 | :param version: Version of the runtime hosted on the remote to install |
19 | 60 | """ |
20 | | - pass |
| 61 | + url = RuntimeManagerConstants.INSTALL_URL |
| 62 | + headers = {"Accept": "application/vnd.github+json"} |
| 63 | + |
| 64 | + response = requests.get(url, headers=headers) |
| 65 | + logger.warning(response) |
| 66 | + |
| 67 | + releases = response.json() |
| 68 | + |
| 69 | + TARGET_SUFFIX = version |
| 70 | + |
| 71 | + selected_asset = None |
| 72 | + |
| 73 | + for download_url in self.download_urls: |
| 74 | + if download_url.endswith(TARGET_SUFFIX): |
| 75 | + selected_asset = download_url |
| 76 | + |
| 77 | + if selected_asset: |
| 78 | + url = selected_asset |
| 79 | + filename = Path(url).name |
| 80 | + |
| 81 | + target_dir = Path(RuntimeManagerConstants.EXTERNAL_RUNTIMES_PATH) |
| 82 | + tmp_path = Path("/tmp") / filename |
| 83 | + |
| 84 | + with requests.get(url, stream=True) as r: |
| 85 | + r.raise_for_status() |
| 86 | + with open(tmp_path, "wb") as f: |
| 87 | + for chunk in r.iter_content(chunk_size=8192): |
| 88 | + if chunk: |
| 89 | + f.write(chunk) |
| 90 | + |
| 91 | + if filename.endswith((".tar.gz", ".tgz")): |
| 92 | + with tarfile.open(tmp_path, "r:*") as tar: |
| 93 | + tar.extractall(path=target_dir) |
| 94 | + |
| 95 | + elif filename.endswith(".zip"): |
| 96 | + with zipfile.ZipFile(tmp_path, "r") as zipf: |
| 97 | + zipf.extractall(path=target_dir) |
| 98 | + |
| 99 | + else: |
| 100 | + dest = target_dir / filename |
| 101 | + shutil.copy2(tmp_path, dest) |
| 102 | + dest.chmod(0o755) # make executable (common for runtimes) |
| 103 | + |
| 104 | + if not selected_asset: |
| 105 | + logger.warning("Can't find binary") |
0 commit comments