#!/usr/bin/env bash # nhanhvps :: install/online.sh -- the one-liner entry point # # curl -fsSL https://your-host/nhanhvps | bash # curl -fsSL https://your-host/nhanhvps | bash -s -- --no-site # # This file is meant to be served over HTTP and piped into bash, so it must not # assume anything about where it lives ($BASH_SOURCE is empty in that case). # It does three things: refuse to run anywhere it does not belong, fetch and # verify the payload, then hand over to setup.sh --wizard with a real terminal # attached. set -o pipefail # Where the release tarball lives. # # There is deliberately no address hardcoded here. A plausible-looking default # (https://download..com/...) is worse than none: it belongs to nobody, # it does not resolve, and the operator gets "could not resolve host" instead of # being told what to do. Set it when you publish: # # NHANHVPS_URL=https://your-host/nhanhvps-latest.tar.gz # # or bake it into the copy you serve by editing this line. --url still wins. DEFAULT_URL="https://nhanhvps.com/nhanhvps-1.0.0-rc3.tar.gz" C_RESET=$'\033[0m'; C_B=$'\033[1m'; C_R=$'\033[31m'; C_Y=$'\033[33m'; C_G=$'\033[32m' say() { printf '%s\n' "$*"; } step() { printf '\n%s== %s ==%s\n' "$C_B" "$*" "$C_RESET"; } ok() { printf ' %sok%s %s\n' "$C_G" "$C_RESET" "$*"; } warn() { printf ' %swarn%s %s\n' "$C_Y" "$C_RESET" "$*"; } die() { printf '\n%sFAIL%s %s\n' "$C_R" "$C_RESET" "$*" >&2; exit 1; } URL=$DEFAULT_URL SRC_DIR="" PASSTHRU=() while [ $# -gt 0 ]; do case $1 in --url) [ -n "${2:-}" ] || die "--url needs a value"; URL=$2; shift 2 ;; --source) [ -n "${2:-}" ] || die "--source needs a value"; SRC_DIR=$2; shift 2 ;; -h|--help) cat <<'EOF' Usage: curl -fsSL | bash -s -- [options] --url
tarball to install (default: baked in when released) --source use a local source tree instead of downloading passed straight to setup.sh --wizard Examples: curl -fsSL https://host/nhanhvps | bash curl -fsSL https://host/nhanhvps | bash -s -- --no-site --yes EOF exit 0 ;; *) PASSTHRU+=("$1"); shift ;; esac done say "nhanhvps installer $(date -u '+%Y-%m-%d %H:%M:%SZ')" # --------------------------------------------------------------------------- # 1. this must be a root shell on the right distribution # --------------------------------------------------------------------------- step "Pre-checks" [ "$(id -u)" -eq 0 ] || die "Run this as root: curl -fsSL $URL | sudo bash" if [ ! -r /etc/os-release ]; then die "/etc/os-release is missing, this does not look like Ubuntu" fi # shellcheck disable=SC1091 . /etc/os-release if [ "${ID:-}" != "ubuntu" ] || [ "${VERSION_ID:-}" != "24.04" ]; then die "Ubuntu 24.04 LTS only, this host is ${PRETTY_NAME:-unknown}" fi ok "${PRETTY_NAME}" command -v systemctl >/dev/null 2>&1 || die "systemd is required" [ -d /run/systemd/system ] || die "systemd is not running" ok "systemd" # The panel needs a terminal to ask its questions. When the script arrives # through a pipe, stdin is the script itself, so it has to be re-pointed. if [ -z "$SRC_DIR" ]; then if [ ! -t 0 ] && [ ! -c /dev/tty ]; then warn "no terminal: every question will take its default value" fi fi for c in curl tar; do command -v "$c" >/dev/null 2>&1 && continue apt-get update -qq >/dev/null 2>&1 || die "cannot run apt-get update" apt-get install -y -qq --no-install-recommends "$c" || die "cannot install $c" done ok "curl, tar" # --------------------------------------------------------------------------- # 2. fetch and verify the payload # --------------------------------------------------------------------------- RESOLVED="" if [ -z "$SRC_DIR" ]; then step "Fetching the installer" if [ -z "$URL" ]; then cat >&2 <<'MSG' FAIL This copy of the script does not know where the release lives. Pass the address when you run it: curl -fsSL | sudo bash -s -- \ --url https://your-host/nhanhvps-latest.tar.gz or set NHANHVPS_URL before serving it, or install from a local tree: sudo bash install/online.sh --source /root/nhanhvps MSG exit 1 fi WORK=$(mktemp -d /tmp/nhanhvps-boot.XXXXXX) || die "cannot create a work directory" say " $URL" if ! curl -fsSL --retry 3 --connect-timeout 15 --max-time 600 \ -o "$WORK/src.tar.gz" "$URL"; then rm -rf "$WORK" die "cannot download $URL" fi # A checksum is only useful if it comes from somewhere else, but a # sidecar file catches truncated downloads and stale caches, which is # what actually goes wrong in practice. if curl -fsSL --max-time 30 -o "$WORK/src.sha256" "$URL.sha256" 2>/dev/null; then want=$(awk '{print $1}' "$WORK/src.sha256" | head -n 1) got=$(sha256sum "$WORK/src.tar.gz" | awk '{print $1}') if [ -n "$want" ] && [ "$want" != "$got" ]; then rm -rf "$WORK" die "checksum mismatch: expected $want, got $got" fi ok "sha256 verified" else warn "no .sha256 published, continuing without a checksum" fi tar -xzf "$WORK/src.tar.gz" -C "$WORK" || { rm -rf "$WORK"; die "the archive is not a valid tar.gz"; } rm -f "$WORK/src.tar.gz" "$WORK/src.sha256" RESOLVED=$WORK # the archive usually contains a single top-level directory if [ ! -f "$RESOLVED/bin/nhanhvps" ]; then inner=$(find "$RESOLVED" -maxdepth 2 -name VERSION -printf '%h\n' 2>/dev/null | head -n 1) [ -n "$inner" ] && RESOLVED=$inner fi else step "Using a local source tree" RESOLVED=$(readlink -f "$SRC_DIR") fi [ -f "$RESOLVED/VERSION" ] || die "the payload does not look like nhanhvps (no VERSION file in $RESOLVED)" [ -x "$RESOLVED/setup.sh" ] || [ -f "$RESOLVED/setup.sh" ] || die "setup.sh is missing from $RESOLVED" ok "nhanhvps $(cat "$RESOLVED/VERSION")" # --------------------------------------------------------------------------- # 3. hand over # --------------------------------------------------------------------------- step "Starting the installer" say " the questions come next; Ctrl-C is safe until the install stage" # `[ -c /dev/tty ]` only proves the device node exists. Reading from a terminal # this process does not control stops it with SIGTTIN, so ask the kernel: `tty` # succeeds only when stdin really is a terminal. if (tty /dev/null 2>&1; then exec bash "$RESOLVED/setup.sh" --wizard "${PASSTHRU[@]}"