From dda214ffb028a3d2e0e9c235b4566fbc3ec7037d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Wed, 12 Aug 2026 16:55:06 +0000 Subject: [PATCH] detect the operating system before any step runs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The script assumed Ubuntu on x86_64 in every line of it. detect_os() now runs first and fills in OS, OS_NAME, OS_VERSION, PM, ARCH and IS_WSL, so the steps have something to branch on as support for other systems is added. Read from /etc/os-release rather than probing for a binary: a machine can have more than one package manager on PATH, and only os-release can say which distribution this actually is or give a version worth printing. Sourced in a subshell so its NAME, VERSION and ID do not leak in here. ID_LIKE is the fallback, so Pop!_OS, Mint and EndeavourOS resolve without being named. ARCH is normalised to amd64/arm64 in one place because upstream disagrees — Neovim ships aarch64, Go and Docker ship arm64, lazygit ships x86_64 — and several steps hardcode one spelling today. Windows exits with a message pointing at WSL2. WSL itself is detected and warned about rather than refused: it reports as Linux but has no real systemd session, so the suspend, logind and boot-hang steps do nothing there. Everything below pre-flight is still apt and systemd only, so a gate refuses pacman/dnf/brew by name rather than half-building a machine and stopping somewhere unhelpful. Relax that case one entry at a time as each grows a path. Verified on this host (Ubuntu 24.04.4, amd64, apt) and by stubbing uname and os_release for arch, manjaro/arm64, fedora, pop, macos, mingw and riscv64. Co-Authored-By: Claude Opus 5 (1M context) --- scripts/machine-setup.sh | 111 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 109 insertions(+), 2 deletions(-) diff --git a/scripts/machine-setup.sh b/scripts/machine-setup.sh index a9c28036..b05f5078 100755 --- a/scripts/machine-setup.sh +++ b/scripts/machine-setup.sh @@ -14,6 +14,15 @@ ERRORS=() CURRENT_STEP="" SKIP_STEP=false +# What machine this is. Filled in by detect_os() before any step runs; every step +# after that branches on these rather than assuming apt on x86_64. +OS="" # os-release ID: ubuntu | debian | arch | fedora | macos | … +OS_NAME="" # pretty name, for the banner +OS_VERSION="" # version id; empty on rolling releases +PM="" # apt | pacman | dnf | brew +ARCH="" # amd64 | arm64, normalised — upstream tarballs disagree on spelling +IS_WSL=false + # ----------------------------------------------------------------------------- # Helpers # ----------------------------------------------------------------------------- @@ -84,15 +93,113 @@ as_user() { sudo -u "$USERNAME" -i bash -c "$1" } +# ----------------------------------------------------------------------------- +# Operating system detection +# ----------------------------------------------------------------------------- +# +# Read one key out of /etc/os-release without leaking the rest of it into this +# script. That file defines NAME, VERSION and ID — all generic enough to collide +# with something here — so it is sourced in a subshell and only the one value +# asked for comes back. +os_release() { + [[ -r /etc/os-release ]] || return 1 + # shellcheck disable=SC1091 + ( + . /etc/os-release 2>/dev/null + printf '%s' "${!1:-}" + ) +} + +# Identify the machine, or refuse to guess. +# +# /etc/os-release rather than probing for a binary: a box can have more than one +# package manager on PATH (a Homebrew install on Linux, a leftover apt on a +# converted box), and only os-release can say which distribution the machine +# actually IS, or give a version worth reporting. +# +# ID_LIKE is the fallback so derivatives resolve without being listed by name — +# Pop!_OS, Mint and EndeavourOS all answer correctly without appearing below. +detect_os() { + local kernel like + kernel="$(uname -s)" + + case "$kernel" in + Darwin) + OS="macos" + OS_VERSION="$(sw_vers -productVersion 2>/dev/null || true)" + OS_NAME="macOS ${OS_VERSION}" + PM="brew" + ;; + Linux) + OS="$(os_release ID || true)" + OS_NAME="$(os_release PRETTY_NAME || true)" + OS_VERSION="$(os_release VERSION_ID || true)" + like="$(os_release ID_LIKE || true)" + + case "$OS" in + ubuntu | debian | linuxmint | pop | raspbian | elementary) PM="apt" ;; + arch | manjaro | endeavouros | cachyos | garuda) PM="pacman" ;; + fedora | rhel | centos | rocky | almalinux) PM="dnf" ;; + *) + case " $like " in + *" debian "* | *" ubuntu "*) PM="apt" ;; + *" arch "*) PM="pacman" ;; + *" fedora "* | *" rhel "*) PM="dnf" ;; + esac + ;; + esac + + # WSL reports itself as Linux, but has no real systemd session: masking + # sleep targets, restarting logind and anything touching the boot path + # either fail or silently do nothing. Worth knowing before those steps run. + if grep -qi microsoft /proc/version 2>/dev/null; then IS_WSL=true; fi + ;; + MINGW* | MSYS* | CYGWIN*) + fail "Windows is not supported. Run this inside WSL2 with an Ubuntu image instead." + ;; + *) + fail "Unrecognised kernel '$kernel' — cannot tell what this machine is." + ;; + esac + + # Normalised once here because upstream projects spell it differently: + # Neovim ships aarch64, Go and Docker ship arm64, and lazygit ships x86_64. + case "$(uname -m)" in + x86_64 | amd64) ARCH="amd64" ;; + aarch64 | arm64) ARCH="arm64" ;; + *) fail "Unsupported CPU architecture '$(uname -m)' — this script installs amd64/arm64 binaries only." ;; + esac + + [[ -n "$OS" ]] || fail "Could not identify this distribution (no readable /etc/os-release)." + [[ -n "$OS_NAME" ]] || OS_NAME="$OS${OS_VERSION:+ $OS_VERSION}" +} + # ============================================================================= # 1. Pre-flight # ============================================================================= echo "" echo -e "${BOLD}╔══════════════════════════════════════════════════╗${NC}" -echo -e "${BOLD}║ Ubuntu Server Setup ║${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 + if [[ -f "$PROGRESS_FILE" ]]; then DONE_COUNT=$(wc -l < "$PROGRESS_FILE") echo -e "${YELLOW} Resuming — $DONE_COUNT step(s) already completed${NC}" @@ -101,7 +208,7 @@ if [[ -f "$PROGRESS_FILE" ]]; then fi if [[ "$EUID" -ne 0 ]]; then - fail "Please run as root: sudo ./setup-ubuntu.sh" + fail "Please run as root: sudo ./machine-setup.sh" fi prompt_value USERNAME "New admin username (or existing)" ""