fix: quiet installer output for non-technical users; Makefile/compose cleanup
Unit Tests / test (push) Successful in 12m18s
Unit Tests / test (push) Successful in 12m18s
The installer dumped ~200 lines of docker layer spam, a leaked apt error, and obsolete version warnings, alarming for non-technical users. install.sh: - Clean, progress-only default output; full log to /var/log/pic-install.log - Admin password still surfaced on stdout at the end - PIC_DEBUG=1 / --debug flag restores verbose output - On error, prints the last 20 lines from the log file Makefile: - start / update / start-core compose invocations get @ prefix to suppress command echo, plus --quiet-pull to kill layer-download spam docker-compose.yml + docker-compose.services.yml: - Removed obsolete `version: '3.3'` top-level key (triggers deprecation warning with current Docker Compose) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -104,7 +104,7 @@ start:
|
|||||||
@echo "Starting Personal Internet Cell..."
|
@echo "Starting Personal Internet Cell..."
|
||||||
@docker network inspect cell-network >/dev/null 2>&1 || \
|
@docker network inspect cell-network >/dev/null 2>&1 || \
|
||||||
docker network create --driver bridge --subnet "$${CELL_NETWORK:-172.20.0.0/16}" cell-network
|
docker network create --driver bridge --subnet "$${CELL_NETWORK:-172.20.0.0/16}" cell-network
|
||||||
PUID=$$(id -u) PGID=$$(id -g) $(DCF) --profile core up -d --build
|
@PUID=$$(id -u) PGID=$$(id -g) $(DCF) --profile core up -d --build --quiet-pull
|
||||||
@echo "Services started. Check status with 'make status'"
|
@echo "Services started. Check status with 'make status'"
|
||||||
|
|
||||||
stop:
|
stop:
|
||||||
@@ -145,7 +145,7 @@ update:
|
|||||||
@echo "Rebuilding and restarting services..."
|
@echo "Rebuilding and restarting services..."
|
||||||
@docker network inspect cell-network >/dev/null 2>&1 || \
|
@docker network inspect cell-network >/dev/null 2>&1 || \
|
||||||
docker network create --driver bridge --subnet "$${CELL_NETWORK:-172.20.0.0/16}" cell-network
|
docker network create --driver bridge --subnet "$${CELL_NETWORK:-172.20.0.0/16}" cell-network
|
||||||
PUID=$$(id -u) PGID=$$(id -g) $(DCF) --profile core up -d --build
|
@PUID=$$(id -u) PGID=$$(id -g) $(DCF) --profile core up -d --build --quiet-pull
|
||||||
@echo "Update complete. Run 'make status' to verify."
|
@echo "Update complete. Run 'make status' to verify."
|
||||||
|
|
||||||
reinstall:
|
reinstall:
|
||||||
@@ -229,7 +229,7 @@ start-core:
|
|||||||
@echo "Starting core services (caddy, dns, wireguard, api, webui)..."
|
@echo "Starting core services (caddy, dns, wireguard, api, webui)..."
|
||||||
@docker network inspect cell-network >/dev/null 2>&1 || \
|
@docker network inspect cell-network >/dev/null 2>&1 || \
|
||||||
docker network create --driver bridge --subnet "$${CELL_NETWORK:-172.20.0.0/16}" cell-network
|
docker network create --driver bridge --subnet "$${CELL_NETWORK:-172.20.0.0/16}" cell-network
|
||||||
PUID=$$(id -u) PGID=$$(id -g) $(DCF) --profile core up -d --build
|
@PUID=$$(id -u) PGID=$$(id -g) $(DCF) --profile core up -d --build --quiet-pull
|
||||||
@echo "Core services started. Run 'make start' to also bring up optional services."
|
@echo "Core services started. Run 'make start' to also bring up optional services."
|
||||||
|
|
||||||
start-dns:
|
start-dns:
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
version: '3.3'
|
|
||||||
services: {}
|
services: {}
|
||||||
networks:
|
networks:
|
||||||
cell-network:
|
cell-network:
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
version: '3.3'
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
# Reverse Proxy - Caddy for routing all .cell traffic
|
# Reverse Proxy - Caddy for routing all .cell traffic
|
||||||
caddy:
|
caddy:
|
||||||
|
|||||||
+142
-37
@@ -42,19 +42,31 @@ API_HEALTH_URL="http://127.0.0.1:3000/health"
|
|||||||
API_HEALTH_TIMEOUT=60
|
API_HEALTH_TIMEOUT=60
|
||||||
WEBUI_PORT=8081
|
WEBUI_PORT=8081
|
||||||
FORCE=0
|
FORCE=0
|
||||||
|
PIC_DEBUG="${PIC_DEBUG:-0}"
|
||||||
|
|
||||||
# Parse flags
|
# Parse flags
|
||||||
for arg in "$@"; do
|
for arg in "$@"; do
|
||||||
case "$arg" in
|
case "$arg" in
|
||||||
--force) FORCE=1 ;;
|
--force) FORCE=1 ;;
|
||||||
|
--debug) PIC_DEBUG=1 ;;
|
||||||
*)
|
*)
|
||||||
echo "Unknown argument: $arg" >&2
|
echo "Unknown argument: $arg" >&2
|
||||||
echo "Usage: $0 [--force]" >&2
|
echo "Usage: $0 [--force] [--debug]" >&2
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Log file — /var/log/pic-install.log when writable (root via sudo), else /tmp
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
if touch /var/log/pic-install.log 2>/dev/null; then
|
||||||
|
LOGFILE="/var/log/pic-install.log"
|
||||||
|
else
|
||||||
|
LOGFILE="${TMPDIR:-/tmp}/pic-install.log"
|
||||||
|
fi
|
||||||
|
: > "$LOGFILE" # truncate / create
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Color output
|
# Color output
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
@@ -77,7 +89,68 @@ log_ok() { printf " ${GREEN}✓${RESET} %s\n" "$1"; }
|
|||||||
log_warn() { printf " ${YELLOW}⚠${RESET} %s\n" "$1"; }
|
log_warn() { printf " ${YELLOW}⚠${RESET} %s\n" "$1"; }
|
||||||
log_error() { printf "\n${RED}${BOLD}ERROR:${RESET}${RED} %s${RESET}\n" "$1" >&2; }
|
log_error() { printf "\n${RED}${BOLD}ERROR:${RESET}${RED} %s${RESET}\n" "$1" >&2; }
|
||||||
|
|
||||||
die() { log_error "$1"; exit 1; }
|
die() {
|
||||||
|
log_error "$1"
|
||||||
|
if [ "$PIC_DEBUG" -eq 0 ]; then
|
||||||
|
printf "\n${YELLOW}Last output (full log: %s):${RESET}\n" "$LOGFILE" >&2
|
||||||
|
tail -n 30 "$LOGFILE" | sed 's/^/ /' >&2
|
||||||
|
fi
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# run_step <label_in_progress> <label_done> <cmd> [args...]
|
||||||
|
#
|
||||||
|
# Default mode: redirect stdout+stderr to LOGFILE; print a single "in
|
||||||
|
# progress" line then overwrite it with a checkmark on success. On failure
|
||||||
|
# print the last 30 log lines and die.
|
||||||
|
#
|
||||||
|
# Debug mode (PIC_DEBUG=1): tee output to LOGFILE AND stdout (indented),
|
||||||
|
# print the done line at the end.
|
||||||
|
#
|
||||||
|
# TERM safety: when stdout is not a TTY the \r trick does not work, so we
|
||||||
|
# fall back to a plain two-line "... / done" style.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
_IS_TTY=0
|
||||||
|
[ -t 1 ] && _IS_TTY=1
|
||||||
|
|
||||||
|
run_step() {
|
||||||
|
local label_running="$1"
|
||||||
|
local label_done="$2"
|
||||||
|
shift 2
|
||||||
|
# "$@" is the command to run
|
||||||
|
|
||||||
|
if [ "$PIC_DEBUG" -eq 1 ]; then
|
||||||
|
printf " → %s\n" "$label_running"
|
||||||
|
# set -o pipefail: the pipeline below fails if "$@" fails, regardless
|
||||||
|
# of tee's or sed's exit code.
|
||||||
|
{ "$@" 2>&1 | tee -a "$LOGFILE" | sed 's/^/ /'; } || \
|
||||||
|
die "Command failed. See $LOGFILE for details."
|
||||||
|
log_ok "$label_done"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Default (quiet) mode
|
||||||
|
if [ "$_IS_TTY" -eq 1 ]; then
|
||||||
|
printf " → %s..." "$label_running"
|
||||||
|
else
|
||||||
|
printf " → %s...\n" "$label_running"
|
||||||
|
fi
|
||||||
|
|
||||||
|
local exit_code=0
|
||||||
|
"$@" >> "$LOGFILE" 2>&1 || exit_code=$?
|
||||||
|
|
||||||
|
if [ "$exit_code" -ne 0 ]; then
|
||||||
|
[ "$_IS_TTY" -eq 1 ] && printf "\n"
|
||||||
|
die "Step failed: $label_running"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$_IS_TTY" -eq 1 ]; then
|
||||||
|
printf "\r ${GREEN}✓${RESET} %-60s\n" "$label_done"
|
||||||
|
else
|
||||||
|
printf " ${GREEN}✓${RESET} %s\n" "$label_done"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
TOTAL_STEPS=7
|
TOTAL_STEPS=7
|
||||||
|
|
||||||
@@ -88,6 +161,9 @@ if ! command -v sudo >/dev/null 2>&1; then
|
|||||||
die "sudo is required. Install it and ensure your user has sudo access."
|
die "sudo is required. Install it and ensure your user has sudo access."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
printf " Full log: %s\n" "$LOGFILE"
|
||||||
|
[ "$PIC_DEBUG" -eq 1 ] && printf " ${YELLOW}Debug mode enabled — verbose output active${RESET}\n"
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Idempotency guard
|
# Idempotency guard
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
@@ -145,60 +221,72 @@ log_ok "Detected OS: ${OS_ID} (package manager: ${PKG_MANAGER})"
|
|||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
log_step 2 "Installing dependencies..."
|
log_step 2 "Installing dependencies..."
|
||||||
|
|
||||||
case "$PKG_MANAGER" in
|
_install_deps() {
|
||||||
|
case "$PKG_MANAGER" in
|
||||||
|
|
||||||
apt)
|
apt)
|
||||||
export DEBIAN_FRONTEND=noninteractive
|
export DEBIAN_FRONTEND=noninteractive
|
||||||
sudo apt-get update -qq
|
sudo apt-get update -qq
|
||||||
sudo apt-get install -y -qq git curl make docker.io docker-compose-plugin 2>&1 \
|
sudo apt-get install -y -qq git curl make docker.io docker-compose-plugin || true
|
||||||
| grep -v "^$" | sed 's/^/ /' || true
|
|
||||||
|
|
||||||
if ! docker compose version >/dev/null 2>&1; then
|
if ! docker compose version >/dev/null 2>&1; then
|
||||||
log_warn "docker-compose-plugin not available; falling back to standalone docker-compose"
|
sudo apt-get install -y -qq docker-compose || true
|
||||||
sudo apt-get install -y -qq docker-compose 2>&1 | grep -v "^$" | sed 's/^/ /' || true
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Ensure host clock is synchronised before DDNS/TOTP registration.
|
# Ensure host clock is synchronised before DDNS/TOTP registration.
|
||||||
# chrony is preferred; the service name differs by distro (chrony on Debian, chronyd on some Ubuntu).
|
sudo apt-get install -y -qq chrony || true
|
||||||
sudo apt-get install -y -qq chrony 2>&1 | grep -v "^$" | sed 's/^/ /' || true
|
|
||||||
if sudo systemctl enable --now chrony >/dev/null 2>&1; then
|
if sudo systemctl enable --now chrony >/dev/null 2>&1; then
|
||||||
log_ok "Host NTP (chrony) enabled and started"
|
: # NTP enabled
|
||||||
elif sudo systemctl enable --now chronyd >/dev/null 2>&1; then
|
elif sudo systemctl enable --now chronyd >/dev/null 2>&1; then
|
||||||
log_ok "Host NTP (chronyd) enabled and started"
|
: # NTP enabled
|
||||||
else
|
|
||||||
log_warn "Could not start chrony — verify host clock is accurate before running the setup wizard"
|
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
|
|
||||||
dnf)
|
dnf)
|
||||||
sudo dnf install -y -q git curl make docker 2>&1 | sed 's/^/ /' || true
|
sudo dnf install -y -q git curl make docker || true
|
||||||
|
|
||||||
sudo systemctl enable --now docker >/dev/null 2>&1 || true
|
sudo systemctl enable --now docker >/dev/null 2>&1 || true
|
||||||
|
|
||||||
if ! docker compose version >/dev/null 2>&1; then
|
if ! docker compose version >/dev/null 2>&1; then
|
||||||
log_warn "docker compose plugin not found; installing docker-compose-plugin..."
|
sudo dnf install -y -q docker-compose-plugin || true
|
||||||
sudo dnf install -y -q docker-compose-plugin 2>&1 | sed 's/^/ /' || true
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
sudo dnf install -y -q chrony 2>&1 | sed 's/^/ /' || true
|
sudo dnf install -y -q chrony || true
|
||||||
sudo systemctl enable --now chronyd >/dev/null 2>&1 \
|
sudo systemctl enable --now chronyd >/dev/null 2>&1 || true
|
||||||
&& log_ok "Host NTP (chronyd) enabled and started" \
|
|
||||||
|| log_warn "Could not start chronyd — verify host clock is accurate before running the setup wizard"
|
|
||||||
;;
|
;;
|
||||||
|
|
||||||
apk)
|
apk)
|
||||||
sudo apk add --quiet git curl make docker docker-cli-compose 2>&1 | sed 's/^/ /' || true
|
sudo apk add --quiet git curl make docker docker-cli-compose || true
|
||||||
|
|
||||||
sudo rc-update add docker default >/dev/null 2>&1 || true
|
sudo rc-update add docker default >/dev/null 2>&1 || true
|
||||||
sudo service docker start >/dev/null 2>&1 || true
|
sudo service docker start >/dev/null 2>&1 || true
|
||||||
|
|
||||||
sudo apk add --quiet chrony 2>&1 | sed 's/^/ /' || true
|
sudo apk add --quiet chrony || true
|
||||||
sudo rc-update add chronyd default >/dev/null 2>&1 || true
|
sudo rc-update add chronyd default >/dev/null 2>&1 || true
|
||||||
sudo service chronyd start >/dev/null 2>&1 \
|
sudo service chronyd start >/dev/null 2>&1 || true
|
||||||
&& log_ok "Host NTP (chronyd) enabled and started" \
|
|
||||||
|| log_warn "Could not start chronyd — verify host clock is accurate before running the setup wizard"
|
|
||||||
;;
|
;;
|
||||||
|
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
run_step "Installing system packages" "System packages installed" _install_deps
|
||||||
|
|
||||||
|
# Report NTP status (informational, outside the noisy run_step)
|
||||||
|
case "$PKG_MANAGER" in
|
||||||
|
apt)
|
||||||
|
if sudo systemctl is-active --quiet chrony 2>/dev/null || \
|
||||||
|
sudo systemctl is-active --quiet chronyd 2>/dev/null; then
|
||||||
|
log_ok "Host NTP (chrony) is running"
|
||||||
|
else
|
||||||
|
log_warn "Could not start chrony — verify host clock is accurate before running the setup wizard"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
dnf|apk)
|
||||||
|
if sudo systemctl is-active --quiet chronyd 2>/dev/null || \
|
||||||
|
sudo service chronyd status >/dev/null 2>&1; then
|
||||||
|
log_ok "Host NTP (chronyd) is running"
|
||||||
|
else
|
||||||
|
log_warn "Could not start chronyd — verify host clock is accurate before running the setup wizard"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
# Final sanity checks
|
# Final sanity checks
|
||||||
@@ -253,14 +341,14 @@ log_step 4 "Setting up repository at ${PIC_DIR}..."
|
|||||||
|
|
||||||
if [ -d "${PIC_DIR}/.git" ]; then
|
if [ -d "${PIC_DIR}/.git" ]; then
|
||||||
log_warn "Repository already cloned — running git pull"
|
log_warn "Repository already cloned — running git pull"
|
||||||
git -C "$PIC_DIR" pull --ff-only 2>&1 | sed 's/^/ /'
|
run_step "Updating repository" "Repository updated" \
|
||||||
log_ok "Repository updated"
|
git -C "$PIC_DIR" pull --ff-only
|
||||||
elif [ -d "$PIC_DIR" ] && [ "$(ls -A "$PIC_DIR" 2>/dev/null)" ]; then
|
elif [ -d "$PIC_DIR" ] && [ "$(ls -A "$PIC_DIR" 2>/dev/null)" ]; then
|
||||||
die "${PIC_DIR} exists and is not empty and is not a git repo. Aborting to avoid data loss."
|
die "${PIC_DIR} exists and is not empty and is not a git repo. Aborting to avoid data loss."
|
||||||
else
|
else
|
||||||
mkdir -p "$(dirname "$PIC_DIR")"
|
mkdir -p "$(dirname "$PIC_DIR")"
|
||||||
git clone "$PIC_REPO" "$PIC_DIR" 2>&1 | sed 's/^/ /'
|
run_step "Cloning repository" "Repository cloned to ${PIC_DIR}" \
|
||||||
log_ok "Repository cloned to ${PIC_DIR}"
|
git clone "$PIC_REPO" "$PIC_DIR"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
sudo git config --system --add safe.directory "$PIC_DIR" 2>/dev/null || true
|
sudo git config --system --add safe.directory "$PIC_DIR" 2>/dev/null || true
|
||||||
@@ -268,12 +356,28 @@ sudo git config --system --add safe.directory "$PIC_DIR" 2>/dev/null || true
|
|||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Step 5 — Run make install
|
# Step 5 — Run make install
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
log_step 5 "Running 'make install'..."
|
log_step 5 "Generating configuration..."
|
||||||
|
|
||||||
cd "$PIC_DIR"
|
cd "$PIC_DIR"
|
||||||
|
|
||||||
if ! make install 2>&1 | sed 's/^/ /'; then
|
# run_step routes all output to LOGFILE. After it returns we scan LOGFILE
|
||||||
die "'make install' failed. Check the output above."
|
# for the admin password banner (printed once by setup_cell.py) and relay it
|
||||||
|
# to the user — it must never be silently buried in the log.
|
||||||
|
# We record the log byte-offset before the step so we only scan new output.
|
||||||
|
_LOG_OFFSET_BEFORE="$(wc -c < "$LOGFILE" 2>/dev/null || echo 0)"
|
||||||
|
|
||||||
|
run_step "Generating configuration" "Configuration generated" make install
|
||||||
|
|
||||||
|
# Extract only the lines added by this step.
|
||||||
|
_NEW_LOG="$(tail -c +"$(( _LOG_OFFSET_BEFORE + 1 ))" "$LOGFILE" 2>/dev/null || true)"
|
||||||
|
|
||||||
|
# Relay admin password banner if present.
|
||||||
|
if printf '%s\n' "$_NEW_LOG" | grep -qiE "(ADMIN PASSWORD|shown once)"; then
|
||||||
|
printf "\n"
|
||||||
|
printf '%s\n' "$_NEW_LOG" \
|
||||||
|
| awk '/ADMIN PASSWORD|shown once|={6}/{found=1} found{print} found && /^[[:space:]]*$/{exit}' \
|
||||||
|
| sed 's/^/ /'
|
||||||
|
printf "\n"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
log_ok "'make install' complete"
|
log_ok "'make install' complete"
|
||||||
@@ -285,9 +389,10 @@ log_step 6 "Starting core services..."
|
|||||||
|
|
||||||
cd "$PIC_DIR"
|
cd "$PIC_DIR"
|
||||||
|
|
||||||
if ! make start-core 2>&1 | sed 's/^/ /'; then
|
run_step \
|
||||||
die "'make start-core' failed. Check the output above."
|
"Downloading container images (first run can take a few minutes)" \
|
||||||
fi
|
"Container images ready" \
|
||||||
|
make start-core
|
||||||
|
|
||||||
log_ok "Core services started"
|
log_ok "Core services started"
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user