From 31345ce0db8112308b78d7f09b9f1dd2c3973b73 Mon Sep 17 00:00:00 2001 From: sommerfeld Date: Tue, 21 Apr 2026 01:23:38 +0100 Subject: feat: add bootstrap.sh for fresh Arch installs Takes a minimal Arch system (only 'base' installed) to the point where 'just init' has run and dotfiles are deployed. Installs prerequisites (sudo, git, base-devel, chezmoi, just, efibootmgr), enables %wheel in sudoers, bootstraps paru-bin from the AUR, clones the repo, runs 'just init' (which swaps sudo for doas-sudo-shim via the existing base meta list), and launches create-efi if no Arch EFI boot entry exists. Designed to be curlable: curl -fsSL https://raw.githubusercontent.com/sommerfelddev/dotfiles/master/bootstrap.sh | sh --- .github/copilot-instructions.md | 1 + README.md | 16 ++++++++- bootstrap.sh | 80 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 96 insertions(+), 1 deletion(-) create mode 100755 bootstrap.sh diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 219ac1b..2a837f5 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -17,6 +17,7 @@ The repo root is a chezmoi source directory. Files targeting `$HOME` use chezmoi - `meta/` contains plain text package lists for Arch Linux (one package per line, `#` comments). Each `.txt` file represents a group (e.g. `base.txt`, `dev.txt`, `wayland.txt`). Install with `just install base dev` or `just install-all`. Detect drift with `just status`. - `firefox/` contains Firefox/LibreWolf hardening overrides (`user-overrides.js`) and custom CSS (`chrome/userChrome.css`). Deployed by `run_onchange_after_deploy-firefox.sh.tmpl`. - `dot_local/bin/executable_create-efi` is an interactive EFI boot entry creation script using `efibootmgr` (deployed to `~/.local/bin/create-efi`). +- `bootstrap.sh` at the repo root is a POSIX shell script that takes a fresh minimal Arch install (only `base`) to a fully deployed state. It installs prerequisites, enables `%wheel` sudoers, bootstraps `paru-bin` from the AUR, clones the repo, runs `just init`, and optionally invokes `create-efi`. Designed to be curlable: `curl -fsSL .../bootstrap.sh | sh`. - `.chezmoiignore` excludes non-home files (`etc/`, `meta/`, `firefox/`, docs) from deployment to `$HOME`. - `.githooks/` contains git hooks (notably `post-commit` which runs `chezmoi apply`). Activated by `just init`. - `justfile` provides recipes: `init` (first-time setup), `sync` (apply + fix), `apply`, `fix`, `status`, `pkg-drift`, `dotfile-drift`, `undeclared`, `diff`, `merge`, `groups`, `install`, `install-all`, `add`, `remove`. Run `just` or `just --list` to see them. diff --git a/README.md b/README.md index c434b03..af8264c 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,21 @@ My Arch Linux configuration, managed with [chezmoi](https://www.chezmoi.io/). -## Setup +## Bootstrap on a fresh Arch install + +On a minimal Arch system (only `base` installed), as the regular wheel +user: + +```sh +curl -fsSL https://raw.githubusercontent.com/sommerfelddev/dotfiles/master/bootstrap.sh | sh +``` + +This installs prerequisites, enables `%wheel` in sudoers, builds +`paru-bin` from the AUR, clones this repo to `~/dotfiles`, runs +`just init`, and — on EFI systems missing an Arch boot entry — +launches `create-efi`. + +## Setup on an existing system ```sh chezmoi init --source ~/dotfiles diff --git a/bootstrap.sh b/bootstrap.sh new file mode 100755 index 0000000..09983f3 --- /dev/null +++ b/bootstrap.sh @@ -0,0 +1,80 @@ +#!/bin/sh +# bootstrap.sh — take a fresh minimal Arch install (only the 'base' +# meta-package installed) to the point where `just init` has run and +# the dotfiles are deployed. +# +# Must be executed as the regular (non-root) user that will own the +# system. paru and makepkg refuse to run as root, so we keep everything +# user-side and only escalate for the pacman + sudoers step. +# +# 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' "$*"; } +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' +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 +# (swapping sudo for doas-sudo-shim via paru -S --ask=4), deploys +# /etc/doas.conf, and installs git hooks. +cd "$DOTFILES_DIR" +log 'running just init' +just init + +# 6. 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 + log 'no Arch Linux EFI boot entry found; launching create-efi' + "$HOME/.local/bin/create-efi" + fi +fi + +log 'done. Log out and back in (or reboot) to pick up shell and group changes.' -- cgit v1.2.3-70-g09d2