From b0def277ea5de5eee9f4e67829853868853256fb Mon Sep 17 00:00:00 2001 From: Mykola Grymalyuk Date: Mon, 27 May 2024 11:29:04 -0600 Subject: [PATCH] updates.py: Add support for PKG updates --- SOURCE.md | 2 +- opencore_legacy_patcher/support/updates.py | 30 +---- .../wx_gui/gui_settings.py | 2 +- opencore_legacy_patcher/wx_gui/gui_update.py | 113 +++++------------- 4 files changed, 36 insertions(+), 111 deletions(-) diff --git a/SOURCE.md b/SOURCE.md index 5977792543..8bd2a036fe 100644 --- a/SOURCE.md +++ b/SOURCE.md @@ -2,7 +2,7 @@ OpenCore Legacy Patcher at its core is a Python-based GUI/CLI-based application. In turn, to run the project from source, you simply need to invoke the OpenCore-Patcher-GUI.command file via Python. -For developers wishing to validate mainline changes, you may use this link: [GUI (Graphical Based App)](https://nightly.link/dortania/OpenCore-Legacy-Patcher/workflows/build-app-wxpython/main/OpenCore-Patcher.app%20%28GUI%29.zip) +For developers wishing to validate mainline changes, you may use this link: [GUI (Graphical Based App)](https://nightly.link/dortania/OpenCore-Legacy-Patcher/workflows/build-app-wxpython/main/OpenCore-Patcher.pkg.zip) * **Warning**: Nightly builds (untagged builds built from the latest commit) are actively developed OpenCore Legacy Patcher builds. These builds have not been tested, are not guaranteed to work, and are not guaranteed to be safe. Do not use nightlies without a good reason to do so, and do not use them on your main machine. Additionally, these binaries should not be used without first consulting the [CHANGELOG](./CHANGELOG.md). diff --git a/opencore_legacy_patcher/support/updates.py b/opencore_legacy_patcher/support/updates.py index 4e5e220e59..bf10de30af 100644 --- a/opencore_legacy_patcher/support/updates.py +++ b/opencore_legacy_patcher/support/updates.py @@ -78,33 +78,6 @@ def _check_if_build_newer(self, first_version: Union[str, version.Version], seco return first_version > second_version - def _determine_local_build_type(self) -> str: - """ - Check if the local build is a GUI or TUI build - - Returns: - str: "GUI" or "TUI" - """ - - return "GUI" if self.constants.wxpython_variant else "TUI" - - def _determine_remote_type(self, remote_name: str) -> str: - """ - Check if the remote build is a GUI or TUI build - - Parameters: - remote_name (str): Name of the remote build - - Returns: - str: "GUI" or "TUI" - """ - - if "TUI" in remote_name: - return "TUI" - elif "GUI" in remote_name: - return "GUI" - else: - return "Unknown" def check_binary_updates(self) -> Optional[dict]: """ @@ -143,12 +116,11 @@ def check_binary_updates(self) -> Optional[dict]: for asset in data_set["assets"]: logging.info(f"Found asset: {asset['name']}") - if self._determine_remote_type(asset["name"]) == self._determine_local_build_type(): + if asset["name"] == "OpenCore-Patcher.pkg": self.latest_details = { "Name": asset["name"], "Version": latest_remote_version, "Link": asset["browser_download_url"], - "Type": self._determine_remote_type(asset["name"]), "Github Link": f"https://github.com/dortania/OpenCore-Legacy-Patcher/releases/{latest_remote_version}", } return self.latest_details diff --git a/opencore_legacy_patcher/wx_gui/gui_settings.py b/opencore_legacy_patcher/wx_gui/gui_settings.py index 8dfe48a6d4..201d1a6901 100644 --- a/opencore_legacy_patcher/wx_gui/gui_settings.py +++ b/opencore_legacy_patcher/wx_gui/gui_settings.py @@ -1303,7 +1303,7 @@ def on_nightly(self, event: wx.Event) -> None: title=self.title, global_constants=self.constants, screen_location=self.parent.GetPosition(), - url=f"https://nightly.link/dortania/OpenCore-Legacy-Patcher/workflows/build-app-wxpython/{branch}/OpenCore-Patcher.app%20%28GUI%29.zip", + url=f"https://nightly.link/dortania/OpenCore-Legacy-Patcher/workflows/build-app-wxpython/{branch}/OpenCore-Patcher.pkg.zip", version_label="(Nightly)" ) diff --git a/opencore_legacy_patcher/wx_gui/gui_update.py b/opencore_legacy_patcher/wx_gui/gui_update.py index d08a49ca04..b96016798c 100644 --- a/opencore_legacy_patcher/wx_gui/gui_update.py +++ b/opencore_legacy_patcher/wx_gui/gui_update.py @@ -43,7 +43,7 @@ def __init__(self, parent: wx.Frame, title: str, global_constants: constants.Con self.title: str = title self.constants: constants.Constants = global_constants - self.application_path = self.constants.payload_path / "OpenCore-Patcher.app" + self.pkg_download_path = self.constants.payload_path / "OpenCore-Patcher.pkg" self.screen_location: wx.Point = screen_location if parent: self.parent.Centre() @@ -98,7 +98,8 @@ def __init__(self, parent: wx.Frame, title: str, global_constants: constants.Con download_obj = None def _fetch_update() -> None: nonlocal download_obj - download_obj = network_handler.DownloadObject(url, self.constants.payload_path / "OpenCore-Patcher-GUI.app.zip") + file_name = "OpenCore-Patcher.pkg.zip" if url.endswith(".zip") else "OpenCore-Patcher.pkg" + download_obj = network_handler.DownloadObject(url, self.constants.payload_path / file_name) thread = threading.Thread(target=_fetch_update) thread.start() @@ -189,90 +190,37 @@ def _fetch_update() -> None: def _extract_update(self) -> None: """ Extracts the update + + Logic: + - Distributed through GitHub Actions: Requires extraction + - Distributed through GitHub Releases: No extraction required """ - logging.info("Extracting update") - if Path(self.application_path).exists(): - subprocess.run(["/bin/rm", "-rf", str(self.application_path)]) - - # Some hell spawn at Github decided to double zip our Github Actions artifacts - # So we need to unzip it twice - for i in range(2): - result = subprocess.run( - ["/usr/bin/ditto", "-xk", str(self.constants.payload_path / "OpenCore-Patcher-GUI.app.zip"), str(self.constants.payload_path)], capture_output=True - ) - if result.returncode != 0: - logging.error(f"Failed to extract update.") - subprocess_wrapper.log(result) - wx.CallAfter(self.progress_bar_animation.stop_pulse) - wx.CallAfter(self.progress_bar.SetValue, 0) - wx.CallAfter(wx.MessageBox, f"Failed to extract update. Error: {result.stderr.decode('utf-8')}", "Critical Error!", wx.OK | wx.ICON_ERROR) - wx.CallAfter(sys.exit, 1) - break + # GitHub Release + if not self.url.endswith(".zip"): + return - if Path(self.application_path).exists(): - break + logging.info("Extracting nightly update") + if Path(self.pkg_download_path).exists(): + subprocess.run(["/bin/rm", "-rf", str(self.pkg_download_path)]) - if i == 1: - logging.error("Failed to extract update. Error: Update file does not exist") - wx.CallAfter(self.progress_bar_animation.stop_pulse) - wx.CallAfter(self.progress_bar.SetValue, 0) - wx.CallAfter(wx.MessageBox, "Failed to extract update. Error: Update file does not exist", "Critical Error!", wx.OK | wx.ICON_ERROR) - wx.CallAfter(sys.exit, 1) - break + result = subprocess.run( + ["/usr/bin/ditto", "-xk", str(self.constants.payload_path / "OpenCore-Patcher.pkg.zip"), str(self.constants.payload_path)], capture_output=True + ) + if result.returncode != 0: + logging.error(f"Failed to extract update.") + subprocess_wrapper.log(result) + wx.CallAfter(self.progress_bar_animation.stop_pulse) + wx.CallAfter(self.progress_bar.SetValue, 0) + wx.CallAfter(wx.MessageBox, f"Failed to extract update. Error: {result.stderr.decode('utf-8')}", "Critical Error!", wx.OK | wx.ICON_ERROR) + wx.CallAfter(sys.exit, 1) def _install_update(self) -> None: """ - Installs update to '/Library/Application Support/Dortania/OpenCore-Patcher.app' + Install PKG """ - logging.info(f"Installing update: {self.application_path}") - - # Create bash script to run as root - script = f"""#!/bin/bash -# Check if '/Library/Application Support/Dortania' exists -if [ ! -d "/Library/Application Support/Dortania" ]; then - mkdir -p "/Library/Application Support/Dortania" -fi - -# Check if 'OpenCore-Patcher.app' exists -if [ -d "/Library/Application Support/Dortania/OpenCore-Patcher.app" ]; then - rm -rf "/Library/Application Support/Dortania/OpenCore-Patcher.app" -fi - -if [ -d "/Applications/OpenCore-Patcher.app" ]; then - rm -rf "/Applications/OpenCore-Patcher.app" -fi - -# Move '/tmp/OpenCore-Patcher.app' to '/Library/Application Support/Dortania' -mv "{str(self.application_path)}" "/Library/Application Support/Dortania/OpenCore-Patcher.app" - -# Check if '/Applications/OpenCore-Patcher.app' exists -ln -s "/Library/Application Support/Dortania/OpenCore-Patcher.app" "/Applications/OpenCore-Patcher.app" - -# Create update.plist with info about update -cat << EOF > "/Library/Application Support/Dortania/update.plist" - - - - CFBundleShortVersionString - {self.version_label} - CFBundleVersion - {self.version_label} - InstallationDate - {datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%SZ")} - InstallationSource - {self.url} - - -EOF -""" - # Write script to file - with open(self.constants.payload_path / "update.sh", "w") as f: - f.write(script) - - # Execute script - args = [self.constants.oclp_helper_path, "/bin/sh", str(self.constants.payload_path / "update.sh")] - result = subprocess.run(args, capture_output=True) + logging.info(f"Installing update: {self.pkg_download_path}") + result = subprocess_wrapper.run_as_root(["/usr/sbin/installer", "-pkg", str(self.pkg_download_path), "-target", "/"], capture_output=True) if result.returncode != 0: wx.CallAfter(self.progress_bar_animation.stop_pulse) wx.CallAfter(self.progress_bar.SetValue, 0) @@ -282,7 +230,12 @@ def _install_update(self) -> None: else: logging.critical("Failed to install update.") subprocess_wrapper.log(result) - wx.CallAfter(wx.MessageBox, f"Failed to install update. Error: {result.stderr.decode('utf-8')}", "Critical Error!", wx.OK | wx.ICON_ERROR) + + # If it fails, fall back to opening the PKG + logging.error("Failed to install update, attempting to open PKG") + subprocess.run(["/usr/bin/open", str(self.pkg_download_path)]) + + wx.CallAfter(wx.MessageBox, f"Failed to install update. Please try installing the OpenCore-Patcher.pkg manually or download from GitHub", "Critical Error!", wx.OK | wx.ICON_ERROR) wx.CallAfter(sys.exit, 1) @@ -291,4 +244,4 @@ def _launch_update(self) -> None: Launches newly installed update """ logging.info("Launching update: '/Library/Application Support/Dortania/OpenCore-Patcher.app'") - subprocess.Popen(["/Library/Application Support/Dortania/OpenCore-Patcher.app/Contents/MacOS/OpenCore-Patcher", "--update_installed"]) + subprocess.run(["/usr/bin/open", "/Library/Application Support/Dortania/OpenCore-Patcher.app", "--args", "--update_installed"])