Every run writes a timestamped install-report.md recording what was installed, changed, kept, skipped, started and run as root. Written for an adversarial read: the person who just ran a setup script off the internet hands it to an agent of their choosing and asks whether it did anything it should not have. Recorded by the HELPERS rather than by the sections. pkg_install and install_config report themselves, so anything installed or written through them appears whether or not a section author remembered — a section that has to remember is a section that will forget, and an incomplete report is worse than none because it reads as a full account. "Kept" is recorded as carefully as "changed". Leaving somebody's .zshrc alone is the claim a reviewer most wants substantiated, and it is invisible unless stated. Secrets are redacted at the moment of recording rather than filtered at render, so a credential never sits in memory formatted for printing. Verified against a POSTGRES_URL and an api_key/password pair. REPORT_FILE is passed through the sudo re-exec. It was not, first time, and the report silently vanished — the third variable this evening lost to env_reset. Unfinished on purpose, paused mid-task at the owner's request: machine-setup's 26 sections still only report through the two shared helpers, so the sections that change system state directly — systemd units, netplan, ufw, sshd drop-ins — are not yet recorded. That is the half a reviewer would care most about. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
134 lines
4.9 KiB
Bash
Executable File
134 lines
4.9 KiB
Bash
Executable File
#!/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}"
|