From 636b86cf3291e3934a1dc0dd57afb5eb7056a431 Mon Sep 17 00:00:00 2001 From: AAGaming Date: Sat, 17 Oct 2026 20:04:39 -0400 Subject: feat(loader): support ARM Linux, experimental builds for every platform that exists (#990) Co-authored-by: Gianni Spadoni Co-authored-by: virtudude Signed-off-by: Gianni Spadoni --- .github/workflows/build-linux-arm64.yml | 14 +++ .github/workflows/build-linux-x86_64.yml | 14 +++ .github/workflows/build-mac.yml | 14 +++ .github/workflows/build-win-arm64.yml | 19 ++++ .github/workflows/build-win-x86_64.yml | 19 ++++ .github/workflows/build-win.yml | 67 ------------- .github/workflows/build.yml | 67 +++++++++---- .github/workflows/edit-check.yml | 4 +- .github/workflows/lint.yml | 7 +- .github/workflows/release.yml | 83 ++++++++++++---- .github/workflows/typecheck.yml | 16 ++-- backend/decky_loader/injector.py | 4 +- .../decky_loader/localplatform/localplatform.py | 3 + backend/decky_loader/plugin/sandboxed_plugin.py | 3 +- backend/decky_loader/updater.py | 106 ++++++++++++++++----- backend/pyinstaller.spec | 27 +++++- .../components/settings/pages/general/index.tsx | 4 +- frontend/src/updater.ts | 2 + 18 files changed, 322 insertions(+), 151 deletions(-) create mode 100644 .github/workflows/build-linux-arm64.yml create mode 100644 .github/workflows/build-linux-x86_64.yml create mode 100644 .github/workflows/build-mac.yml create mode 100644 .github/workflows/build-win-arm64.yml create mode 100644 .github/workflows/build-win-x86_64.yml delete mode 100644 .github/workflows/build-win.yml diff --git a/.github/workflows/build-linux-arm64.yml b/.github/workflows/build-linux-arm64.yml new file mode 100644 index 00000000..fdf84c7e --- /dev/null +++ b/.github/workflows/build-linux-arm64.yml @@ -0,0 +1,14 @@ +name: Builder (Linux, arm64) + +on: + push: + pull_request: + workflow_call: + +jobs: + build: + uses: ./.github/workflows/build.yml + with: + arch: arm64 + os: Linux + artifact_name: PluginLoader-arm64 \ No newline at end of file diff --git a/.github/workflows/build-linux-x86_64.yml b/.github/workflows/build-linux-x86_64.yml new file mode 100644 index 00000000..9aa05b52 --- /dev/null +++ b/.github/workflows/build-linux-x86_64.yml @@ -0,0 +1,14 @@ +name: Builder (Linux, x86_64) + +on: + push: + pull_request: + workflow_call: + +jobs: + build: + uses: ./.github/workflows/build.yml + with: + arch: x86_64 + os: Linux + artifact_name: PluginLoader \ No newline at end of file diff --git a/.github/workflows/build-mac.yml b/.github/workflows/build-mac.yml new file mode 100644 index 00000000..3daf1132 --- /dev/null +++ b/.github/workflows/build-mac.yml @@ -0,0 +1,14 @@ +name: Builder (macOS) + +on: + push: + pull_request: + workflow_call: + +jobs: + build: + uses: ./.github/workflows/build.yml + with: + arch: arm64 + os: macOS + artifact_name: PluginLoader-mac \ No newline at end of file diff --git a/.github/workflows/build-win-arm64.yml b/.github/workflows/build-win-arm64.yml new file mode 100644 index 00000000..7274f235 --- /dev/null +++ b/.github/workflows/build-win-arm64.yml @@ -0,0 +1,19 @@ +name: Builder (Windows, arm64) + +on: + push: + pull_request: + workflow_call: + +jobs: + build: + uses: ./.github/workflows/build.yml + strategy: + matrix: + noconsole: [false, true] + with: + os: Windows + arch: arm64 + artifact_name: PluginLoader-win-arm64${{ matrix.noconsole == true && '-noconsole' || '' }} + binary_name: PluginLoader.exe + noconsole: ${{ matrix.noconsole }} \ No newline at end of file diff --git a/.github/workflows/build-win-x86_64.yml b/.github/workflows/build-win-x86_64.yml new file mode 100644 index 00000000..fa0afb14 --- /dev/null +++ b/.github/workflows/build-win-x86_64.yml @@ -0,0 +1,19 @@ +name: Builder (Windows, x86_64) + +on: + push: + pull_request: + workflow_call: + +jobs: + build: + uses: ./.github/workflows/build.yml + strategy: + matrix: + noconsole: [false, true] + with: + os: Windows + arch: x86_64 + artifact_name: PluginLoader-win${{ matrix.noconsole == true && '-noconsole' || '' }} + binary_name: PluginLoader.exe + noconsole: ${{ matrix.noconsole }} \ No newline at end of file diff --git a/.github/workflows/build-win.yml b/.github/workflows/build-win.yml deleted file mode 100644 index 6cf504c4..00000000 --- a/.github/workflows/build-win.yml +++ /dev/null @@ -1,67 +0,0 @@ -name: Builder Win - -on: - push: - pull_request: - -permissions: - contents: write - -jobs: - build-win: - name: Build PluginLoader for Win - runs-on: windows-2022 - - steps: - - name: Checkout 🧰 - uses: actions/checkout@v4 - - - name: Set up NodeJS 20 💎 - uses: actions/setup-node@v4 - with: - node-version: 20 - - - name: Set up Python 3.11.7 🐍 - uses: actions/setup-python@v5 - with: - python-version: "3.11.7" - - - name: Install Poetry - uses: snok/install-poetry@v1 - with: - virtualenvs-create: false - - - name: Install Python dependencies ⬇️ - working-directory: ./backend - run: | - C:\Users\runneradmin\.local\bin\poetry self add "poetry-dynamic-versioning[plugin]" - C:\Users\runneradmin\.local\bin\poetry install --no-interaction - - - name: Install JS dependencies ⬇️ - working-directory: ./frontend - run: | - npm i -g pnpm - pnpm i --frozen-lockfile - - - name: Build JS Frontend 🛠️ - working-directory: ./frontend - run: pnpm run build - - - name: Build Python Backend 🛠️ - working-directory: ./backend - run: | - C:\Users\runneradmin\.local\bin\poetry dynamic-versioning - C:\Users\runneradmin\.local\bin\poetry run pyinstaller pyinstaller.spec - - - name: Build Python Backend (noconsole) 🛠️ - working-directory: ./backend - run: $env:DECKY_NOCONSOLE = 1; C:\Users\runneradmin\.local\bin\poetry run pyinstaller pyinstaller.spec - - - name: Upload package artifact ⬆️ - uses: actions/upload-artifact@v4 - with: - name: PluginLoader Win - path: | - ./backend/dist/PluginLoader.exe - ./backend/dist/PluginLoader_noconsole.exe - diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 46a106a1..a6a56f39 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,32 +1,59 @@ name: Builder on: - push: - pull_request: workflow_call: - # schedule: - # - cron: '0 13 * * *' # run at 1 PM UTC + inputs: + arch: + description: Architecture to build for (x86_64, arm64) + type: string + default: x86_64 + os: + description: OS to build for (Linux, macOS, Windows) + type: string + default: Linux + artifact_name: + description: Name of the uploaded artifact + type: string + default: PluginLoader + binary_name: + description: Name of the PluginLoader binary + type: string + default: PluginLoader + noconsole: + description: If the windows build is no-console + type: boolean + default: false jobs: build: - name: Build PluginLoader - runs-on: ubuntu-22.04 + name: Build ${{ inputs.artifact_name }} (${{ inputs.os }} ${{ inputs.arch }}) + # No support for mac x86, sorry + runs-on: ${{ + (inputs.os == 'Linux' && inputs.arch == 'arm64' && 'ubuntu-26.04-arm') || + (inputs.os == 'Linux' && inputs.arch == 'x86_64' && 'ubuntu-26.04') || + (inputs.os == 'macOS' && inputs.arch == 'arm64' && 'xcode-27') || + (inputs.os == 'Windows' && inputs.arch == 'arm64' && 'windows-11-arm') || + (inputs.os == 'Windows' && inputs.arch == 'x86_64' && 'windows-2025') + }} + defaults: + run: + shell: bash steps: - name: Checkout 🧰 - uses: actions/checkout@v4 + uses: actions/checkout@v7 with: fetch-depth: 0 - - name: Set up NodeJS 20 💎 - uses: actions/setup-node@v4 + - name: Set up NodeJS 26 💎 + uses: actions/setup-node@v7 with: - node-version: 20 + node-version: 26 - - name: Set up Python 3.11.7 🐍 - uses: actions/setup-python@v5 + - name: Set up Python 3.14.8 🐍 + uses: actions/setup-python@v7 with: - python-version: "3.11.7" + python-version: "3.14.8" - name: Install Poetry uses: snok/install-poetry@v1 @@ -53,17 +80,19 @@ jobs: working-directory: ./backend run: | poetry dynamic-versioning - pyinstaller pyinstaller.spec + DECKY_REPO="${{ github.repository }}" DECKY_BRANCH="${{ github.event.number && format('PR{0}/{1}', github.event.number, github.head_ref) || github.ref_name }}" ${{ inputs.noconsole == true && 'DECKY_NOCONSOLE=1 ' || '' }}pyinstaller pyinstaller.spec + echo Builds: + ls ./dist - name: Upload package artifact ⬆️ if: ${{ !env.ACT }} - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: - name: PluginLoader - path: ./backend/dist/PluginLoader + name: ${{ inputs.artifact_name }} + path: ./backend/dist/${{ inputs.binary_name }} - name: Download package artifact locally if: ${{ env.ACT }} - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: - path: ./backend/dist/PluginLoader + path: ./backend/dist/${{ inputs.binary_name }} diff --git a/.github/workflows/edit-check.yml b/.github/workflows/edit-check.yml index 519d9e3e..95865a3c 100644 --- a/.github/workflows/edit-check.yml +++ b/.github/workflows/edit-check.yml @@ -7,14 +7,14 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@8230315d06ad95c617244d2f265d237a1682d445 + uses: actions/checkout@v7 with: ref: ${{ github.sha }} fetch-depth: 0 token: ${{ secrets.GITHUB_TOKEN }} - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: Get changed files id: changed-files diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 3ff65531..cda19c28 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -10,7 +10,12 @@ jobs: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v4 # Check out the repository first. + - uses: actions/checkout@v7 # Check out the repository first. + + - name: Set up NodeJS 26 💎 + uses: actions/setup-node@v7 + with: + node-version: 26 - name: Install TypeScript dependencies working-directory: frontend diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ac1637e4..9f69a294 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,13 +27,13 @@ permissions: jobs: create_tag: name: Tag a new version of the package - runs-on: ubuntu-22.04 + runs-on: ubuntu-26.04 outputs: tag_name: ${{ steps.ready_tag.outputs.tag_name }} steps: - name: Checkout 🧰 - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: Install semver-tool asdf uses: asdf-vm/actions/install@v4 @@ -42,11 +42,11 @@ jobs: semver 3.4.0 - name: Get latest release - uses: rez0n/actions-github-release@main + uses: rez0n/actions-github-release@v2.1 id: latest_release with: token: ${{ secrets.GITHUB_TOKEN }} - repository: "SteamDeckHomebrew/decky-loader" + repository: ${{ github.repository }} type: "nodraft" - name: Prepare tag ⚙️ @@ -115,42 +115,89 @@ jobs: echo tag_name=v$OUT >> $GITHUB_OUTPUT - name: Push tag 📤 - uses: rickstaa/action-create-tag@v1.3.2 + uses: rickstaa/action-create-tag@v1.7.2 if: ${{ steps.ready_tag.outputs.tag_name && !env.ACT }} with: tag: ${{ steps.ready_tag.outputs.tag_name }} message: Pre-release ${{ steps.ready_tag.outputs.tag_name }} - build: - name: Build tagged artifact - uses: ./.github/workflows/build.yml + build_linux_x86_64: + name: Build tagged artifact (Linux, x86_64) + uses: ./.github/workflows/build-linux-x86_64.yml + needs: [create_tag] + + build_linux_arm64: + name: Build tagged artifact (Linux, arm64) + uses: ./.github/workflows/build-linux-arm64.yml + needs: [create_tag] + + build_win_x86_64: + name: Build tagged artifact (Windows, arm64) + uses: ./.github/workflows/build-win-arm64.yml + needs: [create_tag] + + build_win_arm64: + name: Build tagged artifact (Windows, x86_64) + uses: ./.github/workflows/build-win-x86_64.yml + needs: [create_tag] + + build_macos: + name: Build tagged artifact (macOS) + uses: ./.github/workflows/build-mac.yml needs: [create_tag] release: - name: Release tagged artifact - runs-on: ubuntu-22.04 - needs: [create_tag, build] + name: Release tagged artifacts + runs-on: ubuntu-26.04 + needs: [create_tag, build_linux_x86_64, build_linux_arm64, build_win_x86_64, build_win_arm64, build_macos] steps: - - name: Fetch package artifact ⬇️ - uses: actions/download-artifact@v4 + - name: Fetch package artifacts ⬇️ + uses: actions/download-artifact@v8 with: - name: PluginLoader + path: ./temp_artifacts + + - name: Name release assets 🛠️ + run: | + ARTIFACTS=("default" "arm64" "win" "win-noconsole" "win-arm64" "win-arm64-noconsole" "mac") + + for artifact in "${ARTIFACTS[@]}"; do + echo "naming $artifact" + + if [ "$artifact" = "default" ]; then + suffix="" + else + suffix="-$artifact" + fi + + # stupid windows + if [[ "$artifact" == win* ]]; then + ext=".exe" + else + ext="" + fi + + source="./temp_artifacts/PluginLoader$suffix" + dest="./PluginLoader$suffix$ext" + mv "$source"/PluginLoader$ext "$dest" + done - name: Pre-release 📦 if: github.event.inputs.release == 'prerelease' - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v3 with: name: Prerelease ${{ needs.create_tag.outputs.tag_name }} tag_name: ${{ needs.create_tag.outputs.tag_name }} - files: ./PluginLoader + files: | + ./PluginLoade* prerelease: true generate_release_notes: true - name: Release 📦 if: github.event.inputs.release == 'release' - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v3 with: name: Release ${{ needs.create_tag.outputs.tag_name }} tag_name: ${{ needs.create_tag.outputs.tag_name }} - files: ./PluginLoader + files: | + ./PluginLoade* prerelease: false generate_release_notes: true diff --git a/.github/workflows/typecheck.yml b/.github/workflows/typecheck.yml index 86690aa0..113b3d30 100644 --- a/.github/workflows/typecheck.yml +++ b/.github/workflows/typecheck.yml @@ -10,17 +10,17 @@ jobs: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v4 # Check out the repository first. + - uses: actions/checkout@v7 # Check out the repository first. - - name: Set up NodeJS 20 💎 - uses: actions/setup-node@v4 + - name: Set up NodeJS 26 💎 + uses: actions/setup-node@v7 with: - node-version: 20 + node-version: 26 - - name: Set up Python 3.11.7 🐍 - uses: actions/setup-python@v5 + - name: Set up Python 3.14.8 🐍 + uses: actions/setup-python@v7 with: - python-version: "3.11.7" + python-version: "3.14.8" - name: Install Poetry uses: snok/install-poetry@v1 @@ -40,7 +40,7 @@ jobs: - name: Run pyright (Python) uses: jakebailey/pyright-action@v1 with: - python-version: "3.10.6" + python-version: "3.14.8" no-comments: true working-directory: backend 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: diff --git a/backend/pyinstaller.spec b/backend/pyinstaller.spec index 13ac66a0..3ef888f5 100644 --- a/backend/pyinstaller.spec +++ b/backend/pyinstaller.spec @@ -3,21 +3,38 @@ from PyInstaller.building.build_main import Analysis from PyInstaller.building.api import EXE, PYZ from PyInstaller.utils.hooks import copy_metadata +noconsole_str = os.getenv('DECKY_NOCONSOLE', '0') +noconsole = bool(noconsole_str) +repo = os.getenv('DECKY_REPO', 'SteamDeckHomebrew/decky-loader') +branch = os.getenv('DECKY_BRANCH', 'main') +with open('build_config.py', 'w') as f: + f.write(f""" +import os +os.environ['DECKY_PACKAGED_BUILD'] = '1' + +# Build info +os.environ['DECKY_REPO'] = {repr(repo)} +os.environ['DECKY_BRANCH'] = {repr(branch)} + +# Windows-specific +os.environ['DECKY_NOCONSOLE'] = {repr(noconsole_str)} +""") + +name = "PluginLoader" +# if noconsole: +# name += "_noconsole" + a = Analysis( ['main.py'], datas=[ ('decky_loader/locales', 'decky_loader/locales'), ('decky_loader/static', 'decky_loader/static'), ] + copy_metadata('decky_loader'), + runtime_hooks=['build_config.py'], hiddenimports=['logging.handlers', 'sqlite3', 'http.server', 'socketserver', 'configparser', 'decky_plugin', 'decky'], ) pyz = PYZ(a.pure, a.zipped_data) -noconsole = bool(os.getenv('DECKY_NOCONSOLE')) -name = "PluginLoader" -if noconsole: - name += "_noconsole" - exe = EXE( pyz, a.scripts, diff --git a/frontend/src/components/settings/pages/general/index.tsx b/frontend/src/components/settings/pages/general/index.tsx index 9bd5616d..401da1d2 100644 --- a/frontend/src/components/settings/pages/general/index.tsx +++ b/frontend/src/components/settings/pages/general/index.tsx @@ -57,7 +57,9 @@ export default function GeneralSettings({ {t('SettingsGeneralIndex.about.header')} -
{versionInfo?.current}
+
+ {versionInfo?.current} ({versionInfo?.repo}#{versionInfo?.gitBranch}) +
diff --git a/frontend/src/updater.ts b/frontend/src/updater.ts index a8853b18..7f997b7a 100644 --- a/frontend/src/updater.ts +++ b/frontend/src/updater.ts @@ -18,6 +18,8 @@ export interface RemoteVerInfo { export interface VerInfo { current: string; + repo: string; + gitBranch: string; remote: RemoteVerInfo | null; all: RemoteVerInfo[] | null; updatable: boolean; -- cgit v1.2.3