#!/bin/bash # ============================================================================= # Officer — install # ============================================================================= # # One command, blank machine to running platform. It runs the two halves in # order and does nothing else itself: # # setup/machine-setup/machine-setup.sh a usable machine — packages, tailnet, # runtimes, docker, shell # setup/officer-setup.sh the platform on top of it — repo, # dependencies, postgres, .env, secret # store, schema, build, pm2 # # They stay two scripts because they answer two different questions and are worth # running separately: a machine you already trust needs only the second, and a # machine you are rebuilding needs only the first. This is the wrapper for the # case where you want both, which is most first runs. # # Both are re-runnable. Each remembers the steps it finished and skips them, so # stopping halfway and coming back costs nothing. # # Run it as yourself — it asks for administrator rights when it needs them. # set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" MACHINE="$SCRIPT_DIR/setup/machine-setup/machine-setup.sh" OFFICER="$SCRIPT_DIR/setup/officer-setup.sh" BOLD='\033[1m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' say() { echo -e "$*"; } die() { echo -e "${YELLOW}error:${NC} $*" >&2 exit 1 } [[ -r "$MACHINE" ]] || die "missing $MACHINE" [[ -r "$OFFICER" ]] || die "missing $OFFICER" # Which halves to run. Both by default. RUN_MACHINE=true RUN_OFFICER=true for arg in "$@"; do case "$arg" in --machine-only) RUN_OFFICER=false ;; --officer-only) RUN_MACHINE=false ;; -h | --help) say "usage: install.sh [--machine-only | --officer-only]" say "" say " no flags both halves, machine first" say " --machine-only stop after the machine is provisioned" say " --officer-only the platform only, on a machine you already trust" exit 0 ;; *) die "unknown option: $arg" ;; esac done KERNEL="$(uname -s)" case "$KERNEL" in Darwin) [[ "$EUID" -eq 0 ]] && die "do not run this with sudo on macOS — Homebrew refuses to run as root. Run it as yourself." ;; Linux) ;; *) die "unsupported system: $KERNEL. Officer installs on Linux and macOS." ;; esac SELF="$SCRIPT_DIR/install.sh" # One report for the whole run, not one per half. Both scripts append to this # file, so the person reviewing it sees a single account of what happened rather # than two they have to stitch together and hope are complete. # # Exported before either half starts, and timestamped once here — if each script # made its own name they would differ by however long the first one took. export REPORT_FILE="${REPORT_FILE:-${HOME}/officer-install-report-$(date '+%Y%m%d-%H%M%S').md}" # ── Privileges: asked for, not demanded ── # # Run this as YOURSELF. On Linux it needs root for apt, systemd units, useradd, # netplan, ufw and for creating directories owned by the service account — so it # asks, once, through sudo, and re-executes itself. Typing `sudo` yourself works # too and changes nothing, but it should not be the price of starting. # # Variables are passed to sudo explicitly rather than with -E. `env_reset` is the # sudoers default and strips the environment, which is how DATA_PATH was lost # once already; naming them on the command line survives it. # # macOS never escalates. Homebrew refuses to run as root, and nothing in the # macOS path needs it — the account running this IS the owner, so there is # nothing to chown and nothing to drop privileges to. if [[ "$KERNEL" != "Darwin" && "$EUID" -ne 0 ]]; then command -v sudo >/dev/null 2>&1 || die "this needs root and sudo is not installed — run it as root" say "" say " This needs administrator rights. You will be asked for your password." say "" exec sudo \ OFFICER_ROOT="${OFFICER_ROOT:-}" \ SETUP_USERNAME="${SETUP_USERNAME:-}" \ MACHINE_ROLE="${MACHINE_ROLE:-}" \ REPORT_FILE="${REPORT_FILE:-}" \ bash "$SELF" "$@" fi say "" say "${BOLD}Officer install${NC}" say " system: $KERNEL" $RUN_MACHINE && say " 1/2 machine setup" $RUN_OFFICER && say " $($RUN_MACHINE && echo 2/2 || echo 1/1) officer setup" say "" say " Either half can be run on its own later:" say " scripts/setup/machine-setup/machine-setup.sh" say " scripts/setup/officer-setup.sh" say "" # Not `set -e`'s job: a half that exits non-zero should say which half, and stop # before the next one starts on a machine that is not ready for it. if $RUN_MACHINE; then bash "$MACHINE" || die "machine setup did not finish — fix what it reported, then run this again" fi if $RUN_OFFICER; then bash "$OFFICER" || die "officer setup did not finish — fix what it reported, then run this again" fi say "" say "${GREEN}Done.${NC}"