From 5929e81337c37317414ce9aecb1f9c0a248ad8de Mon Sep 17 00:00:00 2001 From: Mykola Grymalyuk Date: Sun, 14 May 2023 20:46:52 -0600 Subject: [PATCH] GUI: Implement update.plist creation on update --- .gitignore | 1 + resources/network_handler.py | 2 - resources/utilities.py | 2 +- resources/wx_gui/gui_download.py | 3 +- resources/wx_gui/gui_main_menu.py | 2 +- resources/wx_gui/gui_settings.py | 29 +++++++++--- resources/wx_gui/gui_update.py | 75 ++++++++++++++++++++++--------- 7 files changed, 82 insertions(+), 32 deletions(-) diff --git a/.gitignore b/.gitignore index fd62b2859..6d70a38d5 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,4 @@ __pycache__/ /Universal-Binaries.dmg /payloads/KDKInfo.plist /payloads/update.sh +/payloads/OpenCore-Patcher.app diff --git a/resources/network_handler.py b/resources/network_handler.py index d236a24b8..5efd0be14 100644 --- a/resources/network_handler.py +++ b/resources/network_handler.py @@ -382,7 +382,6 @@ class DownloadObject: """ if self.total_file_size == 0.0: - logging.error("- File size is 0, cannot calculate percent") return -1 return self.downloaded_file_size / self.total_file_size * 100 @@ -407,7 +406,6 @@ class DownloadObject: """ if self.total_file_size == 0.0: - logging.error("- File size is 0, cannot calculate time remaining") return -1 speed = self.get_speed() if speed <= 0: diff --git a/resources/utilities.py b/resources/utilities.py index e1f25a17d..186be82b1 100644 --- a/resources/utilities.py +++ b/resources/utilities.py @@ -62,7 +62,7 @@ def seconds_to_readable_time(seconds) -> str: time = "" if seconds == 0: - return "Done" + return "Almost done" if seconds < 0: return "Indeterminate" diff --git a/resources/wx_gui/gui_download.py b/resources/wx_gui/gui_download.py index c957ea1ae..7ba4da5ed 100644 --- a/resources/wx_gui/gui_download.py +++ b/resources/wx_gui/gui_download.py @@ -56,7 +56,7 @@ class DownloadFrame(wx.Frame): self.download_obj.download() while self.download_obj.is_active(): - if self.download_obj.total_file_size == -1: + if self.download_obj.get_percent() == -1: amount_str = f"{utilities.human_fmt(self.download_obj.downloaded_file_size)} downloaded" else: amount_str = f"{utilities.human_fmt(self.download_obj.downloaded_file_size)} downloaded of {utilities.human_fmt(self.download_obj.total_file_size)} ({self.download_obj.get_percent():.2f}%)" @@ -71,7 +71,6 @@ class DownloadFrame(wx.Frame): f"Estimated time remaining: {utilities.seconds_to_readable_time(self.download_obj.get_time_remaining())}" ) - progress_bar.SetValue(int(self.download_obj.get_percent())) wx.GetApp().Yield() diff --git a/resources/wx_gui/gui_main_menu.py b/resources/wx_gui/gui_main_menu.py index 3177092b9..4445087d8 100644 --- a/resources/wx_gui/gui_main_menu.py +++ b/resources/wx_gui/gui_main_menu.py @@ -203,5 +203,5 @@ class MainMenu(wx.Frame): global_constants=self.constants, screen_location=self.GetPosition(), url=oclp_url, - item=oclp_version + version_label=oclp_version ) \ No newline at end of file diff --git a/resources/wx_gui/gui_settings.py b/resources/wx_gui/gui_settings.py index c2ecaade7..49b169b35 100644 --- a/resources/wx_gui/gui_settings.py +++ b/resources/wx_gui/gui_settings.py @@ -4,8 +4,9 @@ import logging import py_sip_xnu import pprint import subprocess +from pathlib import Path -from resources.wx_gui import gui_support +from resources.wx_gui import gui_support, gui_update from resources import constants, global_settings, defaults, generate_smbios from data import model_array, sip_data, smbios_data, os_data @@ -52,9 +53,15 @@ class SettingsFrame(wx.Frame): model_choice.SetSelection(model_choice.FindString(selection)) sizer.Add(model_choice, 0, wx.ALIGN_CENTER | wx.ALL, 5) - model_description = wx.StaticText(frame, label="Overrides Mac Model Patcher will build for.", pos=(-1, -1)) - model_description.SetFont(wx.Font(11, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, ".AppleSystemUIFont")) - sizer.Add(model_description, 0, wx.ALIGN_CENTER | wx.ALL, 5) + if Path("~/.dortania_developer").expanduser().exists(): + developer_mode_button = wx.Button(frame, label="Install latest nightly build 🧪", pos=(-1, -1), size=(200, 30)) + developer_mode_button.Bind(wx.EVT_BUTTON, self.on_dev_mode) + developer_mode_button.SetFont(wx.Font(13, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, ".AppleSystemUIFont")) + sizer.Add(developer_mode_button, 0, wx.ALIGN_CENTER | wx.ALL, 0) + else: + model_description = wx.StaticText(frame, label="Overrides Mac Model Patcher will build for.", pos=(-1, -1)) + model_description.SetFont(wx.Font(11, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, ".AppleSystemUIFont")) + sizer.Add(model_description, 0, wx.ALIGN_CENTER | wx.ALL, 5) tabs = list(self.settings.keys()) for tab in tabs: @@ -1133,7 +1140,6 @@ Hardware Information: self.constants.metal_build = False - def _get_system_settings(self, variable) -> bool: result = subprocess.run(["defaults", "read", "-g", variable], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) if result.returncode == 0: @@ -1145,4 +1151,15 @@ Hardware Information: def on_return(self, event): - self.frame_modal.Destroy() \ No newline at end of file + self.frame_modal.Destroy() + + + def on_dev_mode(self, event: wx.Event) -> None: + gui_update.UpdateFrame( + parent=self.parent, + title=self.title, + global_constants=self.constants, + screen_location=self.parent.GetPosition(), + url="https://nightly.link/dortania/OpenCore-Legacy-Patcher/workflows/build-app-wxpython/main/OpenCore-Patcher.app%20%28GUI%29.zip", + version_label="(Nightly)" + ) \ No newline at end of file diff --git a/resources/wx_gui/gui_update.py b/resources/wx_gui/gui_update.py index aba1b678d..91a1b3648 100644 --- a/resources/wx_gui/gui_update.py +++ b/resources/wx_gui/gui_update.py @@ -4,6 +4,7 @@ import subprocess import threading import logging import time +import datetime from pathlib import Path from resources.wx_gui import gui_download @@ -13,7 +14,7 @@ from resources import constants, network_handler, updates class UpdateFrame(wx.Frame): - def __init__(self, parent: wx.Frame, title: str, global_constants: constants.Constants, screen_location: wx.Point, url: str = "", item: str = "") -> None: + def __init__(self, parent: wx.Frame, title: str, global_constants: constants.Constants, screen_location: wx.Point, url: str = "", version_label: str = "") -> None: if parent: self.parent: wx.Frame = parent @@ -35,14 +36,17 @@ class UpdateFrame(wx.Frame): self.screen_location = self.GetScreenPosition() - if url == "" or item == "": + if url == "" or version_label == "": dict = updates.CheckBinaryUpdates(self.constants).check_binary_updates() if dict: for key in dict: - item = dict[key]["Version"] + version_label = dict[key]["Version"] url = dict[key]["Link"] break + self.version_label = version_label + self.url = url + self.frame: wx.Frame = wx.Frame( parent=parent if parent else self, title=self.title, @@ -51,7 +55,6 @@ class UpdateFrame(wx.Frame): style=wx.DEFAULT_FRAME_STYLE ^ wx.RESIZE_BORDER ^ wx.MAXIMIZE_BOX ) - # Title: Preparing update title_label = wx.StaticText(self.frame, label="Preparing download...", pos=(-1,1)) title_label.SetFont(wx.Font(19, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, False, ".AppleSystemUIFont")) @@ -73,7 +76,7 @@ class UpdateFrame(wx.Frame): title=self.title, global_constants=self.constants, download_obj=download_obj, - item_name=f"OpenCore Patcher {item}" + item_name=f"OpenCore Patcher {version_label}" ) if download_obj.download_complete is False: @@ -110,7 +113,7 @@ class UpdateFrame(wx.Frame): progress_bar.Hide() # Label: 0.6.6 has been installed to: - installed_label = wx.StaticText(self.frame, label=f"{item} has been installed:", pos=(-1, progress_bar.GetPosition().y - 15)) + installed_label = wx.StaticText(self.frame, label=f"{version_label} has been installed:", pos=(-1, progress_bar.GetPosition().y - 15)) installed_label.SetFont(wx.Font(13, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, False, ".AppleSystemUIFont")) installed_label.Center(wx.HORIZONTAL) @@ -120,12 +123,12 @@ class UpdateFrame(wx.Frame): installed_path_label.Center(wx.HORIZONTAL) # Label: Launching update shortly... - launch_label = wx.StaticText(self.frame, label="Launching update shortly...", pos=(-1, installed_path_label.GetPosition().y + 20)) + launch_label = wx.StaticText(self.frame, label="Launching update shortly...", pos=(-1, installed_path_label.GetPosition().y + 30)) launch_label.SetFont(wx.Font(13, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, ".AppleSystemUIFont")) launch_label.Center(wx.HORIZONTAL) # Adjust frame size - self.frame.SetSize((-1, launch_label.GetPosition().y + 80)) + self.frame.SetSize((-1, launch_label.GetPosition().y + 60)) thread = threading.Thread(target=self._launch_update) thread.start() @@ -133,11 +136,11 @@ class UpdateFrame(wx.Frame): while thread.is_alive(): wx.Yield() - timer = 3 + timer = 5 while True: - wx.GetApp().Yield() launch_label.SetLabel(f"Closing old process in {timer} seconds") launch_label.Center(wx.HORIZONTAL) + wx.GetApp().Yield() time.sleep(1) timer -= 1 if timer == 0: @@ -152,18 +155,31 @@ class UpdateFrame(wx.Frame): logging.info("Extracting update") if Path(self.application_path).exists(): subprocess.run(["rm", "-rf", str(self.application_path)]) - result = subprocess.run( - ["ditto", "-xk", str(self.constants.payload_path / "OpenCore-Patcher-GUI.app.zip"), str(self.constants.payload_path)], capture_output=True - ) - if result.returncode != 0: - 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) + + + # Some hell spawn at Github decided to double zip our Github Actions artifacts + # So we need to unzip it twice + loop = 0 + while True: + result = subprocess.run( + ["ditto", "-xk", str(self.constants.payload_path / "OpenCore-Patcher-GUI.app.zip"), str(self.constants.payload_path)], capture_output=True + ) + if result.returncode != 0: + 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) + if Path(self.application_path).exists(): + break + loop += 1 + if loop == 2: + 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) def _install_update(self): # Install update - logging.info("Installing update") + logging.info(f"Installing update: {self.application_path}") # Create bash script to run as root script = f"""#!/bin/bash @@ -184,6 +200,23 @@ mv "{str(self.application_path)}" "/Library/Application Support/Dortania/OpenCor if [ ! -d "/Applications/OpenCore-Patcher.app" ]; then ln -s "/Library/Application Support/Dortania/OpenCore-Patcher.app" "/Applications/OpenCore-Patcher.app" fi + +# 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: @@ -191,11 +224,13 @@ fi # Execute script args = [self.constants.oclp_helper_path, "/bin/sh", str(self.constants.payload_path / "update.sh")] - logging.info(f"Executing: {args}") result = subprocess.run(args, capture_output=True) if result.returncode != 0: wx.CallAfter(self.progress_bar.SetValue, 0) - wx.CallAfter(wx.MessageBox, f"Failed to install update. Error: {result.stderr.decode('utf-8')}", "Critical Error!", wx.OK | wx.ICON_ERROR) + if "User cancelled" in result.stderr.decode("utf-8"): + wx.CallAfter(wx.MessageBox, "User cancelled update", "Update Cancelled", wx.OK | wx.ICON_INFORMATION) + else: + wx.CallAfter(wx.MessageBox, f"Failed to install update. Error: {result.stderr.decode('utf-8')}", "Critical Error!", wx.OK | wx.ICON_ERROR) wx.CallAfter(sys.exit, 1)