Centralize demo config in .env and add bootstrap-stack.sh
Expand .env.example with ports, AI, Usenet, WireGuard, demo logins, and bootstrap flags. One script creates dirs, downloads the model, starts compose, heals gluetun, and configures SABnzbd from the same file.
This commit is contained in:
parent
56ddb349b4
commit
3bf1726a99
7 changed files with 410 additions and 76 deletions
166
scripts/bootstrap-stack.sh
Executable file
166
scripts/bootstrap-stack.sh
Executable file
|
|
@ -0,0 +1,166 @@
|
|||
#!/usr/bin/env bash
|
||||
# Fresh / demo stack bring-up driven almost entirely by .env
|
||||
#
|
||||
# cp .env.example .env # fill usenet keys + STACK_HOST
|
||||
# # drop wireguard/wg0.conf
|
||||
# ./scripts/bootstrap-stack.sh
|
||||
#
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
STACK_DIR="${STACK_DIR:-$(cd "$SCRIPT_DIR/.." && pwd)}"
|
||||
cd "$STACK_DIR"
|
||||
|
||||
log() { echo "[bootstrap] $*"; }
|
||||
die() { log "ERROR: $*"; exit 1; }
|
||||
|
||||
[[ -f "$STACK_DIR/.env" ]] || die "missing .env — cp .env.example .env and edit"
|
||||
|
||||
set -a
|
||||
# shellcheck disable=SC1091
|
||||
source "$STACK_DIR/.env"
|
||||
set +a
|
||||
|
||||
# Expand nested ${STACK_HOST} style leftovers if someone left them literal
|
||||
STACK_HOST="${STACK_HOST:-192.168.8.123}"
|
||||
DEMO_USER="${DEMO_USER:-tim}"
|
||||
DEMO_PASS="${DEMO_PASS:-asdfasdf}"
|
||||
CONFIG_DIR="${CONFIG_DIR:-$STACK_DIR/config}"
|
||||
DATA_DIR="${DATA_DIR:-$STACK_DIR/data}"
|
||||
BONSAI_MODELS_DIR="${BONSAI_MODELS_DIR:-$STACK_DIR/models/bonsai}"
|
||||
AI_WORKSPACE_DIR="${AI_WORKSPACE_DIR:-$STACK_DIR/workspace}"
|
||||
DOWNLOAD_MODEL="${DOWNLOAD_MODEL:-true}"
|
||||
CONFIGURE_USENET="${CONFIGURE_USENET:-true}"
|
||||
HEAL_GLUETUN="${HEAL_GLUETUN:-true}"
|
||||
COMPOSE_PULL="${COMPOSE_PULL:-true}"
|
||||
START_OPENHANDS="${START_OPENHANDS:-false}"
|
||||
BONSAI_BACKEND="${BONSAI_BACKEND:-vulkan}"
|
||||
|
||||
export STACK_DIR CONFIG_DIR DATA_DIR BONSAI_MODELS_DIR AI_WORKSPACE_DIR
|
||||
|
||||
log "STACK_DIR=$STACK_DIR host=$STACK_HOST demo_user=$DEMO_USER"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Directories
|
||||
# ---------------------------------------------------------------------------
|
||||
log "Creating data / config trees"
|
||||
mkdir -p \
|
||||
"$CONFIG_DIR" \
|
||||
"$BONSAI_MODELS_DIR" \
|
||||
"$AI_WORKSPACE_DIR" \
|
||||
"$DATA_DIR"/media/{movies,tv,music} \
|
||||
"$DATA_DIR"/torrents/{movies,tv,music} \
|
||||
"$DATA_DIR"/usenet/incomplete \
|
||||
"$DATA_DIR"/usenet/complete/{movies,tv,music} \
|
||||
"$STACK_DIR/wireguard" \
|
||||
"$STACK_DIR/secrets"
|
||||
|
||||
# Prefer main .env for usenet if secrets/.env missing
|
||||
if [[ ! -f "$STACK_DIR/secrets/.env" && -f "$STACK_DIR/.env" ]]; then
|
||||
log "Using root .env as secrets source for usenet (DEMO_MODE)"
|
||||
# configure-usenet already can source SECRETS_FILE; symlink for convenience
|
||||
ln -sfn ../.env "$STACK_DIR/secrets/.env" 2>/dev/null || \
|
||||
cp "$STACK_DIR/.env" "$STACK_DIR/secrets/.env"
|
||||
fi
|
||||
|
||||
if [[ ! -f "$STACK_DIR/wireguard/wg0.conf" ]]; then
|
||||
log "WARNING: wireguard/wg0.conf missing — gluetun/qbittorrent will stay unhealthy until you add it"
|
||||
else
|
||||
# Ensure EndpointHost comment for the watcher
|
||||
if [[ -n "${WIREGUARD_ENDPOINT_HOST:-}" ]] && ! grep -q EndpointHost "$STACK_DIR/wireguard/wg0.conf"; then
|
||||
sed -i "1a # EndpointHost = ${WIREGUARD_ENDPOINT_HOST}" "$STACK_DIR/wireguard/wg0.conf" || true
|
||||
fi
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Model download
|
||||
# ---------------------------------------------------------------------------
|
||||
if [[ "${DOWNLOAD_MODEL}" == "true" || "${DOWNLOAD_MODEL}" == "1" ]]; then
|
||||
if [[ ! -f "$BONSAI_MODELS_DIR/${BONSAI_MAIN_FILE:-Ternary-Bonsai-27B-Q2_0.gguf}" ]]; then
|
||||
log "Downloading Ternary-Bonsai GGUF (~7GB)…"
|
||||
MODEL_DIR="$BONSAI_MODELS_DIR" \
|
||||
BONSAI_HF_REPO="${BONSAI_HF_REPO:-prism-ml/Ternary-Bonsai-27B-gguf}" \
|
||||
BONSAI_MAIN_FILE="${BONSAI_MAIN_FILE:-Ternary-Bonsai-27B-Q2_0.gguf}" \
|
||||
DOWNLOAD_VISION="${DOWNLOAD_VISION:-1}" \
|
||||
DOWNLOAD_DRAFTER="${DOWNLOAD_DRAFTER:-0}" \
|
||||
"$SCRIPT_DIR/download-bonsai-model.sh"
|
||||
else
|
||||
log "Model already present — skip download"
|
||||
fi
|
||||
else
|
||||
log "DOWNLOAD_MODEL=false — skip model download"
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Docker compose
|
||||
# ---------------------------------------------------------------------------
|
||||
PROFILES=()
|
||||
if [[ -n "${COMPOSE_PROFILES:-}" ]]; then
|
||||
# shellcheck disable=SC2206
|
||||
PROFILES=(--profile ${COMPOSE_PROFILES//,/ --profile })
|
||||
fi
|
||||
if [[ "$START_OPENHANDS" == "true" || "$START_OPENHANDS" == "1" ]]; then
|
||||
PROFILES+=(--profile coding)
|
||||
fi
|
||||
|
||||
if [[ "$COMPOSE_PULL" == "true" || "$COMPOSE_PULL" == "1" ]]; then
|
||||
log "docker compose pull"
|
||||
docker compose "${PROFILES[@]}" pull || true
|
||||
fi
|
||||
|
||||
log "Building bonsai (BONSAI_BACKEND=$BONSAI_BACKEND)"
|
||||
docker compose build bonsai
|
||||
|
||||
log "docker compose up -d"
|
||||
docker compose "${PROFILES[@]}" up -d
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Heal gluetun if needed
|
||||
# ---------------------------------------------------------------------------
|
||||
if [[ "$HEAL_GLUETUN" == "true" || "$HEAL_GLUETUN" == "1" ]]; then
|
||||
if [[ -x "$SCRIPT_DIR/gluetun-endpoint-watch.sh" ]]; then
|
||||
log "Running gluetun endpoint watch (no-op if healthy)"
|
||||
STACK_DIR="$STACK_DIR" "$SCRIPT_DIR/gluetun-endpoint-watch.sh" || log "gluetun watch returned non-zero (VPN may still need wg0.conf)"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Usenet bootstrap
|
||||
# ---------------------------------------------------------------------------
|
||||
if [[ "$CONFIGURE_USENET" == "true" || "$CONFIGURE_USENET" == "1" ]]; then
|
||||
log "Waiting for SABnzbd…"
|
||||
for i in $(seq 1 60); do
|
||||
if curl -fsS "http://127.0.0.1:${SABNZBD_PORT:-8085}/api?mode=version&output=json" >/dev/null 2>&1; then
|
||||
break
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
if [[ -x "$SCRIPT_DIR/configure-usenet.sh" ]]; then
|
||||
SECRETS_FILE="${SECRETS_FILE:-$STACK_DIR/secrets/.env}" \
|
||||
SAB_URL="http://127.0.0.1:${SABNZBD_PORT:-8085}" \
|
||||
"$SCRIPT_DIR/configure-usenet.sh" || log "configure-usenet failed — check SABnzbd wizard / secrets"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Summary
|
||||
# ---------------------------------------------------------------------------
|
||||
log ""
|
||||
log "=== Stack is up (or starting) ==="
|
||||
docker compose ps --format 'table {{.Name}}\t{{.Status}}\t{{.Ports}}' 2>/dev/null || docker compose ps
|
||||
log ""
|
||||
log "Landing: http://${STACK_HOST}/"
|
||||
log "Jellyfin: http://${STACK_HOST}:8096"
|
||||
log "Open WebUI: http://${STACK_HOST}:3000 (first user = admin; use ${DEMO_USER} / ${DEMO_PASS})"
|
||||
log "Forgejo: http://${STACK_HOST}:3002 (install wizard → admin ${DEMO_USER} / ${DEMO_PASS})"
|
||||
log "SABnzbd: http://${STACK_HOST}:${SABNZBD_PORT:-8085}"
|
||||
log "Prowlarr: http://${STACK_HOST}:9696"
|
||||
log "Bonsai: http://${STACK_HOST}:${BONSAI_PORT:-8081}/v1/models"
|
||||
log ""
|
||||
log "Still one-time UI wiring (APIs vary by version):"
|
||||
log " • Prowlarr → NZBgeek + SABnzbd download client → sync to apps"
|
||||
log " • Sonarr/Radarr/Lidarr → root folders /data/media/* + SABnzbd categories"
|
||||
log " • qBittorrent → categories + password ${DEMO_PASS} after first login"
|
||||
log " • Jellyfin → libraries under /data/media/*"
|
||||
log ""
|
||||
log "Demo login for apps you configure: user=${DEMO_USER} pass=${DEMO_PASS}"
|
||||
|
|
@ -15,7 +15,12 @@ SAB_URL="${SAB_URL:-http://127.0.0.1:8085}"
|
|||
log() { echo "[configure-usenet] $*"; }
|
||||
die() { log "ERROR: $*"; exit 1; }
|
||||
|
||||
[[ -f "$SECRETS_FILE" ]] || die "missing $SECRETS_FILE (see secrets/.env.example)"
|
||||
# Prefer secrets/.env; fall back to root .env (tech-demo: everything in one file)
|
||||
if [[ ! -f "$SECRETS_FILE" && -f "$STACK_DIR/.env" ]]; then
|
||||
SECRETS_FILE="$STACK_DIR/.env"
|
||||
log "using $SECRETS_FILE (no secrets/.env)"
|
||||
fi
|
||||
[[ -f "$SECRETS_FILE" ]] || die "missing $SECRETS_FILE (see secrets/.env.example or .env.example)"
|
||||
|
||||
# shellcheck disable=SC1090
|
||||
set -a
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue