From 34f3d23aa031eb39c13b862dd49eaddfe9d147bf Mon Sep 17 00:00:00 2001 From: shadeyg56 <31134255+shadeyg56@users.noreply.github.com> Date: Fri, 27 Oct 2023 04:38:42 -0500 Subject: [PATCH] Feature: Allow GUI to detect available update and install it (#587) * rename verify_update and give return value * change verify_update call * GUI: add update feature --- auto_cpufreq/bin/auto_cpufreq.py | 4 +++- auto_cpufreq/bin/auto_cpufreq_gtk.py | 3 ++- auto_cpufreq/core.py | 8 +++++-- auto_cpufreq/gui/app.py | 35 ++++++++++++++++++++++++++-- auto_cpufreq/gui/objects.py | 17 ++++++++++++++ 5 files changed, 61 insertions(+), 6 deletions(-) diff --git a/auto_cpufreq/bin/auto_cpufreq.py b/auto_cpufreq/bin/auto_cpufreq.py index f3d9fc3..db2b84d 100755 --- a/auto_cpufreq/bin/auto_cpufreq.py +++ b/auto_cpufreq/bin/auto_cpufreq.py @@ -248,7 +248,9 @@ def main(config, daemon, debug, update, install, remove, live, log, monitor, sta elif subprocess.run(["bash", "-c", "command -v pacman >/dev/null 2>&1"]).returncode == 0 and subprocess.run(["bash", "-c", "pacman -Q auto-cpufreq >/dev/null 2>&1"]).returncode == 0: print("Arch-based distribution with AUR support detected. Please refresh auto-cpufreq using your AUR helper.") else: - verify_update() + is_new_update = check_for_update() + if not is_new_update: + return ans = input("Do you want to update auto-cpufreq to the latest release? [Y/n]: ").strip().lower() if not os.path.exists(custom_dir): os.makedirs(custom_dir) diff --git a/auto_cpufreq/bin/auto_cpufreq_gtk.py b/auto_cpufreq/bin/auto_cpufreq_gtk.py index ea7a956..8db1dd3 100644 --- a/auto_cpufreq/bin/auto_cpufreq_gtk.py +++ b/auto_cpufreq/bin/auto_cpufreq_gtk.py @@ -11,10 +11,11 @@ from gi.repository import Gtk, GLib from auto_cpufreq.gui.app import ToolWindow def main(): + GLib.set_prgname("auto-cpufreq") win = ToolWindow() win.connect("destroy", Gtk.main_quit) win.show_all() - GLib.set_prgname("auto-cpufreq") + win.handle_update() Gtk.main() if __name__ == "__main__": diff --git a/auto_cpufreq/core.py b/auto_cpufreq/core.py index f0602fd..f9cbf01 100755 --- a/auto_cpufreq/core.py +++ b/auto_cpufreq/core.py @@ -165,7 +165,10 @@ def app_version(): except Exception as e: print(repr(e)) pass -def verify_update(): + +def check_for_update(): + # returns True if a new release is available from the GitHub repo + # Specify the repository and package name # IT IS IMPORTANT TO THAT IF THE REPOSITORY STRUCTURE IS CHANGED, THE FOLLOWING FUNCTION NEEDS TO BE UPDATED ACCORDINGLY # Fetch the latest release information from GitHub API @@ -192,10 +195,11 @@ def verify_update(): # Compare the latest version with the installed version and perform update if necessary if latest_version == installed_version: print("auto-cpufreq is up to date") - exit(0) + return False else: print(f"Updates are available,\nCurrent version: {installed_version}\nLatest version: {latest_version}") print("Note that your previous custom settings might be erased with the following update") + return True def new_update(custom_dir): os.chdir(custom_dir) diff --git a/auto_cpufreq/gui/app.py b/auto_cpufreq/gui/app.py index d1e60a3..6e91c20 100644 --- a/auto_cpufreq/gui/app.py +++ b/auto_cpufreq/gui/app.py @@ -6,11 +6,15 @@ from gi.repository import Gtk, GLib, Gdk, Gio, GdkPixbuf import os import sys +from contextlib import redirect_stdout +from io import StringIO +from subprocess import run, PIPE +import shutil from threading import Thread sys.path.append("../") -from auto_cpufreq.core import is_running -from auto_cpufreq.gui.objects import RadioButtonView, SystemStatsLabel, CPUFreqStatsLabel, CurrentGovernorBox, DropDownMenu, DaemonNotRunningView +from auto_cpufreq.core import is_running, check_for_update, remove_daemon, new_update +from auto_cpufreq.gui.objects import RadioButtonView, SystemStatsLabel, CPUFreqStatsLabel, CurrentGovernorBox, DropDownMenu, DaemonNotRunningView, UpdateDialog if os.getenv("PKG_MARKER") == "SNAP": ICON_FILE = "/snap/auto-cpufreq/current/icon.png" @@ -20,6 +24,8 @@ else: CSS_FILE = "/usr/local/share/auto-cpufreq/scripts/style.css" HBOX_PADDING = 20 +PKEXEC_ERROR = "Error executing command as another user: Not authorized\n\nThis incident has been reported.\n" + class ToolWindow(Gtk.Window): def __init__(self): @@ -72,6 +78,31 @@ class ToolWindow(Gtk.Window): box.pack_start(button, False, False, 0) self.add(box) + def handle_update(self): + new_stdout = StringIO() + with redirect_stdout(new_stdout): + is_new_update = check_for_update() + if not is_new_update: + return + captured_output = new_stdout.getvalue().splitlines() + dialog = UpdateDialog(self, captured_output[1], captured_output[2]) + response = dialog.run() + dialog.destroy() + if response != Gtk.ResponseType.YES: + return + updater = run(["pkexec", "auto-cpufreq", "--update"], input="y\n", encoding="utf-8", stderr=PIPE) + if updater.stderr == PKEXEC_ERROR: + error = Gtk.MessageDialog(self, 0, Gtk.MessageType.ERROR, Gtk.ButtonsType.OK, "Error updating") + error.format_secondary_text("Authorization Failed") + error.run() + error.destroy() + return + success = Gtk.MessageDialog(self, 0, Gtk.MessageType.INFO, Gtk.ButtonsType.OK, "Update successful") + success.format_secondary_text("The app will now close. Please reopen to apply changes") + success.run() + success.destroy() + exit(0) + def daemon_not_running(self): self.box = DaemonNotRunningView(self) self.add(self.box) diff --git a/auto_cpufreq/gui/objects.py b/auto_cpufreq/gui/objects.py index 5ecb5d7..39f060d 100644 --- a/auto_cpufreq/gui/objects.py +++ b/auto_cpufreq/gui/objects.py @@ -242,6 +242,23 @@ class AboutDialog(Gtk.Dialog): self.box.pack_start(self.love, False, False, 0) self.show_all() +class UpdateDialog(Gtk.Dialog): + def __init__(self, parent, current_version: str, latest_version: str): + super().__init__(title="Update Available", transient_for=parent) + self.box = self.get_content_area() + self.set_default_size(400, 100) + self.add_buttons("Update", Gtk.ResponseType.YES, "Cancel", Gtk.ResponseType.NO) + self.label = Gtk.Label(label="An update is available\n") + self.current_version = Gtk.Label(label=current_version + "\n") + self.latest_version = Gtk.Label(label=latest_version + "\n") + + self.box.pack_start(self.label, True, False, 0) + self.box.pack_start(self.current_version, True, False, 0) + self.box.pack_start(self.latest_version, True, False, 0) + + self.show_all() + + class ConfirmDialog(Gtk.Dialog): def __init__(self, parent, message: str): super().__init__(title="Confirmation", transient_for=parent)