refactor: major architectural restructure for theme-centric organization
Theme System: - Move all theme app configs to apps/ subdirectory (20 themes) - Add theme-loader.nix for dynamic theme config deployment - Simplify stylix.nix to focus on base theming only Override System: - Add overrides.nix for file-based config overrides - Add behavior-configs.nix for non-visual configuration - Split hypr/nomarchy.conf into behavior vs visual sections Module Improvements: - Add lib.mkDefault to all customizable settings - Add modules/lib/ with shared utilities and state schema - Update all home and system modules for downstream overridability Installer: - New minimal TTY installer (installer/install.sh) - Golden path: BTRFS + LUKS2 (disko-golden.nix) - New installer-iso.nix for TTY-only installation - Keep graphical installer as installerIsoGraphical option Cleanup: - Remove obsolete install.sh, disko-ext4.nix, install-nomarchy.sh - Update live-iso.nix references - Add .claude/ to .gitignore for local IDE settings Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
523
installer/install.sh
Executable file
523
installer/install.sh
Executable file
@@ -0,0 +1,523 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
# Nomarchy TTY Installer
|
||||
# Golden path: BTRFS + LUKS2 encryption
|
||||
#
|
||||
# This is a minimal, single-path installer designed for TTY-only environments.
|
||||
# For a customized installation, manually set up your disk and use the generated
|
||||
# flake configuration as a starting point.
|
||||
|
||||
# Colors and styling
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
YELLOW='\033[1;33m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m' # No Color
|
||||
BOLD='\033[1m'
|
||||
|
||||
# Installer state
|
||||
NOMARCHY_REPO=""
|
||||
TARGET_DRIVE=""
|
||||
USERNAME=""
|
||||
USER_PASSWORD=""
|
||||
TIMEZONE="UTC"
|
||||
HARDWARE_MODULES=""
|
||||
NOMARCHY_HW_OPTS=""
|
||||
ENABLE_IMPERMANENCE="false"
|
||||
|
||||
# ============================================================================
|
||||
# UTILITY FUNCTIONS
|
||||
# ============================================================================
|
||||
|
||||
header() {
|
||||
clear
|
||||
gum style \
|
||||
--foreground 212 --border-foreground 212 --border double \
|
||||
--align center --width 60 --margin "1 2" --padding "2 4" \
|
||||
"NOMARCHY INSTALLER" "NixOS with Omarchy flavor"
|
||||
echo ""
|
||||
}
|
||||
|
||||
section() {
|
||||
echo ""
|
||||
gum style --foreground 14 --bold "━━━ $1 ━━━"
|
||||
echo ""
|
||||
}
|
||||
|
||||
success() {
|
||||
gum style --foreground 10 "✓ $1"
|
||||
}
|
||||
|
||||
error() {
|
||||
gum style --foreground 9 "✗ $1"
|
||||
}
|
||||
|
||||
info() {
|
||||
gum style --foreground 12 "→ $1"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# STEP 1: ENVIRONMENT CHECK
|
||||
# ============================================================================
|
||||
|
||||
check_environment() {
|
||||
section "Environment Check"
|
||||
|
||||
# Check for root
|
||||
if [[ $EUID -ne 0 ]]; then
|
||||
error "This installer must be run as root (use sudo)"
|
||||
exit 1
|
||||
fi
|
||||
success "Running as root"
|
||||
|
||||
# Find Nomarchy repo
|
||||
if [[ -d "/etc/nomarchy" ]]; then
|
||||
NOMARCHY_REPO="/etc/nomarchy"
|
||||
elif [[ -d "$(dirname "$0")/.." ]] && [[ -f "$(dirname "$0")/../flake.nix" ]]; then
|
||||
NOMARCHY_REPO="$(realpath "$(dirname "$0")/..")"
|
||||
fi
|
||||
|
||||
if [[ -z "$NOMARCHY_REPO" ]]; then
|
||||
error "Nomarchy repository not found"
|
||||
exit 1
|
||||
fi
|
||||
success "Found Nomarchy at $NOMARCHY_REPO"
|
||||
|
||||
# Check internet
|
||||
gum spin --spinner dot --title "Checking internet connection..." -- sleep 1
|
||||
while ! ping -c 1 -W 2 1.1.1.1 &>/dev/null; do
|
||||
error "No internet connection"
|
||||
local choice
|
||||
choice=$(gum choose "Open Network Manager (nmtui)" "Retry" "Exit")
|
||||
case "$choice" in
|
||||
*nmtui*) nmtui ;;
|
||||
*Exit*) exit 1 ;;
|
||||
esac
|
||||
done
|
||||
success "Internet connection verified"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# STEP 2: DISK SELECTION
|
||||
# ============================================================================
|
||||
|
||||
select_disk() {
|
||||
section "Disk Selection"
|
||||
|
||||
info "Available drives:"
|
||||
echo ""
|
||||
lsblk -d -n -p -o NAME,SIZE,MODEL | grep -v loop
|
||||
echo ""
|
||||
|
||||
local drives
|
||||
drives=$(lsblk -d -n -p -o NAME,SIZE | grep -v loop)
|
||||
TARGET_DRIVE=$(echo "$drives" | gum choose --header "Select target drive" | awk '{print $1}')
|
||||
|
||||
if [[ -z "$TARGET_DRIVE" ]]; then
|
||||
error "No drive selected"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
gum style --foreground 9 --bold "⚠ WARNING: All data on $TARGET_DRIVE will be DESTROYED!"
|
||||
echo ""
|
||||
|
||||
if ! gum confirm "Are you sure you want to use $TARGET_DRIVE?"; then
|
||||
error "Aborted"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
success "Selected: $TARGET_DRIVE"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# STEP 3: LUKS PASSPHRASE
|
||||
# ============================================================================
|
||||
|
||||
get_luks_passphrase() {
|
||||
section "Disk Encryption"
|
||||
|
||||
info "Your disk will be encrypted with LUKS2."
|
||||
info "Enter a strong passphrase (you'll need this at every boot)."
|
||||
echo ""
|
||||
|
||||
local pass1 pass2
|
||||
while true; do
|
||||
pass1=$(gum input --password --placeholder "Enter LUKS passphrase")
|
||||
[[ -z "$pass1" ]] && continue
|
||||
|
||||
pass2=$(gum input --password --placeholder "Confirm passphrase")
|
||||
|
||||
if [[ "$pass1" == "$pass2" ]]; then
|
||||
USER_PASSWORD="$pass1"
|
||||
break
|
||||
else
|
||||
error "Passphrases do not match. Try again."
|
||||
fi
|
||||
done
|
||||
|
||||
success "Encryption passphrase set"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# STEP 4: USER CONFIGURATION
|
||||
# ============================================================================
|
||||
|
||||
configure_user() {
|
||||
section "User Configuration"
|
||||
|
||||
USERNAME=$(gum input --placeholder "Enter username (lowercase, no spaces)")
|
||||
if [[ -z "$USERNAME" ]] || [[ ! "$USERNAME" =~ ^[a-z][a-z0-9_-]*$ ]]; then
|
||||
error "Invalid username"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
success "Username: $USERNAME"
|
||||
|
||||
# User password (can be same as LUKS or different)
|
||||
info "Set a password for your user account"
|
||||
local pass1 pass2
|
||||
while true; do
|
||||
pass1=$(gum input --password --placeholder "Enter user password")
|
||||
[[ -z "$pass1" ]] && continue
|
||||
|
||||
pass2=$(gum input --password --placeholder "Confirm user password")
|
||||
|
||||
if [[ "$pass1" == "$pass2" ]]; then
|
||||
USER_PASSWORD="$pass1"
|
||||
break
|
||||
else
|
||||
error "Passwords do not match. Try again."
|
||||
fi
|
||||
done
|
||||
|
||||
success "User password set"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# STEP 5: TIMEZONE
|
||||
# ============================================================================
|
||||
|
||||
select_timezone() {
|
||||
section "Timezone"
|
||||
|
||||
local timezones
|
||||
timezones=$(timedatectl list-timezones 2>/dev/null || echo "UTC")
|
||||
TIMEZONE=$(echo "$timezones" | gum filter --placeholder "Search timezone...")
|
||||
|
||||
[[ -z "$TIMEZONE" ]] && TIMEZONE="UTC"
|
||||
success "Timezone: $TIMEZONE"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# STEP 6: HARDWARE VENDOR
|
||||
# ============================================================================
|
||||
|
||||
select_hardware() {
|
||||
section "Hardware Configuration"
|
||||
|
||||
local product_name cpu_vendor
|
||||
product_name=$(cat /sys/class/dmi/id/product_name 2>/dev/null || echo "Unknown")
|
||||
cpu_vendor=$(lscpu 2>/dev/null | grep "Vendor ID" | awk '{print $3}' || echo "Unknown")
|
||||
|
||||
info "Detected: $product_name"
|
||||
info "CPU: $cpu_vendor"
|
||||
echo ""
|
||||
|
||||
# Set CPU-specific module
|
||||
if [[ "$cpu_vendor" == "AuthenticAMD" ]]; then
|
||||
HARDWARE_MODULES="inputs.nixos-hardware.nixosModules.common-cpu-amd"
|
||||
elif [[ "$cpu_vendor" == "GenuineIntel" ]]; then
|
||||
HARDWARE_MODULES="inputs.nixos-hardware.nixosModules.common-cpu-intel"
|
||||
fi
|
||||
|
||||
local vendor
|
||||
vendor=$(gum choose --header "Select hardware vendor" \
|
||||
"Generic (Auto-detect)" \
|
||||
"Framework" \
|
||||
"Dell" \
|
||||
"Lenovo" \
|
||||
"Apple (T2 Mac)" \
|
||||
"Microsoft Surface" \
|
||||
"Other...")
|
||||
|
||||
case "$vendor" in
|
||||
*Framework*)
|
||||
local model
|
||||
model=$(gum choose "16-7040-amd" "13-7040-amd" "13-intel-13th-gen" "13-intel-12th-gen")
|
||||
HARDWARE_MODULES="$HARDWARE_MODULES\n inputs.nixos-hardware.nixosModules.framework-$model"
|
||||
NOMARCHY_HW_OPTS="nomarchy.hardware.isFramework = true;"
|
||||
;;
|
||||
*Dell*)
|
||||
local model
|
||||
model=$(gum choose "xps-15-9500" "xps-15-9510" "xps-13-9310" "xps-13-9380" "precision-5530")
|
||||
HARDWARE_MODULES="$HARDWARE_MODULES\n inputs.nixos-hardware.nixosModules.dell-$model"
|
||||
[[ "$model" == *"xps"* ]] && NOMARCHY_HW_OPTS="nomarchy.hardware.isXPS = true;"
|
||||
;;
|
||||
*Lenovo*)
|
||||
local model
|
||||
model=$(gum choose "thinkpad-x1-carbon-gen10" "thinkpad-t14-amd" "thinkpad-t480" "thinkpad-x1-extreme")
|
||||
HARDWARE_MODULES="$HARDWARE_MODULES\n inputs.nixos-hardware.nixosModules.lenovo-$model"
|
||||
;;
|
||||
*Apple*)
|
||||
NOMARCHY_HW_OPTS="nomarchy.hardware.isT2Mac = true;"
|
||||
;;
|
||||
*Surface*)
|
||||
local model
|
||||
model=$(gum choose "surface-pro-9" "surface-pro-8" "surface-laptop-4")
|
||||
HARDWARE_MODULES="$HARDWARE_MODULES\n inputs.nixos-hardware.nixosModules.microsoft-$model"
|
||||
;;
|
||||
*Other*)
|
||||
info "Enter nixos-hardware module path (or leave empty):"
|
||||
local custom_mod
|
||||
custom_mod=$(gum input --placeholder "e.g., inputs.nixos-hardware.nixosModules.asus-zephyrus-ga401")
|
||||
[[ -n "$custom_mod" ]] && HARDWARE_MODULES="$HARDWARE_MODULES\n $custom_mod"
|
||||
;;
|
||||
esac
|
||||
|
||||
success "Hardware configuration set"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# STEP 7: IMPERMANENCE (OPTIONAL)
|
||||
# ============================================================================
|
||||
|
||||
configure_impermanence() {
|
||||
section "Impermanence (Optional)"
|
||||
|
||||
info "Impermanence erases your root filesystem on every boot."
|
||||
info "Only explicitly persisted files survive reboots."
|
||||
info "This provides a clean, reproducible system."
|
||||
echo ""
|
||||
|
||||
if gum confirm "Enable Impermanence?"; then
|
||||
ENABLE_IMPERMANENCE="true"
|
||||
success "Impermanence enabled"
|
||||
else
|
||||
info "Impermanence disabled (traditional persistent root)"
|
||||
fi
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# STEP 8: REVIEW & CONFIRM
|
||||
# ============================================================================
|
||||
|
||||
review_configuration() {
|
||||
section "Review Configuration"
|
||||
|
||||
echo " Drive: $TARGET_DRIVE (BTRFS + LUKS2)"
|
||||
echo " Username: $USERNAME"
|
||||
echo " Timezone: $TIMEZONE"
|
||||
echo " Impermanence: $ENABLE_IMPERMANENCE"
|
||||
echo ""
|
||||
|
||||
gum style --foreground 9 "This will DESTROY all data on $TARGET_DRIVE"
|
||||
echo ""
|
||||
|
||||
if ! gum confirm "Proceed with installation?"; then
|
||||
error "Aborted"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# STEP 9: EXECUTION
|
||||
# ============================================================================
|
||||
|
||||
execute_installation() {
|
||||
section "Installing Nomarchy"
|
||||
|
||||
# 9.1 Partition with disko
|
||||
info "Partitioning disk..."
|
||||
local escaped_drive disko_file tmp_disko
|
||||
escaped_drive=$(printf '%s\n' "$TARGET_DRIVE" | sed 's/[[\.*^$()+?{|]/\\&/g')
|
||||
disko_file="$NOMARCHY_REPO/installer/disko-golden.nix"
|
||||
tmp_disko=$(mktemp --suffix=.nix)
|
||||
|
||||
sed "s|@TARGET_DRIVE@|$escaped_drive|g" "$disko_file" > "$tmp_disko"
|
||||
|
||||
# For LUKS, we need to provide the passphrase
|
||||
echo -n "$USER_PASSWORD" | disko --mode disko "$tmp_disko"
|
||||
success "Disk partitioned"
|
||||
|
||||
# 9.2 Generate hardware config
|
||||
info "Generating hardware configuration..."
|
||||
mkdir -p /mnt/etc/nixos
|
||||
nixos-generate-config --root /mnt
|
||||
success "Hardware configuration generated"
|
||||
|
||||
# 9.3 Generate flake configuration
|
||||
info "Creating system configuration..."
|
||||
generate_flake_config
|
||||
success "Configuration generated"
|
||||
|
||||
# 9.4 Initialize git repo
|
||||
info "Initializing git repository..."
|
||||
(
|
||||
cd /mnt/etc/nixos
|
||||
git init -q
|
||||
git add .
|
||||
git config user.name "Nomarchy Installer"
|
||||
git config user.email "installer@nomarchy"
|
||||
git commit -qm "Initial Nomarchy configuration"
|
||||
)
|
||||
success "Git repository initialized"
|
||||
|
||||
# 9.5 Handle impermanence
|
||||
if [[ "$ENABLE_IMPERMANENCE" == "true" ]]; then
|
||||
info "Setting up impermanence..."
|
||||
mkdir -p /mnt/persist/etc
|
||||
mv /mnt/etc/nixos /mnt/persist/etc/
|
||||
mkdir -p /mnt/etc
|
||||
ln -s /persist/etc/nixos /mnt/etc/nixos
|
||||
success "Impermanence configured"
|
||||
fi
|
||||
|
||||
# 9.6 Install
|
||||
info "Running nixos-install (this will take a while)..."
|
||||
nixos-install --flake /mnt/etc/nixos#default --no-root-passwd
|
||||
success "Installation complete!"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# GENERATE FLAKE CONFIGURATION
|
||||
# ============================================================================
|
||||
|
||||
generate_flake_config() {
|
||||
local impermanence_opt=""
|
||||
[[ "$ENABLE_IMPERMANENCE" == "true" ]] && impermanence_opt="nomarchy.system.impermanence.enable = true;"
|
||||
|
||||
# flake.nix
|
||||
cat > /mnt/etc/nixos/flake.nix << 'FLAKE_EOF'
|
||||
{
|
||||
description = "My Nomarchy Configuration";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixos-25.11";
|
||||
nomarchy.url = "github:bemagri/nomarchy";
|
||||
nixos-hardware.url = "github:NixOS/nixos-hardware/master";
|
||||
home-manager = {
|
||||
url = "github:nix-community/home-manager/release-25.11";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs, nomarchy, home-manager, nixos-hardware, ... }@inputs: {
|
||||
nixosConfigurations.default = nixpkgs.lib.nixosSystem {
|
||||
specialArgs = { inputs = nomarchy.inputs // inputs; };
|
||||
modules = [
|
||||
{ nixpkgs.hostPlatform = "x86_64-linux"; }
|
||||
./hardware-configuration.nix
|
||||
./hardware-selection.nix
|
||||
nomarchy.nixosModules.system
|
||||
./system.nix
|
||||
home-manager.nixosModules.home-manager
|
||||
{
|
||||
home-manager.useGlobalPkgs = true;
|
||||
home-manager.useUserPackages = true;
|
||||
home-manager.extraSpecialArgs = { inherit inputs; };
|
||||
home-manager.users.@USERNAME@ = {
|
||||
imports = [ nomarchy.nixosModules.home ./home.nix ];
|
||||
home.username = "@USERNAME@";
|
||||
home.homeDirectory = "/home/@USERNAME@";
|
||||
home.stateVersion = "25.11";
|
||||
};
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
FLAKE_EOF
|
||||
sed -i "s/@USERNAME@/$USERNAME/g" /mnt/etc/nixos/flake.nix
|
||||
|
||||
# hardware-selection.nix
|
||||
cat > /mnt/etc/nixos/hardware-selection.nix << EOF
|
||||
{ inputs, ... }:
|
||||
{
|
||||
imports = [
|
||||
$(echo -e "$HARDWARE_MODULES")
|
||||
];
|
||||
$NOMARCHY_HW_OPTS
|
||||
}
|
||||
EOF
|
||||
|
||||
# system.nix
|
||||
cat > /mnt/etc/nixos/system.nix << EOF
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
time.timeZone = "$TIMEZONE";
|
||||
|
||||
$impermanence_opt
|
||||
|
||||
services.displayManager.autoLogin.enable = true;
|
||||
services.displayManager.autoLogin.user = "$USERNAME";
|
||||
|
||||
users.users."$USERNAME" = {
|
||||
isNormalUser = true;
|
||||
initialPassword = "$USER_PASSWORD";
|
||||
extraGroups = [ "networkmanager" "wheel" "video" "audio" "render" ];
|
||||
};
|
||||
|
||||
system.stateVersion = "25.11";
|
||||
}
|
||||
EOF
|
||||
|
||||
# home.nix (empty for user customization)
|
||||
cat > /mnt/etc/nixos/home.nix << 'EOF'
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
# Add your personal packages here
|
||||
home.packages = with pkgs; [
|
||||
# example: firefox thunderbird libreoffice
|
||||
];
|
||||
|
||||
# Add your personal home-manager configuration here
|
||||
}
|
||||
EOF
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# FINISH
|
||||
# ============================================================================
|
||||
|
||||
finish() {
|
||||
header
|
||||
|
||||
gum style --foreground 10 --bold --align center "INSTALLATION COMPLETE!"
|
||||
echo ""
|
||||
echo "Nomarchy has been successfully installed."
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo " 1. Remove the installation media"
|
||||
echo " 2. Reboot your computer"
|
||||
echo " 3. Log in with username: $USERNAME"
|
||||
echo " 4. Your configuration is at /etc/nixos/"
|
||||
echo ""
|
||||
|
||||
if gum confirm "Reboot now?"; then
|
||||
reboot
|
||||
fi
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# MAIN
|
||||
# ============================================================================
|
||||
|
||||
main() {
|
||||
header
|
||||
|
||||
check_environment
|
||||
select_disk
|
||||
get_luks_passphrase
|
||||
configure_user
|
||||
select_timezone
|
||||
select_hardware
|
||||
configure_impermanence
|
||||
review_configuration
|
||||
execute_installation
|
||||
finish
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user