#!/bin/bash set -e # ============================================================================= # machine-setup — provisioning for a fresh machine # # Brings a blank box up to a usable state: users, SSH, networking, firewall, # Docker, shell and editor tooling, language runtimes. # # Run as root: sudo scripts/setup/machine-setup/machine-setup.sh # ============================================================================= SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROGRESS_FILE="$SCRIPT_DIR/.setup-progress" # Shared state, output helpers, the step/resume machine and OS detection. Kept in # lib/ so a step can eventually be read — or run — on its own without dragging the # whole script in. Definitions only; nothing in there acts. # shellcheck source=lib/base.sh source "$SCRIPT_DIR/lib/base.sh" # shellcheck source=lib/packages.sh source "$SCRIPT_DIR/lib/packages.sh" # shellcheck source=lib/tools.sh source "$SCRIPT_DIR/lib/tools.sh" # Trap errors with context. Installed here rather than in lib/base.sh, because # that file is definitions only and a trap is a side effect on whoever sources it. trap 'echo ""; echo -e "${RED}╔══════════════════════════════════════════════════╗${NC}"; echo -e "${RED}║ SETUP FAILED${NC}"; echo -e "${RED}║ Step: ${CURRENT_STEP:-unknown}${NC}"; echo -e "${RED}║ Line: $LINENO${NC}"; echo -e "${RED}║ Command: $BASH_COMMAND${NC}"; echo -e "${RED}╚══════════════════════════════════════════════════╝${NC}"' ERR # ============================================================================= # 1. Pre-flight # ============================================================================= echo "" echo -e "${BOLD}╔══════════════════════════════════════════════════╗${NC}" echo -e "${BOLD}║ Machine Setup ║${NC}" echo -e "${BOLD}╚══════════════════════════════════════════════════╝${NC}" detect_os echo "" info "Machine: ${OS_NAME} (${ARCH})" info "Packages: ${PM:-none detected}" [[ "$IS_WSL" == true ]] && warn "WSL detected — the suspend, logind and boot-hang steps do not apply here" # Everything below this line is written against apt and systemd. Detection above # recognises pacman, dnf and brew so the branches have somewhere to hang, but # nothing implements them yet — and running the apt path on Arch would half-build # a machine and stop somewhere unhelpful. Refuse clearly instead, and relax this # list one entry at a time as each package manager grows a real path. case "$PM" in apt) ;; "") fail "Could not find a package manager for '${OS_NAME}'." ;; *) fail "${OS_NAME} uses ${PM}, which this script does not implement yet — apt-based systems only, so far." ;; esac ask_machine_role info "Role: ${MACHINE_ROLE}" if [[ -f "$PROGRESS_FILE" ]]; then DONE_COUNT=$(wc -l < "$PROGRESS_FILE") echo -e "${YELLOW} Resuming — $DONE_COUNT step(s) already completed${NC}" echo -e "${YELLOW} Progress file: $PROGRESS_FILE${NC}" echo -e "${YELLOW} To start fresh: rm $PROGRESS_FILE${NC}" fi if [[ "$EUID" -ne 0 ]]; then fail "Please run as root: sudo ./machine-setup.sh" fi prompt_value USERNAME "New admin username (or existing)" "" if [[ -z "$USERNAME" ]]; then fail "Username cannot be empty" fi USER_HOME="/home/$USERNAME" # ============================================================================= # 2. System Update & Essentials # ============================================================================= step "System Update & Essentials" if ! skip; then info "Refreshing the package index..." pkg_refresh # The one place that deliberately moves versions of things already installed. # Everything else in this script only ever adds what is absent. info "Upgrading installed packages..." pkg_upgrade_all # shellcheck disable=SC2046 # word splitting is how the list is passed pkg_install "Core packages" $(pkgs_core) summarise_last "Core packages" ok "System updated and core packages in place" step_ok fi # ============================================================================= # 2b. Command-line tools # ============================================================================= # # Separate from the packages above because they are a different thing: upstream # binaries on their own release cadence, not anything the distribution ships. # Lumping them in made a run look like it was installing system packages and # then start downloading tarballs unannounced. step "Command-line tools" if ! skip; then # shellcheck disable=SC2046 # word splitting is how the list is passed tools_install "Command-line tools" $(tools_default) summarise_last "Command-line tools" step_ok fi # ============================================================================= # NOT PORTED YET # ============================================================================= # # Sections still to move across from scripts/setup-old/setup-ubuntu.sh, in order: # # locale · timezone · swap · auto-suspend · boot-hang fix · user creation · # ssh keys · ssh hardening · dns · static ip · fail2ban · unattended-upgrades · # git config · docker · zsh + prompt · tailscale · neovim · js runtimes · # dev tools · ufw · zshrc · disk ballast # # Each arrives as its own commit. Delete this block when the list is empty. # ============================================================================= # 23. Summary # ============================================================================= echo "" echo "" if [[ ${#ERRORS[@]} -gt 0 ]]; then echo -e "${YELLOW}╔══════════════════════════════════════════════════╗${NC}" echo -e "${YELLOW}║ Setup Complete (with warnings) ║${NC}" echo -e "${YELLOW}╚══════════════════════════════════════════════════╝${NC}" else echo -e "${GREEN}╔══════════════════════════════════════════════════╗${NC}" echo -e "${GREEN}║ Setup Complete ║${NC}" echo -e "${GREEN}╚══════════════════════════════════════════════════╝${NC}" fi echo "" echo -e "${BOLD} What was done:${NC}" for item in "${SUMMARY[@]}"; do echo -e " ${GREEN}+${NC} $item" done if [[ ${#ERRORS[@]} -gt 0 ]]; then echo "" echo -e "${BOLD} Non-critical issues:${NC}" for err in "${ERRORS[@]}"; do echo -e " ${YELLOW}!${NC} $err" done fi echo "" echo -e "${BOLD} Machine:${NC}" echo " System: $OS_NAME ($ARCH)" echo " Role: $MACHINE_ROLE" echo " User: $USERNAME" echo " Home: $USER_HOME" [[ -n "${TS_IP:-}" && "$TS_IP" != "unknown" ]] && echo " Tailscale: $TS_IP" echo "" # Clean up progress file on success rm -f "$PROGRESS_FILE"