A bunch of updates for rework

This commit is contained in:
Ryan Hughes
2025-11-16 19:25:52 -05:00
parent 080d287d74
commit 234f670310
8 changed files with 722 additions and 131 deletions

344
bin/omarchy-disk-config Executable file
View File

@@ -0,0 +1,344 @@
#!/usr/bin/env python3
"""
Omarchy Disk Configuration Tool
Interactive partition editor and validator for Omarchy installations.
"""
import json
import sys
from pathlib import Path
def _find_root_partition(disk_config):
for dev_mod in disk_config.device_modifications:
for part in dev_mod.partitions:
if part.mountpoint == Path('/'):
return part
if part.btrfs_subvols:
for subvol in part.btrfs_subvols:
if subvol.mountpoint == Path('/'):
return part
return None
def validate_disk_config(config, interactive=True):
from archinstall.lib.models.device import FilesystemType, Size, Unit, EncryptionType
from archinstall.lib.output import info, warn
if not config.disk_config:
return 'CONTINUE'
validation_warnings = []
boot_partition = None
for dev_mod in config.disk_config.device_modifications:
for part in dev_mod.partitions:
if part.mountpoint == Path('/boot') or part.mountpoint == Path('/efi'):
boot_partition = part
break
if boot_partition:
min_boot_size = Size(2, Unit.GiB, boot_partition.length.sector_size)
if boot_partition.length >= min_boot_size:
size_gb = boot_partition.length.convert(Unit.GiB).value
info(f'✓ Boot partition size: {size_gb:.1f} GiB')
else:
size_mb = boot_partition.length.convert(Unit.MiB).value
warn(f'⚠ Boot partition is only {size_mb:.0f} MiB')
warn(' Omarchy recommends at least 2 GiB for boot partition')
warn(' Multiple kernels may not fit')
validation_warnings.append('boot_size')
else:
warn('⚠ Could not find boot partition (/boot or /efi)')
warn(' System may not boot correctly')
validation_warnings.append('no_boot')
root_partition = _find_root_partition(config.disk_config)
if root_partition:
if root_partition.fs_type == FilesystemType.Btrfs:
info('✓ Root filesystem is btrfs')
if root_partition.btrfs_subvols:
subvol_names = [str(sv.name) for sv in root_partition.btrfs_subvols]
subvol_mounts = {str(sv.mountpoint): str(sv.name) for sv in root_partition.btrfs_subvols}
required_subvols = {
'/': '@',
'/home': '@home',
'/var/log': '@log',
'/var/cache/pacman/pkg': '@pkg',
}
missing_subvols = []
for mount, expected_name in required_subvols.items():
if mount not in subvol_mounts:
missing_subvols.append(f'{expected_name} → {mount}')
elif subvol_mounts[mount] != expected_name:
warn(f'⚠ Subvolume at {mount} is named "{subvol_mounts[mount]}" not "{expected_name}"')
if missing_subvols:
warn(f'⚠ Missing recommended subvolumes: {", ".join(missing_subvols)}')
warn(' Omarchy recommends: @, @home, @log, @pkg')
warn(' Some features (like Snapper) may not work optimally')
info(f' Current subvolumes: {", ".join(subvol_names)}')
validation_warnings.append('missing_subvols')
else:
info(f'✓ Btrfs subvolumes: {", ".join(subvol_names)}')
else:
warn('⚠ Btrfs partition has no subvolumes defined')
warn(' Omarchy recommends subvolumes for snapshots')
warn(' Required: @ (root), @home, @log, @pkg')
validation_warnings.append('no_subvols')
else:
fs_name = root_partition.fs_type.value if root_partition.fs_type else 'unknown'
warn(f'⚠ Root filesystem is {fs_name}, not btrfs')
warn(' Omarchy is designed for btrfs with snapshots')
warn(' Some features may not work correctly')
validation_warnings.append('not_btrfs')
is_encrypted = False
if config.disk_config.disk_encryption:
enc = config.disk_config.disk_encryption
if enc.encryption_type != EncryptionType.NoEncryption:
is_encrypted = root_partition in enc.partitions
if is_encrypted:
info('✓ Root partition is encrypted with LUKS')
if config.disk_config.disk_encryption.iter_time != 2000:
old_time = config.disk_config.disk_encryption.iter_time
config.disk_config.disk_encryption.iter_time = 2000
info(f'✓ Adjusted iteration time: {old_time}ms → 2000ms')
else:
info('✓ Iteration time: 2000ms (optimal)')
else:
warn('⚠ Root partition is NOT encrypted')
warn(' Omarchy recommends LUKS encryption for security')
validation_warnings.append('no_encryption')
else:
warn('⚠ Could not identify root partition')
validation_warnings.append('no_root')
if validation_warnings and interactive:
import subprocess
import shutil
gum_path = shutil.which('gum')
if gum_path:
warn('')
try:
result = subprocess.run(
['gum', 'choose', '--header', 'Validation warnings detected. What would you like to do?',
'Re-edit partitions', 'Continue anyway', 'Abort'],
capture_output=True,
text=True,
)
choice = result.stdout.strip() if result.stdout else ''
if choice == 'Re-edit partitions':
return 'RE_EDIT'
elif choice == 'Abort':
return 'ABORT'
else:
info('Continuing despite warnings')
return 'CONTINUE'
except (subprocess.CalledProcessError, KeyboardInterrupt):
return 'ABORT'
return 'CONTINUE'
def apply_omarchy_partition_defaults():
import archinstall.lib.interactions.disk_conf as disk_conf_module
from archinstall.lib.models.device import (
PartitionModification, ModificationStatus, PartitionType,
Size, Unit, SectorSize, FilesystemType, PartitionFlag,
DeviceModification, BDevice
)
from archinstall.lib.interactions.disk_conf import get_default_btrfs_subvols
from archinstall.lib.disk.device_handler import device_handler
def _boot_partition_2gib(sector_size: SectorSize, using_gpt: bool) -> PartitionModification:
flags = [PartitionFlag.BOOT]
size = Size(2, Unit.GiB, sector_size)
start = Size(1, Unit.MiB, sector_size)
if using_gpt:
flags.append(PartitionFlag.ESP)
return PartitionModification(
status=ModificationStatus.Create,
type=PartitionType.Primary,
start=start,
length=size,
mountpoint=Path('/boot'),
fs_type=FilesystemType.Fat32,
flags=flags,
)
def _select_main_filesystem_btrfs() -> FilesystemType:
return FilesystemType.Btrfs
def _select_mount_options_compressed() -> list[str]:
return ['compress=zstd']
def _suggest_single_disk_auto_subvolumes(
device: BDevice,
filesystem_type: FilesystemType | None = None,
separate_home: bool | None = None,
):
if not filesystem_type:
filesystem_type = FilesystemType.Btrfs
if filesystem_type == FilesystemType.Btrfs:
using_subvolumes = True
mount_options = ['compress=zstd']
else:
using_subvolumes = False
mount_options = []
sector_size = device.device_info.sector_size
device_modification = DeviceModification(device, wipe=True)
using_gpt = device_handler.partition_table.is_gpt()
boot_partition = _boot_partition_2gib(sector_size, using_gpt)
device_modification.add_partition(boot_partition)
total_size = device.device_info.total_size
available_space = total_size - boot_partition.length - Size(1, Unit.MiB, sector_size)
root_partition = PartitionModification(
status=ModificationStatus.Create,
type=PartitionType.Primary,
start=boot_partition.start + boot_partition.length,
length=available_space,
mountpoint=None if using_subvolumes else Path('/'),
fs_type=filesystem_type,
mount_options=mount_options,
)
if using_subvolumes:
root_partition.btrfs_subvols = get_default_btrfs_subvols()
device_modification.add_partition(root_partition)
return device_modification
disk_conf_module._boot_partition = _boot_partition_2gib
disk_conf_module.select_main_filesystem_format = _select_main_filesystem_btrfs
disk_conf_module.select_mount_options = _select_mount_options_compressed
disk_conf_module.suggest_single_disk_layout = _suggest_single_disk_auto_subvolumes
def load_config(config_file: Path, creds_file: Path | None = None):
from archinstall.lib.args import ArchConfig, Arguments
with open(config_file) as f:
config_data = json.load(f)
if creds_file and creds_file.exists():
with open(creds_file) as f:
creds_data = json.load(f)
config_data.update(creds_data)
args = Arguments(
config=config_file,
creds=creds_file,
mountpoint=Path('/mnt'),
silent=True,
)
return ArchConfig.from_config(config_data, args)
def save_config(config, output_file: Path):
from archinstall.lib.output import info
try:
config_dict = config.safe_json()
with open(output_file, 'w') as f:
json.dump(config_dict, f, indent=2, default=str)
info(f'✓ Configuration saved to: {output_file}')
except Exception as e:
from archinstall.lib.output import error
error(f'Failed to save config: {e}')
raise
def main():
import argparse
parser = argparse.ArgumentParser(description='Omarchy Disk Configuration Tool')
parser.add_argument('--config', type=Path, required=True, help='Path to config file')
parser.add_argument('--creds', type=Path, help='Path to credentials file')
parser.add_argument('--output', type=Path, help='Output path (default: overwrites input)')
parser.add_argument('--non-interactive', action='store_true', help='Skip interactive prompts')
args = parser.parse_args()
output_file = args.output or args.config
if not args.config.exists():
print(f'ERROR: Config file not found: {args.config}', file=sys.stderr)
sys.exit(1)
if args.creds and not args.creds.exists():
print(f'ERROR: Credentials file not found: {args.creds}', file=sys.stderr)
sys.exit(1)
try:
from archinstall.lib.output import info, error
from archinstall.lib.disk.disk_menu import DiskLayoutConfigurationMenu
from archinstall.tui.curses_menu import Tui
apply_omarchy_partition_defaults()
info('Loading configuration...')
config = load_config(args.config, args.creds)
if not config.disk_config:
error('No disk configuration found in config file')
sys.exit(1)
while True:
info('Launching partition editor...')
with Tui():
edited_disk_config = DiskLayoutConfigurationMenu(config.disk_config).run()
if edited_disk_config:
config.disk_config = edited_disk_config
info('✓ Partition configuration updated')
else:
info('No changes made in partition editor')
interactive = not args.non_interactive
validation_result = validate_disk_config(config, interactive=interactive)
if validation_result == 'RE_EDIT':
continue
elif validation_result == 'ABORT':
info('Disk configuration cancelled by user')
sys.exit(1)
else:
break
save_config(config, output_file)
info('✓ Disk configuration complete!')
except KeyboardInterrupt:
print('\nCancelled by user', file=sys.stderr)
sys.exit(1)
except Exception as e:
print(f'ERROR: {e}', file=sys.stderr)
import traceback
traceback.print_exc()
sys.exit(1)
if __name__ == '__main__':
main()

