diff --git a/configs/airootfs/usr/local/bin/omarchy-install-dashboard b/configs/airootfs/usr/local/bin/omarchy-install-dashboard index 288e84c..1c02c6f 100755 --- a/configs/airootfs/usr/local/bin/omarchy-install-dashboard +++ b/configs/airootfs/usr/local/bin/omarchy-install-dashboard @@ -197,10 +197,11 @@ weights = [ ('Starting installation', 1, 2, 2), ('Preparing live environment', 2, 6, 5), ('Verifying protected mounts', 6, 8, 3), - ('Installing Arch + Omarchy', 8, 82, 60), - ('Installing base system', 8, 82, 60), - ('Configuring protected boot', 82, 86, 8), - ('Finalizing in chroot', 86, 98, 90), + ('Installing Arch + Omarchy', 8, 86, 95), + ('Installing base system', 8, 84, 85), + ('Configuring protected boot', 84, 86, 8), + ('Configuring hibernation', 86, 88, 5), + ('Finalizing in chroot', 88, 98, 10), ('Configuring login', 98, 99, 3), ('Validating protected boot setup', 99, 100, 5), ('Validating boot setup', 99, 100, 5), diff --git a/configs/airootfs/usr/share/omarchy-iso/orchestrator/main.py b/configs/airootfs/usr/share/omarchy-iso/orchestrator/main.py index 93cbd52..277a29f 100644 --- a/configs/airootfs/usr/share/omarchy-iso/orchestrator/main.py +++ b/configs/airootfs/usr/share/omarchy-iso/orchestrator/main.py @@ -34,6 +34,7 @@ def build_phases(ctx: InstallContext): arch_install_base, verify_protected_mounts, configure_protected_boot, + configure_hibernation, run_chroot_finalizer, configure_login, validate_boot_full, @@ -46,6 +47,7 @@ def build_phases(ctx: InstallContext): ("Verifying protected mounts", verify_protected_mounts), ("Installing base system", arch_install_base), ("Configuring protected boot", configure_protected_boot), + ("Configuring hibernation", configure_hibernation), ("Finalizing in chroot", run_chroot_finalizer), ("Configuring login", configure_login), ("Validating protected boot setup", validate_boot_protected), @@ -54,6 +56,7 @@ def build_phases(ctx: InstallContext): return [ ("Preparing live environment", prepare_live), ("Installing Arch + Omarchy", arch_install_full), + ("Configuring hibernation", configure_hibernation), ("Finalizing in chroot", run_chroot_finalizer), ("Configuring login", configure_login), ("Validating boot setup", validate_boot_full), diff --git a/configs/airootfs/usr/share/omarchy-iso/orchestrator/phases_impl.py b/configs/airootfs/usr/share/omarchy-iso/orchestrator/phases_impl.py index fe93ce3..8860465 100644 --- a/configs/airootfs/usr/share/omarchy-iso/orchestrator/phases_impl.py +++ b/configs/airootfs/usr/share/omarchy-iso/orchestrator/phases_impl.py @@ -134,41 +134,45 @@ def arch_install_full(ctx: InstallContext) -> None: if config.mirror_config: installer.set_mirrors(mirror_handler, config.mirror_config, on_target=False) - info("› installing base system (mkinitcpio deferred to final Limine UKI build)") - installer.minimal_installation( - optional_repositories=( - config.mirror_config.optional_repositories - if config.mirror_config else [] - ), - mkinitcpio=False, - hostname=config.hostname, - locale_config=config.locale_config, - pacman_config=config.pacman_config, - ) + _mask_mkinitcpio_pacman_hooks(ctx) + try: + info("› installing base system (mkinitcpio deferred to final Limine UKI build)") + installer.minimal_installation( + optional_repositories=( + config.mirror_config.optional_repositories + if config.mirror_config else [] + ), + mkinitcpio=False, + hostname=config.hostname, + locale_config=config.locale_config, + pacman_config=config.pacman_config, + ) - if config.mirror_config: - installer.set_mirrors(mirror_handler, config.mirror_config, on_target=True) + if config.mirror_config: + installer.set_mirrors(mirror_handler, config.mirror_config, on_target=True) - if config.swap and config.swap.enabled: - installer.setup_swap(algo=config.swap.algorithm) + if config.swap and config.swap.enabled: + installer.setup_swap(algo=config.swap.algorithm) - info(f"› installing early Omarchy packages: {', '.join(EARLY_PACKAGES)}") - installer.add_additional_packages(EARLY_PACKAGES) + info(f"› installing early Omarchy packages: {', '.join(EARLY_PACKAGES)}") + installer.add_additional_packages(EARLY_PACKAGES) - if arch.bootloader_enabled(config): - if not arch.is_limine(config): - raise RuntimeError( - "Omarchy full-disk installs only support Limine bootloader setup" - ) - info("› installing bootloader (Limine)") - _install_limine_omarchy(ctx, installer, config) + if arch.bootloader_enabled(config): + if not arch.is_limine(config): + raise RuntimeError( + "Omarchy full-disk installs only support Limine bootloader setup" + ) + info("› installing bootloader (Limine)") + _install_limine_omarchy(ctx, installer, config) - info("› writing Limine config (so limine-mkinitcpio-hook fires correctly)") - _write_limine_defaults_from_config(ctx, installer, config) + info("› writing Limine config (so limine-mkinitcpio-hook fires correctly)") + _write_limine_defaults_from_config(ctx, installer, config) - info("› creating user (with /etc/skel populated)") - if config.auth_config and config.auth_config.users: - installer.create_users(config.auth_config.users) + info("› creating user (with /etc/skel populated)") + if config.auth_config and config.auth_config.users: + installer.create_users(config.auth_config.users) + finally: + _unmask_mkinitcpio_pacman_hooks(ctx) info("› installing Omarchy runtime + omarchy-base.packages") installer.add_additional_packages(_runtime_package_list(ctx)) @@ -398,6 +402,32 @@ def _limine_template(ctx: InstallContext, filename: str) -> Path: raise RuntimeError(f"Limine template {filename} not found. Searched:\n {searched}") +def _mask_mkinitcpio_pacman_hooks(ctx: InstallContext) -> None: + """Temporarily suppress mkinitcpio pacman hooks between base and runtime. + + The final Limine UKI build happens after all boot config is known. Pacman + would otherwise rebuild initramfs during the base linux transaction and + again when early settings/plymouth packages land. + """ + hooks_dir = ctx.target / "etc" / "pacman.d" / "hooks" + hooks_dir.mkdir(parents=True, exist_ok=True) + for name in ("90-mkinitcpio-install.hook", "60-mkinitcpio-remove.hook"): + path = hooks_dir / name + path.unlink(missing_ok=True) + path.symlink_to("/dev/null") + + +def _unmask_mkinitcpio_pacman_hooks(ctx: InstallContext) -> None: + hooks_dir = ctx.target / "etc" / "pacman.d" / "hooks" + for name in ("90-mkinitcpio-install.hook", "60-mkinitcpio-remove.hook"): + path = hooks_dir / name + try: + if path.is_symlink() and path.readlink() == Path("/dev/null"): + path.unlink() + except OSError: + pass + + def _runtime_package_list(ctx: InstallContext) -> list[str]: """omarchy + every package in install/omarchy-base.packages that isn't already in EARLY_PACKAGES.""" @@ -488,30 +518,34 @@ def arch_install_base(ctx: InstallContext) -> None: if config.mirror_config: installer.set_mirrors(mirror_handler, config.mirror_config, on_target=False) - info("› installing base system (mkinitcpio deferred to configure_protected_boot)") - installer.minimal_installation( - optional_repositories=( - config.mirror_config.optional_repositories - if config.mirror_config else [] - ), - mkinitcpio=False, - hostname=config.hostname, - locale_config=config.locale_config, - pacman_config=config.pacman_config, - ) + _mask_mkinitcpio_pacman_hooks(ctx) + try: + info("› installing base system (mkinitcpio deferred to configure_protected_boot)") + installer.minimal_installation( + optional_repositories=( + config.mirror_config.optional_repositories + if config.mirror_config else [] + ), + mkinitcpio=False, + hostname=config.hostname, + locale_config=config.locale_config, + pacman_config=config.pacman_config, + ) - if config.mirror_config: - installer.set_mirrors(mirror_handler, config.mirror_config, on_target=True) + if config.mirror_config: + installer.set_mirrors(mirror_handler, config.mirror_config, on_target=True) - if config.swap and config.swap.enabled: - installer.setup_swap(algo=config.swap.algorithm) + if config.swap and config.swap.enabled: + installer.setup_swap(algo=config.swap.algorithm) - info(f"› installing early Omarchy packages: {', '.join(EARLY_PACKAGES)}") - installer.add_additional_packages(EARLY_PACKAGES) + info(f"› installing early Omarchy packages: {', '.join(EARLY_PACKAGES)}") + installer.add_additional_packages(EARLY_PACKAGES) - info("› creating user (with /etc/skel populated)") - if config.auth_config and config.auth_config.users: - installer.create_users(config.auth_config.users) + info("› creating user (with /etc/skel populated)") + if config.auth_config and config.auth_config.users: + installer.create_users(config.auth_config.users) + finally: + _unmask_mkinitcpio_pacman_hooks(ctx) info("› installing Omarchy runtime + omarchy-base.packages") installer.add_additional_packages(_runtime_package_list(ctx)) @@ -789,6 +823,30 @@ def _register_efibootmgr_entry( ) +def configure_hibernation(ctx: InstallContext) -> None: + """Configure swap/resume in the target as root before user finalization. + + Hibernation is system boot configuration, not per-user setup. Keep it out + of finalize.sh so the finalizer stays focused on user/session defaults. + The final Limine UKI build still happens later in limine-snapper.sh, after + this writes the resume hook and kernel cmdline drop-in. + """ + setup = ctx.target / "usr" / "bin" / "omarchy-hibernation-setup" + if not setup.exists(): + _debug_log(ctx, "skipping hibernation: /usr/bin/omarchy-hibernation-setup is not installed") + return + + subprocess.run([ + "arch-chroot", str(ctx.target), + "env", + "OMARCHY_INSTALL_MODE=offline", + "OMARCHY_CHROOT_INSTALL=1", + "OMARCHY_PATH=/usr/share/omarchy", + "OMARCHY_INSTALL_LOG_FILE=/var/log/omarchy-install.log", + "/usr/bin/omarchy-hibernation-setup", "--force", "--no-rebuild", + ], check=True) + + def _install_debug_enabled() -> bool: return os.environ.get("OMARCHY_INSTALL_DEBUG") == "1" or Path("/usr/share/omarchy-iso/install-debug").exists()