Add mock mode support and input validation

This commit is contained in:
2026-02-02 20:19:36 +01:00
parent 7a69d62782
commit 26757288c4
4 changed files with 107 additions and 19 deletions

View File

@@ -1,3 +1,4 @@
import argparse
import sys
import gi
@@ -11,21 +12,30 @@ from .ui.window import InstallerWindow
class IridiumInstallerApp(Adw.Application):
def __init__(self):
def __init__(self, mock_mode=False):
super().__init__(
application_id="org.iridium.Installer",
flags=Gio.ApplicationFlags.FLAGS_NONE,
)
self.mock_mode = mock_mode
def do_activate(self):
win = self.props.active_window
if not win:
win = InstallerWindow(application=self)
win = InstallerWindow(application=self, mock_mode=self.mock_mode)
win.present()
def main():
app = IridiumInstallerApp()
parser = argparse.ArgumentParser(description="Iridium OS Installer")
parser.add_argument(
"--mock",
action="store_true",
help="Run in mock mode (no actual changes to disk)",
)
args = parser.parse_args()
app = IridiumInstallerApp(mock_mode=args.mock)
return app.run(sys.argv)

View File

@@ -1,14 +1,22 @@
import re
import gi
gi.require_version("Gtk", "4.0")
gi.require_version("Adw", "1")
from gi.repository import Adw, Gtk
from gi.repository import Adw, GObject, Gtk
class UserPage(Adw.Bin):
__gsignals__ = {
"validity-changed": (GObject.SignalFlags.RUN_FIRST, None, (bool,)),
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._is_valid = False
clamp = Adw.Clamp()
clamp.set_maximum_size(500)
self.set_child(clamp)
@@ -32,6 +40,7 @@ class UserPage(Adw.Bin):
self.fullname_row.set_title("Full Name")
self.fullname_row.set_text("Administrator")
self.fullname_row.connect("notify::text", self.on_fullname_changed)
self.fullname_row.connect("notify::text", self.on_input_changed)
group.add(self.fullname_row)
self.username_row = Adw.EntryRow()
@@ -40,14 +49,17 @@ class UserPage(Adw.Bin):
self.username_handler_id = self.username_row.connect(
"notify::text", self.on_username_changed
)
self.username_row.connect("notify::text", self.on_input_changed)
group.add(self.username_row)
self.password_row = Adw.PasswordEntryRow()
self.password_row.set_title("Password")
self.password_row.connect("notify::text", self.on_input_changed)
group.add(self.password_row)
self.confirm_row = Adw.PasswordEntryRow()
self.confirm_row.set_title("Confirm Password")
self.confirm_row.connect("notify::text", self.on_input_changed)
group.add(self.confirm_row)
# Hostname
@@ -58,10 +70,14 @@ class UserPage(Adw.Bin):
self.hostname_row = Adw.EntryRow()
self.hostname_row.set_title("Hostname")
self.hostname_row.set_text("iridium")
self.hostname_row.connect("notify::text", self.on_input_changed)
host_group.add(self.hostname_row)
self.username_manually_set = False
# Initial validation
self.validate()
def on_fullname_changed(self, entry, _pspec):
if self.username_manually_set:
return
@@ -113,6 +129,53 @@ class UserPage(Adw.Bin):
def on_username_changed(self, entry, _pspec):
self.username_manually_set = True
def on_input_changed(self, *args):
self.validate()
def validate(self):
valid = True
# Username validation
username = self.username_row.get_text()
if not username:
valid = False
self.username_row.add_css_class("error")
elif not re.match(r"^[a-z_][a-z0-9_-]*$", username):
valid = False
self.username_row.add_css_class("error")
else:
self.username_row.remove_css_class("error")
# Password validation
pw = self.password_row.get_text()
confirm = self.confirm_row.get_text()
if not pw:
valid = False
# Don't show error for empty password initially, just invalidate
# It looks a lot nicer like that
if pw != confirm:
valid = False
self.confirm_row.add_css_class("error")
else:
self.confirm_row.remove_css_class("error")
# Hostname validation
hostname = self.hostname_row.get_text()
if not hostname:
valid = False
self.hostname_row.add_css_class("error")
else:
self.hostname_row.remove_css_class("error")
if self._is_valid != valid:
self._is_valid = valid
self.emit("validity-changed", valid)
def is_valid(self):
return self._is_valid
def get_user_info(self):
return {
"fullname": self.fullname_row.get_text(),

View File

@@ -8,17 +8,19 @@ from .pages.additional_modules import ModulesPage
from .pages.install_mode import InstallModePage
from .pages.partitioning import PartitioningPage, calculate_auto_partitions
from .pages.storage import StoragePage
from .pages.summary import SummaryPage
from .pages.user import UserPage
from .pages.welcome import WelcomePage
from .pages.summary import SummaryPage
class InstallerWindow(Adw.ApplicationWindow):
def __init__(self, *args, **kwargs):
def __init__(self, mock_mode=False, *args, **kwargs):
super().__init__(*args, **kwargs)
self.mock_mode = mock_mode
self.set_default_size(900, 650)
self.set_title("Iridium Installer")
self.set_title("Iridium Installer" + (" (MOCK MODE)" if mock_mode else ""))
self.toolbar_view = Adw.ToolbarView()
self.set_content(self.toolbar_view)
@@ -71,6 +73,7 @@ class InstallerWindow(Adw.ApplicationWindow):
self.partitioning_page = PartitioningPage()
self.modules_page = ModulesPage()
self.user_page = UserPage()
self.user_page.connect("validity-changed", self.on_user_validity_changed)
self.summary_page = SummaryPage()
# Add Pages
@@ -94,6 +97,9 @@ class InstallerWindow(Adw.ApplicationWindow):
def on_disk_selected(self, page, device_name):
self.update_buttons()
def on_user_validity_changed(self, page, valid):
self.update_buttons()
def update_buttons(self):
# Back button state
self.back_button.set_sensitive(self.current_page_index > 0)
@@ -105,11 +111,15 @@ class InstallerWindow(Adw.ApplicationWindow):
forced_selection = True
if current_page_name == "storage":
forced_selection = self.storage_page.get_selected_disk() is not None
elif current_page_name == "user":
forced_selection = self.user_page.is_valid()
self.next_button.set_sensitive(forced_selection)
if current_page_name == "summary":
self.next_button.set_label("Install")
self.next_button.set_label(
"Install" + (" (MOCK)" if self.mock_mode else "")
)
self.next_button.add_css_class("destructive-action")
self.next_button.remove_css_class("suggested-action")
else:
@@ -173,30 +183,36 @@ class InstallerWindow(Adw.ApplicationWindow):
mode=mode,
partitions=partitions_config,
user_info=user_info,
modules=modules
modules=modules,
)
# Proceed to summary page (which is next_index after user)
if current_page_name == "summary":
# THIS IS THE REAL INSTALL TRIGGER
print("Install process triggered!")
disk = self.storage_page.get_selected_disk()
print(f"Disk: {disk}")
mode = self.install_mode_page.get_mode()
print(f"Mode: {mode}")
modules = self.modules_page.get_modules()
print(f"Modules: {modules}")
user_info = self.user_page.get_user_info()
print(f"User: {user_info}")
# Recalculate or retrieve partitions just to be safe/consistent
partitions_config = {}
if mode == "manual":
partitions_config = self.partitioning_page.get_config()
elif mode == "automatic":
partitions = calculate_auto_partitions(disk)
partitions_config = {"partitions": partitions}
print(f"Partitioning: {partitions_config}")
if self.mock_mode:
print("!!! MOCK MODE ENABLED - NO CHANGES WILL BE MADE !!!")
print(f"Target Disk: {disk}")
print(f"Mode: {mode}")
print(f"Modules: {modules}")
print(f"User: {user_info}")
print(f"Partition Config: {partitions_config}")
print("Simulation complete.")
else:
print("NOT IMPLEMENTED")
return
if next_index < len(self.page_ids):