258
bin/omarchy-install Executable file
View File

@@ -0,0 +1,258 @@
#!/usr/bin/env python3
"""
Omarchy Install
Installs Arch Linux with Omarchy customizations using archinstall as a library.
For disk configuration, use omarchy-disk-config.
"""
import json
import sys
import time
from pathlib import Path
def load_config(config_file: Path, creds_file: Path | None = None):
from archinstall.lib.args import ArchConfig, Arguments
with open(config_file) as f:
config_data = json.load(f)
if creds_file and creds_file.exists():
with open(creds_file) as f:
creds_data = json.load(f)
config_data.update(creds_data)
args = Arguments(
config=config_file,
creds=creds_file,
mountpoint=Path('/mnt'),
silent=True,
skip_ntp=True,
skip_wkd=True,
)
return ArchConfig.from_config(config_data, args)
def load_omarchy_packages() -> list[str]:
omarchy_path = Path('/usr/share/omarchy/install')
packages = []
base_packages_file = omarchy_path / 'omarchy-base.packages'
if base_packages_file.exists():
with open(base_packages_file) as f:
packages.extend([line.strip() for line in f if line.strip() and not line.startswith('#')])
return packages
def perform_installation(config_file: Path, creds_file: Path | None = None) -> None:
from archinstall.lib.disk.filesystem import FilesystemHandler
from archinstall.lib.installer import Installer
from archinstall.lib.models.device import DiskLayoutType, EncryptionType
from archinstall.lib.output import error, info
from archinstall.lib.profile.profiles_handler import profile_handler
from archinstall.lib.authentication.authentication_handler import auth_handler
start_time = time.time()
info('Loading configuration...')
config = load_config(config_file, creds_file)
if not config.disk_config:
error('No disk configuration found in config file')
error('Use omarchy-disk-config to configure disk layout first')
sys.exit(1)
disk_config = config.disk_config
if disk_config.config_type != DiskLayoutType.Pre_mount:
info('Performing filesystem operations...')
fs_handler = FilesystemHandler(disk_config)
fs_handler.perform_filesystem_operations()
mountpoint = Path('/mnt')
info('Loading Omarchy base packages...')
omarchy_packages = load_omarchy_packages()
info('Starting Omarchy installation...')
with Installer(
mountpoint,
disk_config,
kernels=config.kernels or ['linux'],
) as installation:
info('Mounting filesystems...')
if disk_config.config_type != DiskLayoutType.Pre_mount:
installation.mount_ordered_layout()
installation.sanity_check()
if disk_config.disk_encryption and disk_config.disk_encryption.encryption_type != EncryptionType.NoEncryption:
info('Generating encryption keys...')
installation.generate_key_files()
if config.mirror_config:
info('Configuring mirrors...')
installation.set_mirrors(config.mirror_config, on_target=False)
info('Installing base system...')
installation.minimal_installation(
optional_repositories=config.mirror_config.optional_repositories if config.mirror_config else [],
mkinitcpio=not config.uki,
hostname=config.hostname,
locale_config=config.locale_config,
)
if config.mirror_config:
installation.set_mirrors(config.mirror_config, on_target=True)
if config.swap:
info('Setting up swap...')
installation.setup_swap('zram')
all_packages = omarchy_packages + (config.packages or [])
if all_packages:
info(f'Installing {len(all_packages)} packages...')
installation.add_additional_packages(all_packages)
if config.bootloader:
info(f'Installing bootloader: {config.bootloader.value}...')
installation.add_bootloader(config.bootloader, config.uki)
if config.network_config:
info('Configuring network...')
config.network_config.install_network_config(installation, config.profile_config)
if config.auth_config and config.auth_config.users:
info('Creating users...')
installation.create_users(config.auth_config.users)
auth_handler.setup_auth(installation, config.auth_config, config.hostname)
if config.app_config:
info('Installing applications...')
from archinstall.lib.applications.application_handler import application_handler
application_handler.install_applications(installation, config.app_config)
if config.profile_config:
info('Installing profile...')
profile_handler.install_profile_config(installation, config.profile_config)
if config.timezone:
installation.set_timezone(config.timezone)
if config.ntp:
installation.activate_time_synchronization()
from archinstall.lib.installer import accessibility_tools_in_use
if accessibility_tools_in_use():
installation.enable_espeakup()
if config.auth_config and config.auth_config.root_enc_password:
from archinstall.lib.models.users import User
root_user = User('root', config.auth_config.root_enc_password, False)
installation.set_user_password(root_user)
if config.profile_config and config.profile_config.profile:
config.profile_config.profile.post_install(installation)
if config.services:
info('Enabling services...')
installation.enable_service(config.services)
if disk_config.has_default_btrfs_vols():
btrfs_options = disk_config.btrfs_options
snapshot_config = btrfs_options.snapshot_config if btrfs_options else None
snapshot_type = snapshot_config.snapshot_type if snapshot_config else None
if snapshot_type:
installation.setup_btrfs_snapshot(snapshot_type, config.bootloader)
info('Mounting offline resources for chroot access...')
from archinstall.lib.general import SysCommand
import os
import shutil
offline_mirror_src = Path('/var/cache/omarchy/mirror/offline')
offline_mirror_dst = mountpoint / 'var/cache/omarchy/mirror/offline'
packages_src = Path('/opt/packages')
packages_dst = mountpoint / 'opt/packages'
os.makedirs(offline_mirror_dst, exist_ok=True)
os.makedirs(packages_dst, exist_ok=True)
if offline_mirror_src.exists():
SysCommand(f'mount --bind {offline_mirror_src} {offline_mirror_dst}')
if packages_src.exists():
SysCommand(f'mount --bind {packages_src} {packages_dst}')
pacman_conf_src = Path('/etc/pacman.conf')
pacman_conf_dst = mountpoint / 'etc/pacman.conf'
if pacman_conf_src.exists():
shutil.copy(pacman_conf_src, pacman_conf_dst)
info('Copying user info to chroot...')
os.makedirs(mountpoint / 'tmp', exist_ok=True)
if os.path.exists('/tmp/omarchy-user-name.txt'):
shutil.copy('/tmp/omarchy-user-name.txt', mountpoint / 'tmp/omarchy-user-name.txt')
if os.path.exists('/tmp/omarchy-user-email.txt'):
shutil.copy('/tmp/omarchy-user-email.txt', mountpoint / 'tmp/omarchy-user-email.txt')
if config.custom_commands:
info('Running Omarchy custom commands...')
from archinstall.lib.installer import run_custom_user_commands
run_custom_user_commands(config.custom_commands, installation)
info('Generating fstab...')
installation.genfstab()
end_time = time.time()
duration_seconds = int(end_time - start_time)
duration_mins = duration_seconds // 60
duration_secs = duration_seconds % 60
timing_file = mountpoint / 'tmp/omarchy-install-time.txt'
with open(timing_file, 'w') as f:
f.write(f"{duration_mins}m {duration_secs}s\n")
info(f'Installation complete! Total time: {duration_mins}m {duration_secs}s')
def main():
import argparse
parser = argparse.ArgumentParser(description='Omarchy Install')
parser.add_argument('--config', type=Path, required=True, help='Path to config file')
parser.add_argument('--creds', type=Path, help='Path to credentials file')
args = parser.parse_args()
if not args.config.exists():
print(f'ERROR: Config file not found: {args.config}', file=sys.stderr)
sys.exit(1)
if args.creds and not args.creds.exists():
print(f'ERROR: Credentials file not found: {args.creds}', file=sys.stderr)
sys.exit(1)
try:
perform_installation(
config_file=args.config,
creds_file=args.creds,
)
except KeyboardInterrupt:
print('\nInstallation cancelled by user', file=sys.stderr)
sys.exit(1)
except Exception as e:
print(f'ERROR: Installation failed: {e}', file=sys.stderr)
import traceback
traceback.print_exc()
sys.exit(1)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,83 @@
#!/bin/bash
#
# Omarchy Final Configurations Installer
#
# This script runs from archinstall's custom_commands after base packages
# and user creation. It switches to the created user and runs install.sh
# to complete package installation and system configuration.
#
# archinstall runs custom_commands as root via:
# arch-chroot -S /mnt bash /var/tmp/user-command.0.sh
#
set -eEo pipefail
# Find the first non-root user (UID >= 1000, < 60000)
OMARCHY_USER=$(getent passwd | awk -F: '$3 >= 1000 && $3 < 60000 {print $1; exit}')
if [[ -z "$OMARCHY_USER" ]]; then
echo "ERROR: No non-root user found!"
echo "Users created:"
getent passwd | awk -F: '$3 >= 1000 {print $1, $3}'
exit 1
fi
echo "Setting up Omarchy for user: $OMARCHY_USER"
# Setup passwordless sudo (will be removed by post-install)
echo "Setting up passwordless sudo..."
mkdir -p /etc/sudoers.d
cat >/etc/sudoers.d/99-omarchy-installer <<EOF
root ALL=(ALL:ALL) NOPASSWD: ALL
%wheel ALL=(ALL:ALL) NOPASSWD: ALL
$OMARCHY_USER ALL=(ALL:ALL) NOPASSWD: ALL
EOF
chmod 440 /etc/sudoers.d/99-omarchy-installer
# Get user info from /tmp (written by configurator)
if [[ -f /tmp/omarchy-user-name.txt ]]; then
OMARCHY_USER_NAME=$(cat /tmp/omarchy-user-name.txt)
else
OMARCHY_USER_NAME=""
fi
if [[ -f /tmp/omarchy-user-email.txt ]]; then
OMARCHY_USER_EMAIL=$(cat /tmp/omarchy-user-email.txt)
else
OMARCHY_USER_EMAIL=""
fi
# Run install.sh as the user
echo "========================================"
echo "Running Omarchy installation as user: $OMARCHY_USER"
echo "========================================"
echo
# Use runuser instead of su for better output handling
# runuser doesn't go through PAM and preserves stdout/stderr better
runuser -u "$OMARCHY_USER" -- bash -c "
set -eEo pipefail
export PYTHONUNBUFFERED=1
export OMARCHY_CHROOT_INSTALL=1
export OMARCHY_ARCHINSTALL_WRAPPER=1
export OMARCHY_USER='$OMARCHY_USER'
export OMARCHY_USER_NAME='$OMARCHY_USER_NAME'
export OMARCHY_USER_EMAIL='$OMARCHY_USER_EMAIL'
cd ~
source /usr/share/omarchy/install.sh
"
exit_code=$?
if [[ $exit_code -eq 0 ]]; then
echo
echo "========================================"
echo "Omarchy install.sh completed successfully!"
echo "========================================"
else
echo
echo "========================================"
echo "ERROR: Omarchy install.sh exited with code $exit_code"
echo "========================================"
exit $exit_code
fi

