diff --git a/scripts/hm-bootstrap.sh b/scripts/hm-bootstrap.sh index b707234..f4b2c51 100644 --- a/scripts/hm-bootstrap.sh +++ b/scripts/hm-bootstrap.sh @@ -1,76 +1,92 @@ #!/usr/bin/env bash set -euo pipefail +UID="$(id -u)" +USER_BUS="/run/user/$UID/bus" +HM_DIR="$HOME/.config/home-manager" +SKEL_DIR="/etc/skel/.config/home-manager" + # Ensure basic PATH for user units export PATH="/run/current-system/sw/bin:${PATH:-}" -# Try to hook into the session bus if available -if [ -z "${DBUS_SESSION_BUS_ADDRESS:-}" ] && [ -S "/run/user/$(id -u)/bus" ]; then - export DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/$(id -u)/bus" +# Attach to session bus if possible +if [[ -z "${DBUS_SESSION_BUS_ADDRESS:-}" && -S "$USER_BUS" ]]; then + export DBUS_SESSION_BUS_ADDRESS="unix:path=$USER_BUS" fi -# Only continue in an active, unlocked user session -if command -v loginctl >/dev/null 2>&1 && [ -n "${XDG_SESSION_ID:-}" ]; then - session_class=$(loginctl show-session "${XDG_SESSION_ID}" -p Class --value 2>/dev/null || echo "") - session_state=$(loginctl show-session "${XDG_SESSION_ID}" -p State --value 2>/dev/null || echo "") - session_locked=$(loginctl show-session "${XDG_SESSION_ID}" -p LockedHint --value 2>/dev/null || echo "yes") - if [ "${session_class}" != "user" ] || [ "${session_state}" != "active" ] || [ "${session_locked}" != "no" ]; then - exit 0 - fi +have() { command -v "$1" >/dev/null 2>&1; } + +# ---- Session sanity check --------------------------------------------------- + +if have loginctl && [[ -n "${XDG_SESSION_ID:-}" ]]; then + # One loginctl call instead of three + eval "$( + loginctl show-session "$XDG_SESSION_ID" \ + -p Class -p State -p LockedHint \ + --value 2>/dev/null | + awk ' + NR==1{print "SESSION_CLASS="$0} + NR==2{print "SESSION_STATE="$0} + NR==3{print "SESSION_LOCKED="$0} + ' + )" + + [[ "${SESSION_CLASS:-}" == "user" ]] || exit 0 + [[ "${SESSION_STATE:-}" == "active" ]] || exit 0 + [[ "${SESSION_LOCKED:-yes}" == "no" ]] || exit 0 fi -wait_for_notifications() { - # Wait briefly for GNOME Shell notifications to come up and the session to be unlocked. - if [ -z "${DBUS_SESSION_BUS_ADDRESS:-}" ] || ! command -v gdbus >/dev/null 2>&1; then - return 1 - fi +# ---- Notifications ---------------------------------------------------------- - # Do not notify in greeter sessions. - if [ "${XDG_SESSION_CLASS:-}" != "user" ]; then - return 1 - fi +notifications_ready() { + [[ "${XDG_SESSION_CLASS:-user}" == "user" ]] || return 1 + [[ -n "${DBUS_SESSION_BUS_ADDRESS:-}" ]] || return 1 + have gdbus || return 1 - # If loginctl is available, wait until this session is unlocked and class=user (not greeter). - if command -v loginctl >/dev/null 2>&1 && [ -n "${XDG_SESSION_ID:-}" ]; then - for _ in 1 2 3 4 5 6 7; do - session_class=$(loginctl show-session "${XDG_SESSION_ID}" -p Class --value 2>/dev/null || echo "") - locked=$(loginctl show-session "${XDG_SESSION_ID}" -p LockedHint --value 2>/dev/null || echo "yes") - if [ "${session_class}" = "user" ] && [ "${locked}" = "no" ]; then - break - fi + # Wait for unlock if loginctl is present + if have loginctl && [[ -n "${XDG_SESSION_ID:-}" ]]; then + for _ in {1..7}; do + [[ "$(loginctl show-session "$XDG_SESSION_ID" \ + -p LockedHint --value 2>/dev/null)" == "no" ]] && break sleep 2 done fi - for _ in 1 2 3 4 5 6 7; do - if gdbus introspect --session \ + # Wait for notification service + for _ in {1..7}; do + gdbus introspect --session \ --dest org.freedesktop.Notifications \ - --object-path /org/freedesktop/Notifications >/dev/null 2>&1; then - return 0 - fi + --object-path /org/freedesktop/Notifications \ + >/dev/null 2>&1 && return 0 sleep 2 done + return 1 } notify() { - local title="$1" - shift || true - if command -v notify-send >/dev/null 2>&1 && wait_for_notifications; then - notify-send --app-name="Bootstrap" --urgency=critical --expire-time=0 "$title" "$@" >/dev/null 2>&1 || true - fi + have notify-send || return 0 + notifications_ready || return 0 + notify-send \ + --app-name="Bootstrap" \ + --urgency=critical \ + --expire-time=0 \ + "$@" >/dev/null 2>&1 || true } notify "Reticulating Splines" "Bootstrapping. Please stay tuned." trap 'notify "Bootstrap finished" "Your system is fully hydrated. It is recommended to logout and back in."' EXIT -mkdir -p "$HOME/.config/home-manager" +# ---- Home Manager bootstrap ------------------------------------------------- + +mkdir -p "$HM_DIR" + for f in flake.nix home.nix; do - if [ -f "/etc/skel/.config/home-manager/$f" ] && [ ! -e "$HOME/.config/home-manager/$f" ]; then - cp "/etc/skel/.config/home-manager/$f" "$HOME/.config/home-manager/$f" - chmod u+rw "$HOME/.config/home-manager/$f" - fi + src="$SKEL_DIR/$f" + dst="$HM_DIR/$f" + [[ -f "$src" && ! -e "$dst" ]] || continue + install -m 600 "$src" "$dst" done -home-manager switch --flake "$HOME/.config/home-manager#admin" -touch "$HOME/.config/home-manager/.hm_bootstrap_done" +home-manager switch --flake "$HM_DIR#admin" +touch "$HM_DIR/.hm_bootstrap_done" diff --git a/scripts/install-btrfs.sh b/scripts/install-btrfs.sh index 5f94c65..d47a8f2 100755 --- a/scripts/install-btrfs.sh +++ b/scripts/install-btrfs.sh @@ -4,51 +4,84 @@ set -euo pipefail # Non-interactive Btrfs installer using the flake in this repo. # Defaults: DISK=/dev/vda TARGET_HOST=nanuqsaurus -DISK=${DISK:-/dev/vda} -TARGET_HOST=${TARGET_HOST:-nanuqsaurus} +DISK="${DISK:-/dev/vda}" +TARGET_HOST="${TARGET_HOST:-nanuqsaurus}" -SCRIPT_DIR=$(cd -- "$(dirname "${BASH_SOURCE[0]}")" && pwd) -REPO_ROOT=$(cd -- "${SCRIPT_DIR}/.." && pwd) +SCRIPT_DIR="$(cd -- "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd -- "$SCRIPT_DIR/.." && pwd)" + +MNT=/mnt +EFI_SIZE=550M + +have() { command -v "$1" >/dev/null 2>&1; } + +die() { + echo "ERROR: $*" >&2 + exit 1 +} + +confirm_disk() { + echo "About to DESTROY ALL DATA on ${DISK}" + lsblk "$DISK" || die "Disk not found: $DISK" + echo "Continuing in 5 seconds… (Ctrl+C to abort)" + sleep 5 +} clear +confirm_disk echo "[1/7] Partitioning ${DISK}" -printf "label: gpt\n,550M,U\n,,L\n" | sfdisk "${DISK}" +sfdisk "$DISK" </dev/null 2>&1; then - rsync -a --exclude '.git' --exclude 'result' --exclude 'node_modules' "${REPO_ROOT}/" /mnt/etc/nixos/ +mkdir -p "$MNT/etc/nixos" + +EXCLUDES=(--exclude=.git --exclude=result --exclude=node_modules) + +if have rsync; then + rsync -a "${EXCLUDES[@]}" "$REPO_ROOT/" "$MNT/etc/nixos/" else - (cd "${REPO_ROOT}" && tar -cf - --exclude=.git --exclude=result --exclude=node_modules .) | (cd /mnt/etc/nixos && tar -xf -) + (cd "$REPO_ROOT" && tar -cf - "${EXCLUDES[@]/#/--exclude=}" .) \ + | (cd "$MNT/etc/nixos" && tar -xf -) fi echo "[6/7] Generating hardware config" -mkdir -p /mnt/etc/nixos/hosts -nixos-generate-config --root /mnt --show-hardware-config > "/mnt/etc/nixos/hosts/local-${TARGET_HOST}-hardware.nix" +HOSTS_DIR="$MNT/etc/nixos/hosts" +mkdir -p "$HOSTS_DIR" -host_file="/mnt/etc/nixos/hosts/local-${TARGET_HOST}.nix" -if [ ! -e "${host_file}" ]; then - cat > "${host_file}" < "$HW_FILE" + +if [[ ! -e "$HOST_FILE" ]]; then + cat > "$HOST_FILE" <