530 lines
15 KiB
Bash
Executable File
530 lines
15 KiB
Bash
Executable File
#!/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=""
|
|
LUKS_PASSWORD=""
|
|
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
|
|
LUKS_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 disko_file tmp_disko
|
|
disko_file="$NOMARCHY_REPO/installer/disko-golden.nix"
|
|
tmp_disko=$(mktemp --suffix=.nix)
|
|
|
|
sed "s|@TARGET_DRIVE@|${TARGET_DRIVE}|g" "$disko_file" > "$tmp_disko"
|
|
|
|
# Provide the LUKS passphrase via stdin for disk encryption
|
|
echo -n "$LUKS_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
|
|
];
|
|
};
|
|
|
|
homeConfigurations.@USERNAME@ = home-manager.lib.homeManagerConfiguration {
|
|
pkgs = nixpkgs.legacyPackages.x86_64-linux;
|
|
extraSpecialArgs = { inputs = nomarchy.inputs // inputs; };
|
|
modules = [
|
|
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
|
|
|
|
environment.systemPackages = with pkgs; [
|
|
home-manager
|
|
];
|
|
|
|
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 "$@"
|