View File

@@ -6,11 +6,33 @@ set -eEo pipefail
# Define Omarchy locations
export OMARCHY_PATH="/usr/share/omarchy"
export OMARCHY_INSTALL="$OMARCHY_PATH/install"
export OMARCHY_INSTALL_LOG_FILE="/var/log/omarchy-install.log"
# Install
source "$OMARCHY_INSTALL/helpers/all.sh"
source "$OMARCHY_INSTALL/preflight/all.sh"
# Load helpers
source "$OMARCHY_INSTALL/helpers/chroot.sh"
# Simple script runner that outputs to stdout/stderr
# archinstall captures all output in /var/log/archinstall/install.log
run_logged() {
local script="$1"
local script_name=$(basename "$script")
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "[$(date '+%H:%M:%S')] Running: $script_name"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
source "$script"
local exit_code=$?
if [ $exit_code -eq 0 ]; then
echo "✓ Completed: $script_name"
echo
else
echo "✗ Failed: $script_name (exit code: $exit_code)"
return $exit_code
fi
}
# Run installation phases
source "$OMARCHY_INSTALL/packaging/all.sh"
source "$OMARCHY_INSTALL/config/all.sh"
source "$OMARCHY_INSTALL/login/all.sh"

View File

@@ -1,4 +1,5 @@
source $OMARCHY_INSTALL/helpers/chroot.sh
# Helper functions for ISO/non-chroot usage
# These are used by .automated_script.sh on the ISO
source $OMARCHY_INSTALL/helpers/presentation.sh
source $OMARCHY_INSTALL/helpers/errors.sh
source $OMARCHY_INSTALL/helpers/logging.sh
source $OMARCHY_INSTALL/helpers/errors.sh

