1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
|
#!/bin/sh
# bootstrap.sh — take a fresh minimal Arch install (only the 'base'
# meta-package installed) to the point where `just init` has run, the
# dotfiles are deployed, and recommended services are enabled.
#
# Prerequisites (from the Arch installation guide):
# - A regular user already exists and is a member of the 'wheel' group.
# - You are logged in as that user (paru/makepkg refuse to run as root).
#
# Usage:
# curl -fsSL https://raw.githubusercontent.com/sommerfelddev/dotfiles/master/bootstrap.sh | sh
#
# Overrides:
# DOTFILES_REPO (default: https://github.com/sommerfelddev/dotfiles.git)
# DOTFILES_DIR (default: $HOME/dotfiles)
set -eu
log() { printf '\033[1;34m==>\033[0m %s\n' "$*"; }
warn() { printf '\033[1;33m==>\033[0m %s\n' "$*" >&2; }
die() {
printf '\033[1;31m==>\033[0m %s\n' "$*" >&2
exit 1
}
# 0. refuse root — paru/makepkg won't run as root
[ "$(id -u)" -ne 0 ] || die "run this as your regular user, not root"
# 1. user must be in wheel (required so the sudoers rule we enable takes effect)
id -nG "$USER" | tr ' ' '\n' | grep -qx wheel ||
die "user '$USER' must be in the 'wheel' group"
# 2. install sudo + pacman prerequisites, enable wheel in sudoers.
# If sudo is absent we do this in a single su -c so the root password
# is entered only once. If sudo is already there, reuse it.
PREREQS='sudo git base-devel chezmoi just efibootmgr nix'
SUDOERS_SED='s/^# *\(%wheel ALL=(ALL:ALL\(:ALL\)*) ALL\)/\1/'
if ! command -v sudo >/dev/null 2>&1; then
log 'installing prerequisites (prompting for root password)'
su -c "pacman -Syu --needed --noconfirm ${PREREQS} && \
sed -i '${SUDOERS_SED}' /etc/sudoers"
else
log 'installing prerequisites'
# shellcheck disable=SC2086 # PREREQS is an intentional word list
sudo pacman -Syu --needed --noconfirm ${PREREQS}
sudo sed -i "${SUDOERS_SED}" /etc/sudoers
fi
# 3. bootstrap paru-bin from AUR if missing
if ! command -v paru >/dev/null 2>&1; then
log 'building paru-bin from AUR'
tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' EXIT
git clone --depth=1 https://aur.archlinux.org/paru-bin.git "$tmp/paru-bin"
(cd "$tmp/paru-bin" && makepkg -si --noconfirm)
fi
# 4. clone dotfiles
DOTFILES_DIR="${DOTFILES_DIR:-$HOME/dotfiles}"
REPO_URL="${DOTFILES_REPO:-https://github.com/sommerfelddev/dotfiles.git}"
if [ ! -d "$DOTFILES_DIR/.git" ]; then
log "cloning $REPO_URL -> $DOTFILES_DIR"
git clone "$REPO_URL" "$DOTFILES_DIR"
else
log "using existing clone at $DOTFILES_DIR"
fi
# 5. run just init — this deploys chezmoi, installs the 'base' meta list
# (which pulls in sudo-rs), deploys /etc/sudoers-rs, /etc/pam.d/sudo,
# creates /usr/local/bin/{sudo,su,visudo,sudoedit} symlinks pointing
# at sudo-rs (PATH precedence shadows /usr/bin/sudo), and installs
# git hooks. The classic 'sudo' package stays installed because
# base-devel hard-depends on it; that's harmless — the binary is
# never invoked once /usr/local/bin/sudo is in place. `just init`
# also runs `just nix-switch` to apply the Home-Manager profile;
# nix itself is part of PREREQS above (pacman package), and
# nix-daemon.socket is enabled by `unit-apply` via
# systemd-units/system.txt.
cd "$DOTFILES_DIR"
# Source the nix profile so `nix` is on PATH for the rest of this
# script (pacman drops /etc/profile.d/nix.sh but the current shell
# didn't read it). Multi-user (daemon mode) and per-user variants exist;
# pacman ships the multi-user one.
for f in /etc/profile.d/nix.sh /etc/profile.d/nix-daemon.sh; do
if [ -r "$f" ]; then
# shellcheck disable=SC1090
. "$f"
break
fi
done
log 'running just init'
just init
# 5b. chsh to nix-store zsh (provisioned by home-manager via nix/common.nix)
NIX_ZSH="$HOME/.nix-profile/bin/zsh"
if [ -x "$NIX_ZSH" ]; then
if ! grep -qxF "$NIX_ZSH" /etc/shells 2>/dev/null; then
log "appending $NIX_ZSH to /etc/shells"
echo "$NIX_ZSH" | sudo tee -a /etc/shells >/dev/null
fi
current_shell="$(getent passwd "$USER" | cut -d: -f7)"
if [ "$current_shell" != "$NIX_ZSH" ]; then
log "changing login shell to $NIX_ZSH"
sudo chsh -s "$NIX_ZSH" "$USER"
fi
fi
# 6. refresh pacman mirrorlist once via reflector (config deployed by chezmoi)
log 'refreshing pacman mirrorlist via reflector'
sudo reflector @/etc/xdg/reflector/reflector.conf \
--save /etc/pacman.d/mirrorlist ||
warn 'reflector failed; keeping existing mirrorlist'
# 7. create XDG user directories (~/Documents, ~/Downloads, etc.)
if command -v xdg-user-dirs-update >/dev/null 2>&1; then
log 'creating XDG user directories'
xdg-user-dirs-update || warn 'xdg-user-dirs-update failed'
fi
# 8. optional: create an Arch EFI boot entry if none exists
if [ -d /sys/firmware/efi ]; then
if ! sudo efibootmgr 2>/dev/null | grep -iq arch; then
warn 'no Arch Linux EFI boot entry found'
warn 'after first kernel install, run: sudo mkinitcpio -P'
warn 'then register the UKIs with efibootmgr (hardened first so it'\''s the default):'
# shellcheck disable=SC1003 # backslash is literal text shown to the user
warn ' sudo efibootmgr --create --disk /dev/nvme0n1 --part 1 \'
warn " --label 'Arch Hardened' --loader '\\EFI\\Linux\\arch-linux-hardened.efi'"
# shellcheck disable=SC1003
warn ' sudo efibootmgr --create --disk /dev/nvme0n1 --part 1 \'
warn " --label 'Arch Hardened Fallback' --loader '\\EFI\\Linux\\arch-linux-hardened-fallback.efi'"
warn 'and the linux-lts fallback kernel UKIs:'
# shellcheck disable=SC1003
warn ' sudo efibootmgr --create --disk /dev/nvme0n1 --part 1 \'
warn " --label 'Arch LTS' --loader '\\EFI\\Linux\\arch-linux-lts.efi'"
# shellcheck disable=SC1003
warn ' sudo efibootmgr --create --disk /dev/nvme0n1 --part 1 \'
warn " --label 'Arch LTS Fallback' --loader '\\EFI\\Linux\\arch-linux-lts-fallback.efi'"
fi
fi
log 'done. Log out and back in (or reboot) to pick up shell and group changes.'
|