Add mock mode support and input validation
This commit is contained in:
@@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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(),
|
||||||
|
|||||||
@@ -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,30 +183,36 @@ 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)
|
||||||
|
|
||||||
if current_page_name == "summary":
|
if current_page_name == "summary":
|
||||||
# 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):
|
||||||
|
|||||||
Reference in New Issue
Block a user