Bigger update modal, error report to Downloads, reboot button
This commit is contained in:
@@ -5,6 +5,7 @@ from __future__ import annotations
|
|||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
import threading
|
import threading
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
import gi
|
import gi
|
||||||
|
|
||||||
@@ -42,12 +43,20 @@ AUTOSTART_DIR = os.path.join(
|
|||||||
USER_AUTOSTART_FILE = os.path.join(AUTOSTART_DIR, "sovran-hub.desktop")
|
USER_AUTOSTART_FILE = os.path.join(AUTOSTART_DIR, "sovran-hub.desktop")
|
||||||
SYSTEM_AUTOSTART_FILE = "/etc/xdg/autostart/sovran-hub.desktop"
|
SYSTEM_AUTOSTART_FILE = "/etc/xdg/autostart/sovran-hub.desktop"
|
||||||
|
|
||||||
|
DOWNLOADS_DIR = os.path.join(os.path.expanduser("~"), "Downloads")
|
||||||
|
|
||||||
UPDATE_COMMAND = [
|
UPDATE_COMMAND = [
|
||||||
"ssh", "-o", "StrictHostKeyChecking=no", "-o", "BatchMode=yes",
|
"ssh", "-o", "StrictHostKeyChecking=no", "-o", "BatchMode=yes",
|
||||||
"root@localhost",
|
"root@localhost",
|
||||||
"cd /etc/nixos && nix flake update && nixos-rebuild switch && flatpak update -y",
|
"cd /etc/nixos && nix flake update && nixos-rebuild switch && flatpak update -y",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
REBOOT_COMMAND = [
|
||||||
|
"ssh", "-o", "StrictHostKeyChecking=no", "-o", "BatchMode=yes",
|
||||||
|
"root@localhost",
|
||||||
|
"reboot",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
def get_autostart_enabled() -> bool:
|
def get_autostart_enabled() -> bool:
|
||||||
if os.path.isfile(USER_AUTOSTART_FILE):
|
if os.path.isfile(USER_AUTOSTART_FILE):
|
||||||
@@ -85,20 +94,42 @@ class UpdateDialog(Adw.Window):
|
|||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
super().__init__(
|
super().__init__(
|
||||||
title="Sovran_SystemsOS Update",
|
title="Sovran_SystemsOS Update",
|
||||||
default_width=700,
|
default_width=900,
|
||||||
default_height=500,
|
default_height=700,
|
||||||
modal=True,
|
modal=True,
|
||||||
transient_for=parent,
|
transient_for=parent,
|
||||||
)
|
)
|
||||||
|
|
||||||
self._process = None
|
self._process = None
|
||||||
|
self._full_log = ""
|
||||||
|
|
||||||
header = Adw.HeaderBar()
|
header = Adw.HeaderBar()
|
||||||
|
|
||||||
|
# ── Close button (disabled during update) ────────────────
|
||||||
self._close_btn = Gtk.Button(label="Close", sensitive=False)
|
self._close_btn = Gtk.Button(label="Close", sensitive=False)
|
||||||
self._close_btn.connect("clicked", lambda _b: self.close())
|
self._close_btn.connect("clicked", lambda _b: self.close())
|
||||||
header.pack_end(self._close_btn)
|
header.pack_end(self._close_btn)
|
||||||
|
|
||||||
|
# ── Reboot button (hidden until update succeeds) ─────────
|
||||||
|
self._reboot_btn = Gtk.Button(
|
||||||
|
label="Reboot",
|
||||||
|
css_classes=["destructive-action"],
|
||||||
|
tooltip_text="Reboot the system now",
|
||||||
|
visible=False,
|
||||||
|
)
|
||||||
|
self._reboot_btn.connect("clicked", self._on_reboot_clicked)
|
||||||
|
header.pack_end(self._reboot_btn)
|
||||||
|
|
||||||
|
# ── Save error report button (hidden until failure) ──────
|
||||||
|
self._save_btn = Gtk.Button(
|
||||||
|
label="Save Error Report",
|
||||||
|
css_classes=["warning"],
|
||||||
|
tooltip_text="Save full log to ~/Downloads",
|
||||||
|
visible=False,
|
||||||
|
)
|
||||||
|
self._save_btn.connect("clicked", self._on_save_report)
|
||||||
|
header.pack_start(self._save_btn)
|
||||||
|
|
||||||
self._spinner = Gtk.Spinner(spinning=True)
|
self._spinner = Gtk.Spinner(spinning=True)
|
||||||
header.pack_start(self._spinner)
|
header.pack_start(self._spinner)
|
||||||
|
|
||||||
@@ -106,8 +137,8 @@ class UpdateDialog(Adw.Window):
|
|||||||
label="Updating…",
|
label="Updating…",
|
||||||
css_classes=["title-4"],
|
css_classes=["title-4"],
|
||||||
halign=Gtk.Align.CENTER,
|
halign=Gtk.Align.CENTER,
|
||||||
margin_top=8,
|
margin_top=12,
|
||||||
margin_bottom=4,
|
margin_bottom=8,
|
||||||
)
|
)
|
||||||
|
|
||||||
self._textview = Gtk.TextView(
|
self._textview = Gtk.TextView(
|
||||||
@@ -144,15 +175,18 @@ class UpdateDialog(Adw.Window):
|
|||||||
self._start_update()
|
self._start_update()
|
||||||
|
|
||||||
def _append_text(self, text):
|
def _append_text(self, text):
|
||||||
|
self._full_log += text
|
||||||
end_iter = self._buffer.get_end_iter()
|
end_iter = self._buffer.get_end_iter()
|
||||||
self._buffer.insert(end_iter, text)
|
self._buffer.insert(end_iter, text)
|
||||||
# Auto-scroll to bottom
|
|
||||||
mark = self._buffer.create_mark(None, self._buffer.get_end_iter(), False)
|
mark = self._buffer.create_mark(None, self._buffer.get_end_iter(), False)
|
||||||
self._textview.scroll_mark_onscreen(mark)
|
self._textview.scroll_mark_onscreen(mark)
|
||||||
self._buffer.delete_mark(mark)
|
self._buffer.delete_mark(mark)
|
||||||
|
|
||||||
def _start_update(self):
|
def _start_update(self):
|
||||||
self._append_text("$ ssh root@localhost 'cd /etc/nixos && nix flake update && nixos-rebuild switch && flatpak update -y'\n\n")
|
self._append_text(
|
||||||
|
"$ ssh root@localhost 'cd /etc/nixos && nix flake update "
|
||||||
|
"&& nixos-rebuild switch && flatpak update -y'\n\n"
|
||||||
|
)
|
||||||
thread = threading.Thread(target=self._run_update, daemon=True)
|
thread = threading.Thread(target=self._run_update, daemon=True)
|
||||||
thread.start()
|
thread.start()
|
||||||
|
|
||||||
@@ -187,11 +221,51 @@ class UpdateDialog(Adw.Window):
|
|||||||
if success:
|
if success:
|
||||||
self._status_label.set_label("✓ " + message)
|
self._status_label.set_label("✓ " + message)
|
||||||
self._status_label.set_css_classes(["title-4", "success"])
|
self._status_label.set_css_classes(["title-4", "success"])
|
||||||
|
self._reboot_btn.set_visible(True)
|
||||||
else:
|
else:
|
||||||
self._status_label.set_label("✗ " + message)
|
self._status_label.set_label("✗ " + message)
|
||||||
self._status_label.set_css_classes(["title-4", "error"])
|
self._status_label.set_css_classes(["title-4", "error"])
|
||||||
|
self._save_btn.set_visible(True)
|
||||||
|
|
||||||
self._append_text(f"\n{'─' * 50}\n{message}\n")
|
self._append_text(f"\n{'─' * 60}\n{message}\n")
|
||||||
|
|
||||||
|
def _on_save_report(self, _btn):
|
||||||
|
os.makedirs(DOWNLOADS_DIR, exist_ok=True)
|
||||||
|
timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
|
||||||
|
filename = f"sovran-update-error-{timestamp}.log"
|
||||||
|
filepath = os.path.join(DOWNLOADS_DIR, filename)
|
||||||
|
try:
|
||||||
|
with open(filepath, "w") as f:
|
||||||
|
f.write(f"Sovran_SystemsOS Update Error Report\n")
|
||||||
|
f.write(f"Date: {datetime.now().isoformat()}\n")
|
||||||
|
f.write(f"{'═' * 60}\n\n")
|
||||||
|
f.write(self._full_log)
|
||||||
|
self._save_btn.set_label(f"Saved: {filename}")
|
||||||
|
self._save_btn.set_sensitive(False)
|
||||||
|
self._append_text(f"\n✓ Error report saved to ~/Downloads/{filename}\n")
|
||||||
|
except Exception as e:
|
||||||
|
self._append_text(f"\n✗ Failed to save report: {e}\n")
|
||||||
|
|
||||||
|
def _on_reboot_clicked(self, _btn):
|
||||||
|
dialog = Adw.MessageDialog(
|
||||||
|
transient_for=self,
|
||||||
|
heading="Reboot Now?",
|
||||||
|
body="The system will restart immediately. Save any open work first.",
|
||||||
|
)
|
||||||
|
dialog.add_response("cancel", "Cancel")
|
||||||
|
dialog.add_response("reboot", "Reboot")
|
||||||
|
dialog.set_response_appearance("reboot", Adw.ResponseAppearance.DESTRUCTIVE)
|
||||||
|
dialog.set_default_response("cancel")
|
||||||
|
dialog.set_close_response("cancel")
|
||||||
|
dialog.connect("response", self._on_reboot_confirmed)
|
||||||
|
dialog.present()
|
||||||
|
|
||||||
|
def _on_reboot_confirmed(self, dialog, response):
|
||||||
|
if response == "reboot":
|
||||||
|
try:
|
||||||
|
subprocess.Popen(REBOOT_COMMAND)
|
||||||
|
except Exception as e:
|
||||||
|
self._append_text(f"\n✗ Reboot failed: {e}\n")
|
||||||
|
|
||||||
|
|
||||||
class SovranHubWindow(Adw.ApplicationWindow):
|
class SovranHubWindow(Adw.ApplicationWindow):
|
||||||
|
|||||||
Reference in New Issue
Block a user