Files
ubuntu-post-install/vendor/ai-stack/configure-storage.sh
T
Claude b2d064573f Move ai-stack and paintplus vendored source under vendor/
Matches the existing vendor/easy-asterisk convention (used by
services/asterisk.sh) instead of two one-off top-level directories that
cluttered the repo root and didn't look like anything else next to
setup.sh, lib/, services/, extras/. Only the two services' own SRC_DIR
path resolution and header comments needed updating — nothing else in
the repo referenced the old ./ai-stack / ./paintplus paths.

Also documents vendor/ in README.md's Layout section.
2026-08-04 12:55:09 +00:00

240 lines
9.2 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# configure-storage.sh — interactively assign drives for ZIM and model storage
# then patches docker-compose.yml in place
set -euo pipefail
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
CYAN='\033[0;36m'; BOLD='\033[1m'; NC='\033[0m'
info() { echo -e "${CYAN}[..]${NC} $*"; }
ok() { echo -e "${GREEN}[OK]${NC} $*"; }
warn() { echo -e "${YELLOW}[!!]${NC} $*"; }
die() { echo -e "${RED}[ERR]${NC} $*"; exit 1; }
BASE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
COMPOSE="$BASE/docker-compose.yml"
[[ -f "$COMPOSE" ]] || die "docker-compose.yml not found at $BASE — run local-ai-setup.sh first"
# ── collect drives ─────────────────────────────────────────────────────────────
echo -e "\n${BOLD}━━━ Detected Drives ━━━${NC}\n"
# build array of mountpoints excluding tiny/system ones
mapfile -t MOUNTS < <(
df -h --output=target,size,avail,pcent 2>/dev/null \
| tail -n +2 \
| grep -v -E '^\s*(/$|/boot|/sys|/proc|/dev|/run|/snap|/var/lib/docker|/etc|tmpfs)' \
| awk '$3 ~ /[0-9]/ {print}' \
| sort -u
)
if [[ ${#MOUNTS[@]} -eq 0 ]]; then
warn "No additional mounted drives found beyond system disk."
warn "Mount your drives first (e.g. /mnt/storage), then re-run this script."
echo ""
echo "Quick mount example:"
echo " sudo mkdir -p /mnt/storage"
echo " sudo mount /dev/sdb1 /mnt/storage"
echo " # To make permanent, add to /etc/fstab"
exit 0
fi
# display numbered list
echo -e " ${BOLD}# Mount Point Size Free Used${NC}"
echo " ─────────────────────────────────────────────────────"
IDX=0
declare -a MOUNT_PATHS
for line in "${MOUNTS[@]}"; do
mp=$(echo "$line" | awk '{print $1}')
sz=$(echo "$line" | awk '{print $2}')
av=$(echo "$line" | awk '{print $3}')
pc=$(echo "$line" | awk '{print $4}')
printf " ${CYAN}%-3d${NC} %-30s %-7s %-7s %s\n" "$IDX" "$mp" "$sz" "$av" "$pc"
MOUNT_PATHS[$IDX]="$mp"
(( IDX++ ))
done
echo ""
pick_drive() {
local purpose="$1"
local varname="$2"
local skip_ok="${3:-true}"
echo -e "${BOLD}$purpose storage${NC}"
if $skip_ok; then
read -rp " Enter drive number (or Enter to skip): " choice
else
read -rp " Enter drive number: " choice
fi
if [[ -z "$choice" ]]; then
eval "$varname=''"
info "Skipping $purpose storage."
elif [[ "$choice" =~ ^[0-9]+$ ]] && [[ "$choice" -lt "$IDX" ]]; then
eval "$varname='${MOUNT_PATHS[$choice]}'"
ok "Selected: ${MOUNT_PATHS[$choice]}"
else
warn "Invalid choice — skipping $purpose."
eval "$varname=''"
fi
echo ""
}
# ── ZIM storage ────────────────────────────────────────────────────────────────
echo -e "${BOLD}━━━ Kiwix ZIM Storage ━━━${NC}\n"
echo " ZIM files can be large (Wikipedia alone is ~100GB)."
echo " Pick a drive with plenty of space."
echo ""
pick_drive "ZIM" ZIM_DRIVE
ZIM_PATH=""
if [[ -n "$ZIM_DRIVE" ]]; then
read -rp " Folder name on that drive [zims]: " ZIM_FOLDER
ZIM_FOLDER="${ZIM_FOLDER:-zims}"
ZIM_PATH="$ZIM_DRIVE/$ZIM_FOLDER"
mkdir -p "$ZIM_PATH"
ok "ZIM path: $ZIM_PATH"
echo ""
fi
# ── Model storage ──────────────────────────────────────────────────────────────
echo -e "${BOLD}━━━ Ollama Model Storage ━━━${NC}\n"
echo " Models range from 4GB (7B) to 30GB+ (70B)."
echo " Pick a fast drive (SSD preferred) with 50200GB free."
echo ""
pick_drive "Model" MODEL_DRIVE
MODEL_PATH=""
if [[ -n "$MODEL_DRIVE" ]]; then
read -rp " Folder name on that drive [ollama-models]: " MODEL_FOLDER
MODEL_FOLDER="${MODEL_FOLDER:-ollama-models}"
MODEL_PATH="$MODEL_DRIVE/$MODEL_FOLDER"
mkdir -p "$MODEL_PATH"
ok "Model path: $MODEL_PATH"
echo ""
fi
# ── bail if nothing selected ───────────────────────────────────────────────────
if [[ -z "$ZIM_PATH" && -z "$MODEL_PATH" ]]; then
info "Nothing selected — no changes made."
exit 0
fi
# ── summary + confirm ──────────────────────────────────────────────────────────
echo -e "${BOLD}━━━ Planned Changes ━━━${NC}\n"
[[ -n "$ZIM_PATH" ]] && echo " Kiwix ZIMs → $ZIM_PATH"
[[ -n "$MODEL_PATH" ]] && echo " Ollama models → $MODEL_PATH"
echo ""
read -rp "Apply these changes to docker-compose.yml? [Y/n]: " CONFIRM
[[ "${CONFIRM,,}" == "n" ]] && { info "Aborted — no changes made."; exit 0; }
# ── backup ────────────────────────────────────────────────────────────────────
cp "$COMPOSE" "$COMPOSE.bak"
info "Backed up to docker-compose.yml.bak"
# ── patch kiwix volumes ────────────────────────────────────────────────────────
if [[ -n "$ZIM_PATH" ]]; then
# Replace the kiwix volumes line to add extra ZIM mounts
# Current: volumes: [$BASE/kiwix:/data]
# New: volumes:
# - $BASE/kiwix:/data
# - /mnt/drive/zims:/data/external
python3 - "$COMPOSE" "$BASE" "$ZIM_PATH" << 'PY'
import sys, re
compose_file = sys.argv[1]
base = sys.argv[2]
zim_path = sys.argv[3]
text = open(compose_file).read()
# Find the kiwix service volumes line (single-line array style)
old = f" volumes: [{base}/kiwix:/data]"
new = (
f" volumes:\n"
f" - {base}/kiwix:/data\n"
f" - {zim_path}:/data/external"
)
if old in text:
text = text.replace(old, new)
open(compose_file, "w").write(text)
print(f"[OK] Kiwix volumes updated")
else:
# Already multi-line — append the new mount if not already there
marker = "container_name: kiwix"
if marker in text and zim_path not in text:
# find the volumes block under kiwix and append
lines = text.splitlines()
in_kiwix = False
in_vols = False
insert_after = -1
for i, line in enumerate(lines):
if marker in line:
in_kiwix = True
if in_kiwix and "volumes:" in line:
in_vols = True
if in_vols and line.strip().startswith("- ") and ":/data" in line:
insert_after = i
if in_vols and insert_after > 0 and not line.strip().startswith("- "):
break
if insert_after > 0:
lines.insert(insert_after + 1, f" - {zim_path}:/data/external")
open(compose_file, "w").write("\n".join(lines) + "\n")
print(f"[OK] Kiwix extra ZIM volume appended")
else:
print(f"[!!] Could not patch kiwix volumes — edit manually: add - {zim_path}:/data/external")
else:
print(f"[!!] Kiwix volumes line not found in expected format — edit manually")
PY
fi
# ── patch ollama volumes ───────────────────────────────────────────────────────
if [[ -n "$MODEL_PATH" ]]; then
python3 - "$COMPOSE" "$MODEL_PATH" << 'PY'
import sys
compose_file = sys.argv[1]
model_path = sys.argv[2]
text = open(compose_file).read()
# Replace Docker-managed volume with host path
old = " volumes: [ollama-models:/root/.ollama]"
new = f" volumes: [{model_path}:/root/.ollama]"
if old in text:
text = text.replace(old, new)
# Remove ollama-models from the top-level volumes section if present
text = text.replace(" ollama-models:\n", "")
open(compose_file, "w").write(text)
print(f"[OK] Ollama volume → {model_path}")
else:
print(f"[!!] ollama volumes line not in expected format — edit manually:")
print(f" change ollama-models:/root/.ollama to {model_path}:/root/.ollama")
PY
fi
# ── restart if running ─────────────────────────────────────────────────────────
echo ""
if docker compose -f "$COMPOSE" ps --quiet 2>/dev/null | grep -q .; then
read -rp "Stack is running — restart now to apply changes? [Y/n]: " RESTART
if [[ "${RESTART,,}" != "n" ]]; then
cd "$BASE"
docker compose down
docker compose up -d
ok "Stack restarted"
else
warn "Remember to restart: cd $BASE && docker compose down && docker compose up -d"
fi
else
info "Stack not running — changes will apply on next start."
fi
echo ""
echo -e "${GREEN}${BOLD}Done!${NC}"
[[ -n "$ZIM_PATH" ]] && echo " Drop .zim files into: $ZIM_PATH"
[[ -n "$MODEL_PATH" ]] && echo " Ollama models stored in: $MODEL_PATH"
echo ""
echo " Undo: cp $COMPOSE.bak $COMPOSE"