diff --git a/MIGRATION.md b/MIGRATION.md index acf4f9b..4bcb2b3 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -1,5 +1,63 @@ # Laptop Migration: MacBook → x86_64 NixOS +## T490 + Windows 11 dual-boot (recommended flow) + +The T490 already has Windows 11 installed. To add NixOS alongside it without +losing Windows: + +### 1. Boot the NixOS installer from a Ventoy stick +From the live installer environment: +```bash +# Network access is required; either wifi-menu or plug in ethernet +# Then run the guided installer — it handles partitioning: +curl -L https://os.scoble.me/forgejo/sscoble/laptop-config/raw/branch/main/install.sh | sudo bash +``` + +The script: +- Detects `/dev/nvme0n1` and the existing ESP +- Shrinks the Windows NTFS partition with `ntfsresize` (no data loss) +- Creates a NixOS ext4 root partition in the freed space +- Mounts the existing Windows ESP at `/mnt/boot` (shared, not reformatted) +- Runs `nixos-install` with this flake +- Reboots into a systemd-boot menu with both NixOS and Windows entries + +### 2. After install +First boot → systemd-boot menu shows: +- `NixOS (default)` — most recent generation +- Older NixOS generations (up to 50, configurable in `modules/laptop.nix`) +- `Windows Boot Manager` — chainloads the standard Microsoft EFI binary at + `$ESP/EFI/Microsoft/Boot/bootmgfw.efi` + +If you ever want to skip the menu and boot straight to Windows, hold F12 at +the Lenovo splash and pick "Windows Boot Manager" from the firmware menu. + +### 3. Fall back to manual partition if the script fails +The script handles the common case. For unusual layouts (multiple disks, LVM, +existing NixOS partitions, etc.), do it by hand: +```bash +# Identify partitions +lsblk -f +# Target the existing Windows ESP for /boot +mount /dev/nvme0n1p1 /mnt/boot # adjust to your partition +# Create NixOS root +mkfs.ext4 -L nixos /dev/nvme0n1pX +mount /dev/nvme0n1pX /mnt +nixos-generate-config --root /mnt +nixos-install --flake git+https://os.scoble.me/forgejo/sscoble/laptop-config.git#laptop --root /mnt +``` + +### 4. NixOS 26.05 ("Yarara") compatibility +The flake pins `nixos-unstable`, which as of June 2026 tracks NixOS 26.05+. +Key 26.05 changes that affect this config: +- `boot.initrd.systemd.enable` is now default true. Most things Just Work. +- `boot.supportedFilesystems` is now an attrset (not a list) — nix warns but + the existing config still parses. +- `systemd.extraConfig` is removed; use `systemd.settings.Manager` instead. + We don't use `systemd.extraConfig` anywhere, so this is a no-op for us. +- The new `boot.loader.systemd-boot.windows` first-class option exists for + multi-drive dual-boot, but we don't need it — sharing a single ESP means + our explicit `extraEntries` works on all systemd-boot versions. + ## Order of operations ### 1. Install NixOS diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..6d6070a --- /dev/null +++ b/install.sh @@ -0,0 +1,191 @@ +#!/usr/bin/env bash +# install.sh — guided NixOS T490 dual-boot installer. +# +# Run this from a NixOS 26.05+ installer environment AFTER you've booted +# the ISO and dropped into a root shell. +# +# What it does: +# 1. Detects the T490's internal NVMe (/dev/nvme0n1) and existing partitions +# 2. Identifies the Windows EFI System Partition (ESP) and the Windows root +# 3. Shrinks the Windows root partition to make room +# 4. Creates a NixOS root + (optional) swap partition +# 5. Formats and mounts them +# 6. Runs nixos-install with this flake +# 7. Reboots into a dual-boot systemd-boot menu +# +# Prereqs: +# - Booted from the NixOS 26.05+ installer ISO (any of them) +# - Network connectivity (for fetching the flake + building) +# - UEFI boot mode (T490 default) +# - Win11 already installed (we share its ESP, we don't touch its partitions) +# +# Usage: +# curl -L https://os.scoble.me/forgejo/sscoble/laptop-config/raw/branch/main/install.sh | sudo bash +# +# Or, if you've cloned the repo: +# sudo ./install.sh +# +# Override defaults with env vars: +# DISK=/dev/nvme0n1 # internal NVMe (default: auto-detect) +# NIXOS_SIZE=200G # size of the new NixOS root partition (default: 200G) +# SWAP_SIZE=16G # size of swap partition (default: 16G, 0 to skip) +# FLAKE_URL=... # override the flake source +# HOST=laptop # nixosConfiguration name (default: laptop) + +set -euo pipefail + +# === Defaults === +DISK="${DISK:-}" +NIXOS_SIZE="${NIXOS_SIZE:-200G}" +SWAP_SIZE="${SWAP_SIZE:-16G}" +FLAKE_URL="${FLAKE_URL:-git+https://os.scoble.me/forgejo/sscoble/laptop-config.git}" +HOST="${HOST:-laptop}" + +# === Color output (no-op if not a TTY) === +if [[ -t 1 ]]; then + RED=$'\033[0;31m'; YEL=$'\033[0;33m'; GRN=$'\033[0;32m'; CYN=$'\033[0;36m'; NC=$'\033[0m' +else + RED=''; YEL=''; GRN=''; CYN=''; NC='' +fi +log() { echo "${CYN}>>>${NC} $*"; } +warn() { echo "${YEL}!!!${NC} $*" >&2; } +fail() { echo "${RED}FAIL${NC} $*" >&2; exit 1; } +ok() { echo "${GRN} OK ${NC} $*"; } + +# === Root check === +[[ $EUID -eq 0 ]] || fail "must run as root (sudo ./install.sh)" + +# === Sanity: this is a UEFI machine === +[[ -d /sys/firmware/efi ]] || fail "system is not booted in UEFI mode — T490 should always be UEFI" + +# === Detect disk === +if [[ -z "$DISK" ]]; then + # T490 internal is /dev/nvme0n1; reject anything that's clearly removable + for candidate in /dev/nvme0n1 /dev/sda; do + if [[ -b "$candidate" ]] && lsblk -ndo TRAN "$candidate" 2>/dev/null | grep -qE "nvme|sata"; then + # Make sure it's the boot disk (has the ESP) + if lsblk -nlo PARTTYPE "$candidate"* 2>/dev/null | grep -qi "EFI System"; then + DISK="$candidate" + break + fi + fi + done +fi +[[ -n "$DISK" ]] || fail "could not auto-detect internal disk; set DISK=/dev/nvme0n1 explicitly" +log "Target disk: $DISK" + +# === Identify partitions === +ESP_PART="" +WIN_PART="" +# Use lsblk to enumerate partitions, then filter by parttype +while IFS= read -r part; do + ptype=$(lsblk -ndo PARTTYPE "$part" 2>/dev/null || true) + if [[ "$ptype" == "c12a7328-f81f-11d2-ba4b-00a0c93ec93b" && -z "$ESP_PART" ]]; then + ESP_PART="$part" + elif [[ "$ptype" == "ebd0a0a2-b9e5-4433-87c0-68b6b72699c7" && -z "$WIN_PART" ]]; then + # First big NTFS partition = Windows root + WIN_PART="$part" + WIN_SIZE=$(lsblk -ndo SIZE "$part" 2>/dev/null || true) + fi +done < <(lsblk -nlo NAME "$DISK" 2>/dev/null | tail -n +2 | sed "s|^|/dev/|") + +[[ -n "$ESP_PART" ]] || fail "no EFI System Partition found on $DISK" +[[ -n "$WIN_PART" ]] || fail "no Windows NTFS partition found on $DISK" +log "EFI System Partition: $ESP_PART" +log "Windows partition: $WIN_PART ($WIN_SIZE)" + +# === Confirm with user === +cat </dev/null || true +# ntfsresize syntax: -s = perform the shrink, --size = new size in bytes +# We want the new Windows partition to be (current size - nixos_bytes). +win_size_bytes=$(lsblk -ndo SIZE -b "$WIN_PART") +new_win_bytes=$(( win_size_bytes - nixos_bytes )) +ntfsresize -s --size "$new_win_bytes" "$WIN_PART" 2>&1 | tail -5 \ + || fail "ntfsresize failed; aborting" + +# === Recreate partition table entry === +# ntfsresize modified the partition in place; parted may need a re-read +partprobe "$DISK" +sleep 2 + +# === Find next available partition number === +existing=$(lsblk -nlo NAME "$DISK" 2>/dev/null | wc -l) +log "Existing partitions on $DISK: $existing" + +# Create NixOS root partition in the freed space +log "Creating NixOS root partition ($NIXOS_SIZE)..." +parted -s -a opt "$DISK" mkpart primary ext4 -- -"$NIXOS_SIZE" 100% || fail "parted mkpart failed" +partprobe "$DISK" +sleep 2 + +# Find the new partition +NIXOS_PART=$(lsblk -nlo NAME "$DISK" 2>/dev/null | sed "s|^|/dev/|" | sort | tail -1) +log "New partition: $NIXOS_PART" + +# === Format and mount === +log "Formatting $NIXOS_PART as ext4..." +mkfs.ext4 -F -L nixos "$NIXOS_PART" + +log "Mounting..." +mount "$NIXOS_PART" /mnt +mkdir -p /mnt/boot +mount "$ESP_PART" /mnt/boot +ok "Mounted: $(lsblk -no MOUNTPOINTS $NIXOS_PART) and $(lsblk -no MOUNTPOINTS $ESP_PART)" + +# === Generate hardware config === +log "Generating hardware-configuration.nix..." +nixos-generate-config --root /mnt + +# === nixos-install with the flake === +log "Installing NixOS from flake: $FLAKE_URL#$HOST" +log "(This fetches and builds the world; first run is slow.)" +nixos-install --flake "${FLAKE_URL}#${HOST}" --root /mnt --no-root-passwd + +ok "NixOS installed." + +cat <