View File

@@ -1,3 +1,5 @@
# Log output UI for .automated_script.sh
# Tails /var/log/omarchy-install.log and displays it with pretty formatting
start_log_output() {
local ANSI_SAVE_CURSOR="\033[s"
local ANSI_RESTORE_CURSOR="\033[u"
@@ -5,6 +7,8 @@ start_log_output() {
local ANSI_HIDE_CURSOR="\033[?25l"
local ANSI_RESET="\033[0m"
local ANSI_GRAY="\033[90m"
local log_file="${1:-/var/log/omarchy-install.log}"
# Save cursor position and hide cursor
printf $ANSI_SAVE_CURSOR
@@ -16,7 +20,7 @@ start_log_output() {
while true; do
# Read the last N lines into an array
mapfile -t current_lines < <(tail -n $log_lines "$OMARCHY_INSTALL_LOG_FILE" 2>/dev/null)
mapfile -t current_lines < <(tail -n $log_lines "$log_file" 2>/dev/null)
# Build complete output buffer with escape sequences
output=""
@@ -51,84 +55,3 @@ stop_log_output() {
unset monitor_pid
fi
}
start_install_log() {
sudo touch "$OMARCHY_INSTALL_LOG_FILE"
sudo chmod 666 "$OMARCHY_INSTALL_LOG_FILE"
export OMARCHY_START_TIME=$(date '+%Y-%m-%d %H:%M:%S')
echo "=== Omarchy Installation Started: $OMARCHY_START_TIME ===" >>"$OMARCHY_INSTALL_LOG_FILE"
start_log_output
}
stop_install_log() {
stop_log_output
show_cursor
if [[ -n ${OMARCHY_INSTALL_LOG_FILE:-} ]]; then
OMARCHY_END_TIME=$(date '+%Y-%m-%d %H:%M:%S')
echo "=== Omarchy Installation Completed: $OMARCHY_END_TIME ===" >>"$OMARCHY_INSTALL_LOG_FILE"
echo "" >>"$OMARCHY_INSTALL_LOG_FILE"
echo "=== Installation Time Summary ===" >>"$OMARCHY_INSTALL_LOG_FILE"
if [ -f "/var/log/archinstall/install.log" ]; then
ARCHINSTALL_START=$(grep -m1 '^\[' /var/log/archinstall/install.log 2>/dev/null | sed 's/^\[\([^]]*\)\].*/\1/' || true)
ARCHINSTALL_END=$(grep 'Installation completed without any errors' /var/log/archinstall/install.log 2>/dev/null | sed 's/^\[\([^]]*\)\].*/\1/' || true)
if [ -n "$ARCHINSTALL_START" ] && [ -n "$ARCHINSTALL_END" ]; then
ARCH_START_EPOCH=$(date -d "$ARCHINSTALL_START" +%s)
ARCH_END_EPOCH=$(date -d "$ARCHINSTALL_END" +%s)
ARCH_DURATION=$((ARCH_END_EPOCH - ARCH_START_EPOCH))
ARCH_MINS=$((ARCH_DURATION / 60))
ARCH_SECS=$((ARCH_DURATION % 60))
echo "Archinstall: ${ARCH_MINS}m ${ARCH_SECS}s" >>"$OMARCHY_INSTALL_LOG_FILE"
fi
fi
if [ -n "$OMARCHY_START_TIME" ]; then
OMARCHY_START_EPOCH=$(date -d "$OMARCHY_START_TIME" +%s)
OMARCHY_END_EPOCH=$(date -d "$OMARCHY_END_TIME" +%s)
OMARCHY_DURATION=$((OMARCHY_END_EPOCH - OMARCHY_START_EPOCH))
OMARCHY_MINS=$((OMARCHY_DURATION / 60))
OMARCHY_SECS=$((OMARCHY_DURATION % 60))
echo "Omarchy: ${OMARCHY_MINS}m ${OMARCHY_SECS}s" >>"$OMARCHY_INSTALL_LOG_FILE"
if [ -n "$ARCH_DURATION" ]; then
TOTAL_DURATION=$((ARCH_DURATION + OMARCHY_DURATION))
TOTAL_MINS=$((TOTAL_DURATION / 60))
TOTAL_SECS=$((TOTAL_DURATION % 60))
echo "Total: ${TOTAL_MINS}m ${TOTAL_SECS}s" >>"$OMARCHY_INSTALL_LOG_FILE"
fi
fi
echo "=================================" >>"$OMARCHY_INSTALL_LOG_FILE"
echo "Rebooting system..." >>"$OMARCHY_INSTALL_LOG_FILE"
fi
}
run_logged() {
local script="$1"
export CURRENT_SCRIPT="$script"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Starting: $script" >>"$OMARCHY_INSTALL_LOG_FILE"
# Use bash -c to create a clean subshell
bash -c "source '$script'" </dev/null >>"$OMARCHY_INSTALL_LOG_FILE" 2>&1
local exit_code=$?
if [ $exit_code -eq 0 ]; then
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Completed: $script" >>"$OMARCHY_INSTALL_LOG_FILE"
unset CURRENT_SCRIPT
else
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Failed: $script (exit code: $exit_code)" >>"$OMARCHY_INSTALL_LOG_FILE"
fi
return $exit_code
}

View File

@@ -1,43 +1,7 @@
# Handle chroot install completion (non-interactive)
if [[ -n "${OMARCHY_CHROOT_INSTALL:-}" ]]; then
echo "[finished] Chroot installation completed, creating marker file"
if sudo test -f /etc/sudoers.d/99-omarchy-installer; then
sudo rm -f /etc/sudoers.d/99-omarchy-installer &>/dev/null
fi
touch /var/tmp/omarchy-install-completed
exit 0
fi
# Normal (non-chroot) finish
stop_install_log
echo_in_style() {
echo "$1" | tte --canvas-width 0 --anchor-text c --frame-rate 640 print
}
clear
echo
tte -i /usr/share/omarchy/logo.txt --canvas-width 0 --anchor-text c --frame-rate 920 laseretch
echo
# Display installation time if available
if [[ -f $OMARCHY_INSTALL_LOG_FILE ]] && grep -q "Total:" "$OMARCHY_INSTALL_LOG_FILE" 2>/dev/null; then
echo
TOTAL_TIME=$(tail -n 20 "$OMARCHY_INSTALL_LOG_FILE" | grep "^Total:" | sed 's/^Total:[[:space:]]*//')
if [ -n "$TOTAL_TIME" ]; then
echo_in_style "Installed in $TOTAL_TIME"
fi
else
echo_in_style "Finished installing"
fi
# Installation completed in chroot
echo "[finished] Chroot installation completed successfully"
# Clean up installer sudoers
if sudo test -f /etc/sudoers.d/99-omarchy-installer; then
sudo rm -f /etc/sudoers.d/99-omarchy-installer &>/dev/null
fi
# Exit gracefully if user chooses not to reboot
if gum confirm --padding "0 0 0 $((PADDING_LEFT + 32))" --show-help=false --default --affirmative "Reboot Now" --negative "" ""; then
# Clear screen to hide any shutdown messages
clear
sudo reboot 2>/dev/null
fi

View File

@@ -1,4 +0,0 @@
clear_logo
gum style --foreground 3 --padding "1 0 0 $PADDING_LEFT" "Installing..."
echo
start_install_log