update
This commit is contained in:
@@ -0,0 +1,571 @@
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import socket
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
import gi
|
||||
|
||||
# Insertion for embedded VTE terminal
|
||||
gi.require_version("Gtk", "3.0")
|
||||
gi.require_version("Gdk", "3.0")
|
||||
gi.require_version("Vte", "2.91")
|
||||
from gi.repository import Gdk, GLib, Gtk, Vte
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
||||
from fabric.utils.helpers import get_relative_path
|
||||
|
||||
import config.data as data
|
||||
|
||||
# File locations
|
||||
VERSION_FILE = get_relative_path("../version.json")
|
||||
REMOTE_VERSION_FILE = "/tmp/remote_version.json"
|
||||
REMOTE_URL = "https://raw.githubusercontent.com/Axenide/Ax-Shell/refs/heads/main/version.json"
|
||||
REPO_DIR = get_relative_path("../")
|
||||
|
||||
SNOOZE_FILE_NAME = "updater_snooze.txt"
|
||||
UPDATER_DISABLE_FILE_NAME = "updater_disabled.flag"
|
||||
SNOOZE_DURATION_SECONDS = 8 * 60 * 60 # 8 hours
|
||||
|
||||
# --- Global state for standalone execution control ---
|
||||
_QUIT_GTK_IF_NO_WINDOW_STANDALONE = False
|
||||
|
||||
def get_cache_dir():
|
||||
"""Returns the cache directory path, creating it if necessary."""
|
||||
cache_dir_base = data.CACHE_DIR or os.path.expanduser(f"~/.cache/{data.APP_NAME}")
|
||||
try:
|
||||
os.makedirs(cache_dir_base, exist_ok=True)
|
||||
except Exception as e:
|
||||
print(f"Error creating cache directory {cache_dir_base}: {e}")
|
||||
return cache_dir_base
|
||||
|
||||
def get_snooze_file_path():
|
||||
"""
|
||||
Returns the path to the 'snooze' file inside ~/.cache/APP_NAME.
|
||||
"""
|
||||
return os.path.join(get_cache_dir(), SNOOZE_FILE_NAME)
|
||||
|
||||
def get_disable_file_path():
|
||||
"""
|
||||
Returns the path to the 'updater_disabled.flag' file inside ~/.cache/APP_NAME.
|
||||
"""
|
||||
return os.path.join(get_cache_dir(), UPDATER_DISABLE_FILE_NAME)
|
||||
|
||||
|
||||
def fetch_remote_version():
|
||||
"""
|
||||
Downloads the remote version JSON using curl, with timeout and error handling.
|
||||
"""
|
||||
try:
|
||||
subprocess.run(
|
||||
["curl", "-sL", "--connect-timeout", "10", REMOTE_URL, "-o", REMOTE_VERSION_FILE],
|
||||
check=False,
|
||||
timeout=15
|
||||
)
|
||||
except subprocess.TimeoutExpired:
|
||||
print("Error: curl timed out while fetching the remote version.")
|
||||
except FileNotFoundError:
|
||||
print("Error: curl not found. Please install curl.")
|
||||
except Exception as e:
|
||||
print(f"Error fetching remote version: {e}")
|
||||
|
||||
|
||||
def get_local_version():
|
||||
"""
|
||||
Reads the local version file and returns (version, changelog).
|
||||
"""
|
||||
if os.path.exists(VERSION_FILE):
|
||||
try:
|
||||
with open(VERSION_FILE, "r") as f:
|
||||
data_content = json.load(f)
|
||||
return data_content.get("version", "0.0.0"), data_content.get("changelog", [])
|
||||
except json.JSONDecodeError:
|
||||
print(f"Error: Invalid JSON in local file: {VERSION_FILE}")
|
||||
return "0.0.0", []
|
||||
except Exception as e:
|
||||
print(f"Error reading local version file {VERSION_FILE}: {e}")
|
||||
return "0.0.0", []
|
||||
return "0.0.0", []
|
||||
|
||||
|
||||
def get_remote_version():
|
||||
"""
|
||||
Reads the downloaded remote file and returns (version, changelog, download_url, pkg_update).
|
||||
"""
|
||||
if os.path.exists(REMOTE_VERSION_FILE):
|
||||
try:
|
||||
with open(REMOTE_VERSION_FILE, "r") as f:
|
||||
data_content = json.load(f)
|
||||
return (
|
||||
data_content.get("version", "0.0.0"),
|
||||
data_content.get("changelog", []),
|
||||
data_content.get("download_url", "#"),
|
||||
data_content.get("pkg_update", True), # Default to True if missing
|
||||
)
|
||||
except json.JSONDecodeError:
|
||||
print(f"Error: Invalid JSON in remote file: {REMOTE_VERSION_FILE}")
|
||||
return "0.0.0", [], "#", True
|
||||
except Exception as e:
|
||||
print(f"Error reading remote version file {REMOTE_VERSION_FILE}: {e}")
|
||||
return "0.0.0", [], "#", True
|
||||
return "0.0.0", [], "#", True
|
||||
|
||||
|
||||
def update_local_version_file():
|
||||
"""
|
||||
Replaces the local version with the remote one by moving the downloaded JSON to the local version file.
|
||||
"""
|
||||
if os.path.exists(REMOTE_VERSION_FILE):
|
||||
try:
|
||||
shutil.move(REMOTE_VERSION_FILE, VERSION_FILE)
|
||||
except Exception as e:
|
||||
print(f"Error updating local version file: {e}")
|
||||
raise
|
||||
|
||||
|
||||
def is_connected():
|
||||
"""
|
||||
Checks basic connectivity by attempting to connect to www.google.com:80.
|
||||
"""
|
||||
try:
|
||||
socket.create_connection(("www.google.com", 80), timeout=5)
|
||||
return True
|
||||
except OSError:
|
||||
return False
|
||||
|
||||
|
||||
class UpdateWindow(Gtk.Window):
|
||||
def __init__(self, latest_version, changelog, pkg_update, is_standalone_mode=False):
|
||||
super().__init__(name="update-window", title=f"{data.APP_NAME_CAP} Updater")
|
||||
self.set_default_size(500, 480)
|
||||
self.set_border_width(16)
|
||||
self.set_resizable(False)
|
||||
self.set_position(Gtk.WindowPosition.CENTER)
|
||||
self.set_keep_above(True)
|
||||
self.set_type_hint(Gdk.WindowTypeHint.DIALOG)
|
||||
|
||||
self.is_standalone_mode = is_standalone_mode
|
||||
self.quit_gtk_main_on_destroy = False
|
||||
self.pkg_update = pkg_update # Store pkg_update
|
||||
|
||||
# Main vertical container
|
||||
self.main_vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=15)
|
||||
self.add(self.main_vbox)
|
||||
|
||||
# Title
|
||||
title_label = Gtk.Label(name="update-title")
|
||||
title_label.set_markup("<span size='xx-large' weight='bold'>📦 Update Available ✨</span>")
|
||||
title_label.get_style_context().add_class("title-1")
|
||||
self.main_vbox.pack_start(title_label, False, False, 10)
|
||||
|
||||
# Version info text
|
||||
info_label = Gtk.Label(
|
||||
label=f"A new version ({latest_version}) of {data.APP_NAME_CAP} is available."
|
||||
)
|
||||
info_label.set_xalign(0)
|
||||
info_label.set_line_wrap(True)
|
||||
self.main_vbox.pack_start(info_label, False, False, 0)
|
||||
|
||||
# Changelog header
|
||||
changelog_header_label = Gtk.Label()
|
||||
changelog_header_label.set_markup("<b>Changelog:</b>")
|
||||
changelog_header_label.set_xalign(0)
|
||||
self.main_vbox.pack_start(changelog_header_label, False, False, 5)
|
||||
|
||||
# — Scrollable window for the changelog (using Gtk.Label with markup) —
|
||||
scrolled_window = Gtk.ScrolledWindow()
|
||||
scrolled_window.set_hexpand(True)
|
||||
scrolled_window.set_vexpand(True)
|
||||
scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
|
||||
|
||||
if changelog:
|
||||
# Each entry may already contain Pango tags (<b>, <i>, etc.)
|
||||
joined = "\n".join(f"• {change}" for change in changelog)
|
||||
else:
|
||||
joined = "No specific changes listed for this version."
|
||||
|
||||
self.changelog_label = Gtk.Label()
|
||||
self.changelog_label.set_xalign(0)
|
||||
self.changelog_label.set_yalign(0)
|
||||
self.changelog_label.set_line_wrap(Gtk.WrapMode.WORD_CHAR) # Gtk.WrapMode instead of just True
|
||||
self.changelog_label.set_selectable(False)
|
||||
self.changelog_label.set_markup(joined)
|
||||
|
||||
scrolled_window.add(self.changelog_label)
|
||||
self.main_vbox.pack_start(scrolled_window, True, True, 0)
|
||||
|
||||
# ProgressBar (will be shown if we need to indicate status, although with VTE it remains unused)
|
||||
self.progress_bar = Gtk.ProgressBar()
|
||||
self.progress_bar.set_no_show_all(True)
|
||||
self.progress_bar.set_visible(False)
|
||||
self.main_vbox.pack_start(self.progress_bar, False, False, 5)
|
||||
|
||||
# Button container
|
||||
action_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
|
||||
self.main_vbox.pack_start(action_box, False, False, 10)
|
||||
|
||||
# "Disable/Enable Updater" Button (aligned left)
|
||||
self.toggle_updater_button = Gtk.Button(name="toggle-updater-button")
|
||||
self.toggle_updater_button.connect("clicked", self.on_toggle_updater_clicked)
|
||||
self._update_toggle_updater_button_label() # Set initial label
|
||||
action_box.pack_start(self.toggle_updater_button, False, False, 0)
|
||||
|
||||
# Box for right-aligned buttons
|
||||
right_aligned_buttons_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
|
||||
right_aligned_buttons_box.set_halign(Gtk.Align.END)
|
||||
action_box.pack_end(right_aligned_buttons_box, True, True, 0) # This box expands
|
||||
|
||||
# Update button (will now show embedded VTE terminal)
|
||||
self.update_button = Gtk.Button(name="update-button", label="Update")
|
||||
self.update_button.get_style_context().add_class("suggested-action")
|
||||
self.update_button.connect("clicked", self.on_update_clicked)
|
||||
right_aligned_buttons_box.pack_end(self.update_button, False, False, 0)
|
||||
|
||||
# 'Later' button
|
||||
self.close_button = Gtk.Button(name="later-button", label="Later")
|
||||
self.close_button.connect("clicked", self.on_later_clicked)
|
||||
right_aligned_buttons_box.pack_end(self.close_button, False, False, 0)
|
||||
|
||||
self.connect("destroy", self.on_window_destroyed)
|
||||
|
||||
# Placeholder for embedded terminal
|
||||
self.terminal_container = None
|
||||
self.vte_terminal = None
|
||||
|
||||
def _update_toggle_updater_button_label(self):
|
||||
disable_file = get_disable_file_path()
|
||||
if os.path.exists(disable_file):
|
||||
self.toggle_updater_button.set_label("Enable Updater")
|
||||
else:
|
||||
self.toggle_updater_button.set_label("Disable Updater")
|
||||
|
||||
def on_toggle_updater_clicked(self, _widget):
|
||||
disable_file = get_disable_file_path()
|
||||
try:
|
||||
if os.path.exists(disable_file):
|
||||
os.remove(disable_file)
|
||||
print("Updater enabled.")
|
||||
else:
|
||||
with open(disable_file, "w") as f:
|
||||
pass # File content doesn't matter, its existence is the flag
|
||||
print("Updater disabled.")
|
||||
self._update_toggle_updater_button_label()
|
||||
except Exception as e:
|
||||
print(f"Error toggling updater state: {e}")
|
||||
error_dialog = Gtk.MessageDialog(
|
||||
transient_for=self,
|
||||
flags=0,
|
||||
message_type=Gtk.MessageType.ERROR,
|
||||
buttons=Gtk.ButtonsType.OK,
|
||||
text="Error Changing Updater Setting",
|
||||
)
|
||||
error_dialog.format_secondary_text(f"Could not change the updater setting: {e}")
|
||||
error_dialog.run()
|
||||
error_dialog.destroy()
|
||||
|
||||
def on_later_clicked(self, _widget):
|
||||
"""
|
||||
When 'Later' is clicked, create/update the snooze file and close the window.
|
||||
"""
|
||||
snooze_file_path = get_snooze_file_path()
|
||||
try:
|
||||
with open(snooze_file_path, "w") as f:
|
||||
f.write(str(time.time()))
|
||||
print(f"Update snoozed. Snooze file at: {snooze_file_path}")
|
||||
except Exception as e:
|
||||
print(f"Error creating snooze file {snooze_file_path}: {e}")
|
||||
self.destroy()
|
||||
|
||||
def on_update_clicked(self, _widget):
|
||||
"""
|
||||
When 'Update' is pressed, disable buttons, hide the progress bar,
|
||||
and create a VTE terminal to run the update command.
|
||||
"""
|
||||
# Disable the buttons so they can't be clicked again
|
||||
self.update_button.set_sensitive(False)
|
||||
self.close_button.set_sensitive(False)
|
||||
self.toggle_updater_button.set_sensitive(False) # Disable toggle button during update
|
||||
|
||||
# Hide the progress bar (we don't need it now)
|
||||
self.progress_bar.set_visible(False)
|
||||
|
||||
# If there's no container for the terminal, create it
|
||||
if self.terminal_container is None:
|
||||
# Scrollable container so the terminal can scroll
|
||||
self.terminal_container = Gtk.ScrolledWindow()
|
||||
self.terminal_container.set_hexpand(True)
|
||||
self.terminal_container.set_vexpand(True)
|
||||
self.terminal_container.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
|
||||
|
||||
# Create the VTE terminal
|
||||
self.vte_terminal = Vte.Terminal()
|
||||
self.vte_terminal.set_size(120, 48)
|
||||
# Make update window larger
|
||||
self.set_default_size(720, 540)
|
||||
self.terminal_container.add(self.vte_terminal)
|
||||
# Insert the terminal at the end of main_vbox
|
||||
self.main_vbox.pack_start(self.terminal_container, True, True, 0)
|
||||
|
||||
# Show everything
|
||||
self.show_all()
|
||||
|
||||
# Command to run in the terminal
|
||||
if self.pkg_update:
|
||||
update_command = "curl -fsSL https://raw.githubusercontent.com/Axenide/Ax-Shell/main/install.sh | bash"
|
||||
else:
|
||||
# Ensure REPO_DIR is correctly defined at the top of the file.
|
||||
update_command = f"git -C \"{REPO_DIR}\" pull && echo 'Reloading in 3...' && sleep 1 && echo '2...' && sleep 1 && echo '1...' && sleep 1 && killall {data.APP_NAME} && setsid python \"{REPO_DIR}main.py\""
|
||||
|
||||
|
||||
# Spawn the process asynchronously inside the terminal
|
||||
self.vte_terminal.spawn_async(
|
||||
Vte.PtyFlags.DEFAULT,
|
||||
os.environ.get("HOME", "/"), # CWD for the command
|
||||
["/bin/bash", "-lc", update_command], # Command and args
|
||||
[], # envv
|
||||
GLib.SpawnFlags.DO_NOT_REAP_CHILD, # spawn_flags
|
||||
None, # child_setup
|
||||
None, # child_setup_data
|
||||
-1, # timeout
|
||||
None, # cancellable
|
||||
None, # callback_data for Vte.Terminal.spawn_async_wait_finish
|
||||
self.on_curl_script_exit, # callback for when process finishes
|
||||
None # user_data for callback
|
||||
)
|
||||
|
||||
def on_curl_script_exit(self, terminal, exit_status, user_data):
|
||||
"""
|
||||
Callback when the script running in the VTE terminal finishes.
|
||||
Depending on exit_status, success or failure is considered.
|
||||
"""
|
||||
# exit_status is encoded: 0 means success
|
||||
if exit_status == 0:
|
||||
# Call the success routine, which restarts the app
|
||||
GLib.idle_add(self.handle_update_success)
|
||||
else:
|
||||
# If there was an error, read the last part of the buffer to display it
|
||||
end_iter = self.vte_terminal.get_end_iter()
|
||||
start_iter = self.vte_terminal.get_iter_at_line(max(0, self.vte_terminal.get_line_count() - 5))
|
||||
error_excerpt = self.vte_terminal.get_text_range(start_iter, end_iter, False)
|
||||
GLib.idle_add(self.handle_update_failure, f"Script exited with status {exit_status}. Last lines:\n{error_excerpt}")
|
||||
|
||||
def handle_update_success(self):
|
||||
"""
|
||||
Shows a success message, updates local version.json, and restarts the application.
|
||||
"""
|
||||
# Update the local version.json with the fetched remote one
|
||||
try:
|
||||
update_local_version_file()
|
||||
print("Local version.json updated successfully.")
|
||||
except Exception as e:
|
||||
print(f"Failed to update local version.json: {e}")
|
||||
# Optionally, you could show an error message to the user here
|
||||
# For now, we'll proceed with the restart if the script itself was successful.
|
||||
|
||||
# If there was any progress bar timeout, remove it
|
||||
if hasattr(self, "pulse_timeout_id"):
|
||||
GLib.source_remove(self.pulse_timeout_id)
|
||||
delattr(self, "pulse_timeout_id")
|
||||
|
||||
# Replace the terminal (or other widget) with a brief message
|
||||
# First remove the terminal to show the progress bar and text
|
||||
if self.terminal_container:
|
||||
self.main_vbox.remove(self.terminal_container)
|
||||
|
||||
# Prepare the progress bar to indicate success
|
||||
self.progress_bar.set_visible(True)
|
||||
self.progress_bar.set_fraction(1.0)
|
||||
self.progress_bar.set_text("Update Complete. Restarting application...")
|
||||
self.progress_bar.set_show_text(True)
|
||||
|
||||
# Force it to show
|
||||
self.show_all()
|
||||
|
||||
# After 2 seconds, close and restart
|
||||
GLib.timeout_add_seconds(2, self.trigger_restart_and_close)
|
||||
|
||||
def trigger_restart_and_close(self):
|
||||
"""
|
||||
Closes the window and relaunches the application.
|
||||
"""
|
||||
self.destroy()
|
||||
try:
|
||||
print("Restarting application...")
|
||||
# Relaunch the application
|
||||
os.execv(sys.executable, [sys.executable] + sys.argv)
|
||||
except Exception as e:
|
||||
print(f"Error during application restart: {e}")
|
||||
# Fallback or error message if execv fails
|
||||
# For instance, you might want to just quit GTK if restart fails in standalone mode.
|
||||
if self.is_standalone_mode and self.quit_gtk_main_on_destroy:
|
||||
Gtk.main_quit()
|
||||
return False # So the timeout runs only once
|
||||
|
||||
def handle_update_failure(self, error_message):
|
||||
"""
|
||||
Shows an error dialog if the script execution fails.
|
||||
"""
|
||||
# If there was any progress bar timeout, remove it
|
||||
if hasattr(self, "pulse_timeout_id"):
|
||||
GLib.source_remove(self.pulse_timeout_id)
|
||||
delattr(self, "pulse_timeout_id")
|
||||
|
||||
# Indicate failure in the progress bar
|
||||
self.progress_bar.set_visible(True)
|
||||
self.progress_bar.set_fraction(0.0)
|
||||
self.progress_bar.set_text("Update Failed.")
|
||||
self.progress_bar.set_show_text(True)
|
||||
|
||||
# Buttons are re-enabled to retry or close
|
||||
self.update_button.set_sensitive(True)
|
||||
self.close_button.set_sensitive(True)
|
||||
self.toggle_updater_button.set_sensitive(True) # Re-enable toggle button
|
||||
|
||||
# Error dialog
|
||||
error_dialog = Gtk.MessageDialog(
|
||||
transient_for=self,
|
||||
flags=0,
|
||||
message_type=Gtk.MessageType.ERROR,
|
||||
buttons=Gtk.ButtonsType.OK,
|
||||
text="Update Failed",
|
||||
)
|
||||
error_dialog.format_secondary_text(error_message)
|
||||
error_dialog.run()
|
||||
error_dialog.destroy()
|
||||
|
||||
def on_window_destroyed(self, _widget):
|
||||
"""
|
||||
If the window is destroyed and we're in standalone mode, quit Gtk.main().
|
||||
"""
|
||||
if hasattr(self, "pulse_timeout_id"):
|
||||
GLib.source_remove(self.pulse_timeout_id)
|
||||
delattr(self, "pulse_timeout_id")
|
||||
|
||||
if self.quit_gtk_main_on_destroy:
|
||||
Gtk.main_quit()
|
||||
|
||||
|
||||
def _initiate_update_check_flow(is_standalone_mode, force=False): # Added force argument with default
|
||||
"""
|
||||
Logic that checks connection, snooze, and downloads the remote version.
|
||||
If there's a new version or force is True, launches the update window.
|
||||
"""
|
||||
global _QUIT_GTK_IF_NO_WINDOW_STANDALONE
|
||||
|
||||
# --- Check if updater is permanently disabled ---
|
||||
disable_file_path = get_disable_file_path()
|
||||
if os.path.exists(disable_file_path) and not force:
|
||||
print(f"Updater is disabled via {UPDATER_DISABLE_FILE_NAME}. Skipping update check.")
|
||||
if is_standalone_mode and _QUIT_GTK_IF_NO_WINDOW_STANDALONE:
|
||||
GLib.idle_add(Gtk.main_quit)
|
||||
return
|
||||
|
||||
if not is_connected():
|
||||
print("No internet connection. Skipping update check.")
|
||||
if is_standalone_mode and _QUIT_GTK_IF_NO_WINDOW_STANDALONE:
|
||||
GLib.idle_add(Gtk.main_quit)
|
||||
return
|
||||
|
||||
fetch_remote_version()
|
||||
latest_version, changelog, _, pkg_update = get_remote_version() # Unpack pkg_update
|
||||
|
||||
if force:
|
||||
print(f"Force update mode enabled. Opening updater for version {latest_version}.")
|
||||
if latest_version == "0.0.0" and not changelog: # And pkg_update will be True (default)
|
||||
print(f"Warning: Could not fetch remote version details for {data.APP_NAME_CAP}. Updater will show default/empty info.")
|
||||
GLib.idle_add(launch_update_window, latest_version, changelog, pkg_update, is_standalone_mode) # Pass pkg_update
|
||||
return # Exit after launching in force mode
|
||||
|
||||
# --- Regular update check flow (if not forced) ---
|
||||
snooze_file_path = get_snooze_file_path()
|
||||
if os.path.exists(snooze_file_path):
|
||||
try:
|
||||
with open(snooze_file_path, "r") as f:
|
||||
snooze_timestamp_str = f.read().strip()
|
||||
snooze_timestamp = float(snooze_timestamp_str)
|
||||
|
||||
current_time = time.time()
|
||||
if current_time - snooze_timestamp < SNOOZE_DURATION_SECONDS:
|
||||
snooze_until_time = snooze_timestamp + SNOOZE_DURATION_SECONDS
|
||||
snooze_until_time_str = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(snooze_until_time))
|
||||
print(f"Check postponed. It will resume after {snooze_until_time_str}.")
|
||||
if is_standalone_mode and _QUIT_GTK_IF_NO_WINDOW_STANDALONE:
|
||||
GLib.idle_add(Gtk.main_quit)
|
||||
return
|
||||
else:
|
||||
print("Snooze period expired. Removing file and checking for updates.")
|
||||
os.remove(snooze_file_path)
|
||||
except ValueError:
|
||||
print(f"Error: invalid content in snooze file. Removing: {snooze_file_path}")
|
||||
try:
|
||||
os.remove(snooze_file_path)
|
||||
except OSError as e_remove:
|
||||
print(f"Error removing corrupt snooze file: {e_remove}")
|
||||
except Exception as e_snooze:
|
||||
print(f"Error processing snooze file {snooze_file_path}: {e_snooze}. Proceeding with check.")
|
||||
try:
|
||||
os.remove(snooze_file_path) # Attempt to remove problematic snooze file
|
||||
except OSError as e_remove_generic:
|
||||
print(f"Error removing problematic snooze file: {e_remove_generic}")
|
||||
|
||||
|
||||
current_version, _ = get_local_version()
|
||||
|
||||
# Basic version comparison (not strict semver)
|
||||
if latest_version > current_version and latest_version != "0.0.0":
|
||||
GLib.idle_add(launch_update_window, latest_version, changelog, pkg_update, is_standalone_mode) # Pass pkg_update
|
||||
else:
|
||||
print(f"{data.APP_NAME_CAP} is up to date or the remote version is invalid.")
|
||||
if is_standalone_mode and _QUIT_GTK_IF_NO_WINDOW_STANDALONE:
|
||||
GLib.idle_add(Gtk.main_quit)
|
||||
|
||||
|
||||
def launch_update_window(latest_version, changelog, pkg_update, is_standalone_mode):
|
||||
"""
|
||||
Creates and shows the update window.
|
||||
"""
|
||||
win = UpdateWindow(latest_version, changelog, pkg_update, is_standalone_mode) # Pass pkg_update
|
||||
if is_standalone_mode:
|
||||
win.quit_gtk_main_on_destroy = True
|
||||
win.show_all()
|
||||
|
||||
|
||||
def check_for_updates():
|
||||
"""
|
||||
Entry point when called from the main application.
|
||||
Initiates an update check in a background thread without force.
|
||||
"""
|
||||
# Create wrapper function for GLib.Thread compatibility
|
||||
def _update_check_wrapper(user_data):
|
||||
_initiate_update_check_flow(False, False)
|
||||
|
||||
GLib.Thread.new("update-check", _update_check_wrapper, None)
|
||||
|
||||
|
||||
def run_updater(force=False): # Modified to accept force argument
|
||||
"""
|
||||
Standalone entry point: starts Gtk.main and the update check.
|
||||
Args:
|
||||
force (bool): If True, opens the updater even if the version isn't outdated or snoozed.
|
||||
Defaults to False.
|
||||
"""
|
||||
global _QUIT_GTK_IF_NO_WINDOW_STANDALONE
|
||||
_QUIT_GTK_IF_NO_WINDOW_STANDALONE = True
|
||||
|
||||
# Create wrapper function for GLib.Thread compatibility
|
||||
def _standalone_update_wrapper(user_data):
|
||||
_initiate_update_check_flow(True, force)
|
||||
|
||||
GLib.Thread.new("standalone-update-check", _standalone_update_wrapper, None)
|
||||
|
||||
Gtk.main()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Example of how to run with force=True:
|
||||
# run_updater(force=True)
|
||||
# By default, runs with force=False:
|
||||
run_updater()
|
||||
Reference in New Issue
Block a user