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 sys
import gi import gi
@@ -11,21 +12,30 @@ from .ui.window import InstallerWindow
class IridiumInstallerApp(Adw.Application): class IridiumInstallerApp(Adw.Application):
def __init__(self): def __init__(self, mock_mode=False):
super().__init__( super().__init__(
application_id="org.iridium.Installer", application_id="org.iridium.Installer",
flags=Gio.ApplicationFlags.FLAGS_NONE, flags=Gio.ApplicationFlags.FLAGS_NONE,
) )
self.mock_mode = mock_mode
def do_activate(self): def do_activate(self):
win = self.props.active_window win = self.props.active_window
if not win: if not win:
win = InstallerWindow(application=self) win = InstallerWindow(application=self, mock_mode=self.mock_mode)
win.present() win.present()
def main(): 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) return app.run(sys.argv)

View File

@@ -1,14 +1,22 @@
import re
import gi import gi
gi.require_version("Gtk", "4.0") gi.require_version("Gtk", "4.0")
gi.require_version("Adw", "1") gi.require_version("Adw", "1")
from gi.repository import Adw, Gtk from gi.repository import Adw, GObject, Gtk
class UserPage(Adw.Bin): class UserPage(Adw.Bin):
__gsignals__ = {
"validity-changed": (GObject.SignalFlags.RUN_FIRST, None, (bool,)),
}
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self._is_valid = False
clamp = Adw.Clamp() clamp = Adw.Clamp()
clamp.set_maximum_size(500) clamp.set_maximum_size(500)
self.set_child(clamp) self.set_child(clamp)
@@ -32,6 +40,7 @@ class UserPage(Adw.Bin):
self.fullname_row.set_title("Full Name") self.fullname_row.set_title("Full Name")
self.fullname_row.set_text("Administrator") self.fullname_row.set_text("Administrator")
self.fullname_row.connect("notify::text", self.on_fullname_changed) 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) group.add(self.fullname_row)
self.username_row = Adw.EntryRow() self.username_row = Adw.EntryRow()
@@ -40,14 +49,17 @@ class UserPage(Adw.Bin):
self.username_handler_id = self.username_row.connect( self.username_handler_id = self.username_row.connect(
"notify::text", self.on_username_changed "notify::text", self.on_username_changed
) )
self.username_row.connect("notify::text", self.on_input_changed)
group.add(self.username_row) group.add(self.username_row)
self.password_row = Adw.PasswordEntryRow() self.password_row = Adw.PasswordEntryRow()
self.password_row.set_title("Password") self.password_row.set_title("Password")
self.password_row.connect("notify::text", self.on_input_changed)
group.add(self.password_row) group.add(self.password_row)
self.confirm_row = Adw.PasswordEntryRow() self.confirm_row = Adw.PasswordEntryRow()
self.confirm_row.set_title("Confirm Password") self.confirm_row.set_title("Confirm Password")
self.confirm_row.connect("notify::text", self.on_input_changed)
group.add(self.confirm_row) group.add(self.confirm_row)
# Hostname # Hostname
@@ -58,10 +70,14 @@ class UserPage(Adw.Bin):
self.hostname_row = Adw.EntryRow() self.hostname_row = Adw.EntryRow()
self.hostname_row.set_title("Hostname") self.hostname_row.set_title("Hostname")
self.hostname_row.set_text("iridium") self.hostname_row.set_text("iridium")
self.hostname_row.connect("notify::text", self.on_input_changed)
host_group.add(self.hostname_row) host_group.add(self.hostname_row)
self.username_manually_set = False self.username_manually_set = False
# Initial validation
self.validate()
def on_fullname_changed(self, entry, _pspec): def on_fullname_changed(self, entry, _pspec):
if self.username_manually_set: if self.username_manually_set:
return return
@@ -113,6 +129,53 @@ class UserPage(Adw.Bin):
def on_username_changed(self, entry, _pspec): def on_username_changed(self, entry, _pspec):
self.username_manually_set = True 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): def get_user_info(self):
return { return {
"fullname": self.fullname_row.get_text(), "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.install_mode import InstallModePage
from .pages.partitioning import PartitioningPage, calculate_auto_partitions from .pages.partitioning import PartitioningPage, calculate_auto_partitions
from .pages.storage import StoragePage from .pages.storage import StoragePage
from .pages.summary import SummaryPage
from .pages.user import UserPage from .pages.user import UserPage
from .pages.welcome import WelcomePage from .pages.welcome import WelcomePage
from .pages.summary import SummaryPage
class InstallerWindow(Adw.ApplicationWindow): class InstallerWindow(Adw.ApplicationWindow):
def __init__(self, *args, **kwargs): def __init__(self, mock_mode=False, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.mock_mode = mock_mode
self.set_default_size(900, 650) 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.toolbar_view = Adw.ToolbarView()
self.set_content(self.toolbar_view) self.set_content(self.toolbar_view)
@@ -71,6 +73,7 @@ class InstallerWindow(Adw.ApplicationWindow):
self.partitioning_page = PartitioningPage() self.partitioning_page = PartitioningPage()
self.modules_page = ModulesPage() self.modules_page = ModulesPage()
self.user_page = UserPage() self.user_page = UserPage()
self.user_page.connect("validity-changed", self.on_user_validity_changed)
self.summary_page = SummaryPage() self.summary_page = SummaryPage()
# Add Pages # Add Pages
@@ -94,6 +97,9 @@ class InstallerWindow(Adw.ApplicationWindow):
def on_disk_selected(self, page, device_name): def on_disk_selected(self, page, device_name):
self.update_buttons() self.update_buttons()
def on_user_validity_changed(self, page, valid):
self.update_buttons()
def update_buttons(self): def update_buttons(self):
# Back button state # Back button state
self.back_button.set_sensitive(self.current_page_index > 0) self.back_button.set_sensitive(self.current_page_index > 0)
@@ -105,11 +111,15 @@ class InstallerWindow(Adw.ApplicationWindow):
forced_selection = True forced_selection = True
if current_page_name == "storage": if current_page_name == "storage":
forced_selection = self.storage_page.get_selected_disk() is not None 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) self.next_button.set_sensitive(forced_selection)
if current_page_name == "summary": 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.add_css_class("destructive-action")
self.next_button.remove_css_class("suggested-action") self.next_button.remove_css_class("suggested-action")
else: else:
@@ -173,7 +183,7 @@ class InstallerWindow(Adw.ApplicationWindow):
mode=mode, mode=mode,
partitions=partitions_config, partitions=partitions_config,
user_info=user_info, user_info=user_info,
modules=modules modules=modules,
) )
# Proceed to summary page (which is next_index after user) # Proceed to summary page (which is next_index after user)
@@ -181,22 +191,28 @@ class InstallerWindow(Adw.ApplicationWindow):
# THIS IS THE REAL INSTALL TRIGGER # THIS IS THE REAL INSTALL TRIGGER
print("Install process triggered!") print("Install process triggered!")
disk = self.storage_page.get_selected_disk() disk = self.storage_page.get_selected_disk()
print(f"Disk: {disk}")
mode = self.install_mode_page.get_mode() mode = self.install_mode_page.get_mode()
print(f"Mode: {mode}")
modules = self.modules_page.get_modules() modules = self.modules_page.get_modules()
print(f"Modules: {modules}")
user_info = self.user_page.get_user_info() user_info = self.user_page.get_user_info()
print(f"User: {user_info}")
# Recalculate or retrieve partitions just to be safe/consistent
partitions_config = {} partitions_config = {}
if mode == "manual": if mode == "manual":
partitions_config = self.partitioning_page.get_config() partitions_config = self.partitioning_page.get_config()
elif mode == "automatic": elif mode == "automatic":
partitions = calculate_auto_partitions(disk) partitions = calculate_auto_partitions(disk)
partitions_config = {"partitions": partitions} 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 return
if next_index < len(self.page_ids): if next_index < len(self.page_ids):

3
run.sh
View File

@@ -1,5 +1,4 @@
#!/bin/bash #!/bin/bash
export GSETTINGS_SCHEMA_DIR=. export GSETTINGS_SCHEMA_DIR=.
echo "Starting Iridium Installer..." python3 -m iridium_installer.main "$@"
python3 -m iridium_installer.main