diff options
Diffstat (limited to 'backend/decky_loader')
| -rw-r--r-- | backend/decky_loader/injector.py | 4 | ||||
| -rw-r--r-- | backend/decky_loader/localplatform/localplatform.py | 3 | ||||
| -rw-r--r-- | backend/decky_loader/plugin/sandboxed_plugin.py | 3 | ||||
| -rw-r--r-- | backend/decky_loader/updater.py | 106 |
4 files changed, 86 insertions, 30 deletions
diff --git a/backend/decky_loader/injector.py b/backend/decky_loader/injector.py index 39b5b15d..ded58193 100644 --- a/backend/decky_loader/injector.py +++ b/backend/decky_loader/injector.py @@ -4,7 +4,7 @@ from asyncio import sleep from logging import getLogger from typing import Any, Callable, List, TypedDict, Dict -from aiohttp import ClientSession +from aiohttp import ClientSession, ClientTimeout from aiohttp.client_exceptions import ClientConnectorError, ClientOSError from asyncio.exceptions import TimeoutError import uuid @@ -374,7 +374,7 @@ async def get_tabs() -> List[Tab]: while True: try: async with ClientSession() as web: - res = await web.get(f"{BASE_ADDRESS}/json", timeout=3) + res = await web.get(f"{BASE_ADDRESS}/json", timeout=ClientTimeout(total=3)) except ClientConnectorError: if not na: logger.debug("Steam isn't available yet. Wait for a moment...") diff --git a/backend/decky_loader/localplatform/localplatform.py b/backend/decky_loader/localplatform/localplatform.py index 6cb08982..e7760315 100644 --- a/backend/decky_loader/localplatform/localplatform.py +++ b/backend/decky_loader/localplatform/localplatform.py @@ -4,6 +4,9 @@ ON_WINDOWS = platform.system() == "Windows" ON_MAC = platform.system() == "Darwin" ON_LINUX = not ON_WINDOWS and not ON_MAC +# macOS reports arm64, Linux reports aarch64 +ON_ARM64 = platform.machine() == "arm64" or platform.machine() == "aarch64" + if ON_WINDOWS: from .localplatformwin import * from . import localplatformwin as localplatform diff --git a/backend/decky_loader/plugin/sandboxed_plugin.py b/backend/decky_loader/plugin/sandboxed_plugin.py index 675eb464..668e1d25 100644 --- a/backend/decky_loader/plugin/sandboxed_plugin.py +++ b/backend/decky_loader/plugin/sandboxed_plugin.py @@ -211,5 +211,4 @@ class SandboxedPlugin: except Exception as e: d["res"] = str(e) d["success"] = False - finally: - return dumps(d, ensure_ascii=False) + return dumps(d, ensure_ascii=False) diff --git a/backend/decky_loader/updater.py b/backend/decky_loader/updater.py index 3fdfcc04..0a9356b6 100644 --- a/backend/decky_loader/updater.py +++ b/backend/decky_loader/updater.py @@ -1,12 +1,11 @@ from __future__ import annotations from asyncio import sleep from logging import getLogger -import os -from os import getcwd, path, remove +from os import getcwd, getenv, path, remove, mkdir from typing import TYPE_CHECKING, List, TypedDict if TYPE_CHECKING: from .main import PluginManager -from .localplatform.localplatform import chmod, service_restart, service_stop, ON_LINUX, ON_WINDOWS, get_keep_systemd_service, get_selinux +from .localplatform.localplatform import chmod, service_restart, service_stop, ON_LINUX, ON_WINDOWS, ON_MAC, ON_ARM64, get_keep_systemd_service, get_selinux import shutil from typing import List, TYPE_CHECKING, TypedDict import zipfile @@ -42,6 +41,8 @@ class Updater: self.remoteVer: RemoteVer | None = None self.allRemoteVers: List[RemoteVer] = [] self.localVer = helpers.get_loader_version() + self.repo = getenv('DECKY_REPO', 'SteamDeckHomebrew/decky-loader') + self.git_branch = getenv('DECKY_BRANCH', 'dirty') try: self.currentBranch = self.get_branch(self.context.settings) @@ -50,13 +51,13 @@ class Updater: logger.error("Current branch could not be determined, defaulting to \"Stable\"") if context: - context.ws.add_route("updater/get_version_info", self.get_version_info); - context.ws.add_route("updater/check_for_updates", self.check_for_updates); - context.ws.add_route("updater/do_restart", self.do_restart); - context.ws.add_route("updater/do_shutdown", self.do_shutdown); - context.ws.add_route("updater/do_update", self.do_update); - context.ws.add_route("updater/get_testing_versions", self.get_testing_versions); - context.ws.add_route("updater/download_testing_version", self.download_testing_version); + context.ws.add_route("updater/get_version_info", self.get_version_info) + context.ws.add_route("updater/check_for_updates", self.check_for_updates) + context.ws.add_route("updater/do_restart", self.do_restart) + context.ws.add_route("updater/do_shutdown", self.do_shutdown) + context.ws.add_route("updater/do_update", self.do_update) + context.ws.add_route("updater/get_testing_versions", self.get_testing_versions) + context.ws.add_route("updater/download_testing_version", self.download_testing_version) context.loop.create_task(self.version_reloader()) def get_branch(self, manager: SettingsManager): @@ -83,17 +84,19 @@ class Updater: branch = self.get_branch(self.context.settings) match branch: case 0: - url = "https://raw.githubusercontent.com/SteamDeckHomebrew/decky-loader/main/dist/plugin_loader-release.service" + url = f"https://raw.githubusercontent.com/{self.repo}/{self.git_branch}/dist/plugin_loader-release.service" case 1 | 2: - url = "https://raw.githubusercontent.com/SteamDeckHomebrew/decky-loader/main/dist/plugin_loader-prerelease.service" + url = f"https://raw.githubusercontent.com/{self.repo}/{self.git_branch}/dist/plugin_loader-prerelease.service" case _: logger.error("You have an invalid branch set... Defaulting to prerelease service, please send the logs to the devs!") - url = "https://raw.githubusercontent.com/SteamDeckHomebrew/decky-loader/main/dist/plugin_loader-prerelease.service" + url = f"https://raw.githubusercontent.com/{self.repo}/{self.git_branch}/dist/plugin_loader-prerelease.service" return str(url) async def get_version_info(self): return { "current": self.localVer, + "repo": self.repo, + "gitBranch": self.git_branch, "remote": self.remoteVer, "all": self.allRemoteVers, "updatable": self.localVer != "unknown" and self.localVer != "dev" @@ -103,7 +106,7 @@ class Updater: logger.debug("checking for updates") selectedBranch = self.get_branch(self.context.settings) async with ClientSession() as web: - async with web.request("GET", "https://api.github.com/repos/SteamDeckHomebrew/decky-loader/releases", headers={'X-GitHub-Api-Version': '2022-11-28'}, ssl=helpers.get_ssl_context()) as res: + async with web.request("GET", f"https://api.github.com/repos/{self.repo}/releases", headers={'X-GitHub-Api-Version': '2022-11-28'}, ssl=helpers.get_ssl_context()) as res: remoteVersions: List[RemoteVer] = await res.json() if selectedBranch == 0: logger.debug("release type: release") @@ -138,12 +141,48 @@ class Updater: pass await sleep(60 * 60 * 6) # 6 hours + def get_remote_binary_name(self, with_extension: bool = True): + """ + The binaries on GitHub contain an extra part in their name to denote their architecture + + For example: + Windows: PluginLoader.exe + Linux (x86_64): PluginLoader + Linux (arm64): PluginLoader-arm64 + """ + binary_name = "PluginLoader" + + if ON_ARM64 and ON_LINUX: + binary_name += "-arm64" + + if ON_WINDOWS: + binary_name += "-win" + if ON_ARM64: + binary_name += "-arm64" + if bool(getenv("DECKY_NOCONSOLE", "0")): + binary_name += "-noconsole" + if with_extension: + binary_name += ".exe" + + if ON_MAC: + binary_name += "-mac" + + return binary_name + + def get_local_binary_name(self): + binary_name = "PluginLoader" + + if ON_WINDOWS: + binary_name += ".exe" + + return binary_name + async def download_decky_binary(self, download_url: str, version: str, is_zip: bool = False, size_in_bytes: int | None = None): - download_filename = "PluginLoader" if ON_LINUX else "PluginLoader.exe" + download_filename = self.get_local_binary_name() download_temp_filename = download_filename + ".new" if size_in_bytes == None: - size_in_bytes = 26214400 # 25MiB, a reasonable overestimate (19.6MiB as of 2024/02/25) + size_in_bytes = 35000000 # 35MiB, a reasonable overestimate (30.00 MiB as of 2026/10/05) async with ClientSession() as web: logger.debug("Downloading binary") @@ -196,10 +235,10 @@ class Updater: version = self.remoteVer["tag_name"] download_url = None size_in_bytes = None - download_filename = "PluginLoader" if ON_LINUX else "PluginLoader.exe" + remote_binary_name = self.get_remote_binary_name() for x in self.remoteVer["assets"]: - if x["name"] == download_filename: + if x["name"] == remote_binary_name: download_url = x["browser_download_url"] size_in_bytes = x["size"] break @@ -233,8 +272,8 @@ class Updater: logger.debug("Saved service file") logger.debug("Copying service file over current file.") shutil.copy(service_file_path, "/etc/systemd/system/plugin_loader.service") - if not os.path.exists(path.join(getcwd(), ".systemd")): - os.mkdir(path.join(getcwd(), ".systemd")) + if not path.exists(path.join(getcwd(), ".systemd")): + mkdir(path.join(getcwd(), ".systemd")) shutil.move(service_file_path, path.join(getcwd(), ".systemd")+"/plugin_loader.service") await self.download_decky_binary(download_url, version, size_in_bytes=size_in_bytes) @@ -249,7 +288,7 @@ class Updater: async def get_testing_versions(self) -> List[TestingVersion]: result: List[TestingVersion] = [] async with ClientSession() as web: - async with web.request("GET", "https://api.github.com/repos/SteamDeckHomebrew/decky-loader/pulls", + async with web.request("GET", f"https://api.github.com/repos/{self.repo}/pulls", headers={'X-GitHub-Api-Version': '2022-11-28'}, params={'state':'open'}, ssl=helpers.get_ssl_context()) as res: open_prs = await res.json() for pr in open_prs: @@ -265,27 +304,42 @@ class Updater: down_id = '' #Get all the associated workflow run for the given sha_id code hash async with ClientSession() as web: - async with web.request("GET", "https://api.github.com/repos/SteamDeckHomebrew/decky-loader/actions/runs", + async with web.request("GET", f"https://api.github.com/repos/{self.repo}/actions/runs", headers={'X-GitHub-Api-Version': '2022-11-28'}, params={'head_sha': sha_id}, ssl=helpers.get_ssl_context()) as res: works = await res.json() #Iterate over the workflow_run to get the two builds if they exists for work in works['workflow_runs']: - if ON_WINDOWS and work['name'] == 'Builder Win': + if ON_WINDOWS and ON_ARM64 and work['name'] == 'Builder (Windows, arm64)': down_id=work['id'] break - elif ON_LINUX and work['name'] == 'Builder': + elif ON_WINDOWS and not ON_ARM64 and work['name'] == 'Builder (Windows, x86_64)': down_id=work['id'] break + elif ON_LINUX and ON_ARM64 and work['name'] == 'Builder (Linux, arm64)': + down_id=work['id'] + break + elif ON_LINUX and not ON_ARM64 and work['name'] == 'Builder (Linux, x86_64)': + down_id=work['id'] + break + elif ON_MAC and work['name'] == 'Builder (macOS)': + down_id=work['id'] + break + if down_id != '': async with ClientSession() as web: - async with web.request("GET", f"https://api.github.com/repos/SteamDeckHomebrew/decky-loader/actions/runs/{down_id}/artifacts", + async with web.request("GET", f"https://api.github.com/repos/{self.repo}/actions/runs/{down_id}/artifacts", headers={'X-GitHub-Api-Version': '2022-11-28'}, ssl=helpers.get_ssl_context()) as res: jresp = await res.json() #If the request found at least one artifact to download... if int(jresp['total_count']) != 0: # this assumes that the artifact we want is the first one! artifact = jresp['artifacts'][0] - down_link = f"https://nightly.link/SteamDeckHomebrew/decky-loader/actions/artifacts/{artifact['id']}.zip" + target_art = self.get_remote_binary_name(False) + for art in jresp['artifacts']: + if art['name'] == target_art: + artifact = art + break + down_link = f"https://nightly.link/{self.repo}/actions/artifacts/{artifact['id']}.zip" #Then fetch it and restart itself await self.download_decky_binary(down_link, f'PR-{pr_id}', is_zip=True, size_in_bytes=artifact.get('size_in_bytes',None)) else: |
