#!/usr/bin/env bash # Create LXC 7805 (sankofa-studio): FusionAI Creator stack (API + worker + services) for Sankofa Studio at https://studio.sankofa.nexus # Usage: ./scripts/deployment/deploy-sankofa-studio-lxc.sh [--dry-run] [--skip-create] # --dry-run Print commands only. # --skip-create Use existing container 7805 (only install Docker / compose / deploy app). # Env: PROXMOX_HOST, NODE, VMID, HOSTNAME, IP_SANKOFA_STUDIO, REPO_URL or REPO_PATH, ENV_FILE. # See: docs/03-deployment/SANKOFA_STUDIO_DEPLOYMENT.md set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROXMOX_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" IP_CONFIG_PATH="${IP_CONFIG_PATH:-}" if [[ -n "$IP_CONFIG_PATH" && -f "$IP_CONFIG_PATH" ]]; then source "$IP_CONFIG_PATH" 2>/dev/null || true elif [[ -f "$PROXMOX_ROOT/config/ip-addresses.conf" ]]; then source "$PROXMOX_ROOT/config/ip-addresses.conf" 2>/dev/null || true elif [[ -f "$SCRIPT_DIR/../../config/ip-addresses.conf" ]]; then source "$SCRIPT_DIR/../../config/ip-addresses.conf" 2>/dev/null || true fi VMID="${VMID:-${SANKOFA_STUDIO_VMID:-7805}}" HOSTNAME="${HOSTNAME:-sankofa-studio}" IP="${IP_SANKOFA_STUDIO:-192.168.11.72}" GATEWAY="${NETWORK_GATEWAY:-192.168.11.1}" NETWORK="${NETWORK:-vmbr0}" STORAGE="${STORAGE:-local-lvm}" TEMPLATE="${TEMPLATE:-local:vztmpl/ubuntu-22.04-standard_22.04-1_amd64.tar.zst}" MEMORY_MB="${MEMORY_MB:-8192}" CORES="${CORES:-4}" DISK_GB="${DISK_GB:-60}" REPO_URL="${REPO_URL:-}" REPO_PATH="${REPO_PATH:-}" ENV_FILE="${ENV_FILE:-}" APP_DIR="${APP_DIR:-/srv/fusionai-creator}" PROXMOX_HOST="${PROXMOX_HOST:-}" NODE="${NODE:-}" SSH_OPTS="-o ConnectTimeout=15 -o StrictHostKeyChecking=accept-new" DRY_RUN=false SKIP_CREATE=false for a in "$@"; do [[ "$a" == "--dry-run" ]] && DRY_RUN=true [[ "$a" == "--skip-create" ]] && SKIP_CREATE=true done run_cmd() { if [[ -n "$PROXMOX_HOST" ]]; then ssh $SSH_OPTS root@"$PROXMOX_HOST" "$@" else bash -c "$*" fi } run_pct() { local node_opt="" [[ -n "$NODE" && -z "$PROXMOX_HOST" ]] && node_opt="--node $NODE" if [[ -n "$PROXMOX_HOST" ]]; then ssh $SSH_OPTS root@"$PROXMOX_HOST" "pct $node_opt $*" else pct $node_opt "$@" fi } pct_exec() { run_pct "exec $VMID -- $*" } echo "=== Sankofa Studio LXC ($VMID) — $HOSTNAME ===" echo "URL: https://studio.sankofa.nexus → http://${IP}:8000" echo "IP: $IP | Memory: ${MEMORY_MB}MB | Cores: $CORES | Disk: ${DISK_GB}G" echo "" # pct runs only on Proxmox hosts; from another machine set PROXMOX_HOST to SSH there if ! $DRY_RUN && [[ -z "${PROXMOX_HOST:-}" ]] && ! command -v pct &>/dev/null; then echo "ERROR: 'pct' not found. This script must run on a Proxmox host or with PROXMOX_HOST set." echo "" echo "From your current machine, run:" echo " PROXMOX_HOST=192.168.11.11 REPO_URL='https://gitea.d-bis.org/d-bis/FusionAI-Creator.git' $0" echo "" echo "Or SSH to the Proxmox host and run the script there (with REPO_URL set)." exit 1 fi if ! $SKIP_CREATE; then if $DRY_RUN; then echo "[DRY-RUN] Would create LXC $VMID with hostname=$HOSTNAME, ip=$IP/24 (Docker + FusionAI Creator)" exit 0 fi if run_pct list 2>/dev/null | grep -q " $VMID "; then echo "Container $VMID already exists. Use --skip-create to only install/deploy app." exit 0 fi echo "Creating CT $VMID ($HOSTNAME)..." node_opt="" [[ -n "$NODE" && -z "$PROXMOX_HOST" ]] && node_opt="--node $NODE" run_cmd "pct create $VMID $TEMPLATE \ --hostname $HOSTNAME \ --memory $MEMORY_MB \ --cores $CORES \ --rootfs $STORAGE:${DISK_GB} \ --net0 name=eth0,bridge=$NETWORK,ip=$IP/24,gw=$GATEWAY \ --nameserver ${DNS_PRIMARY:-1.1.1.1} \ --description 'Sankofa Studio (FusionAI Creator) - studio.sankofa.nexus. See docs/03-deployment/SANKOFA_STUDIO_DEPLOYMENT.md' \ --start 1 \ --onboot 1 \ --unprivileged 0 \ --features nesting=1 \ $node_opt" echo "Waiting for container to boot..." sleep 25 fi if $DRY_RUN; then echo "[DRY-RUN] Would install Docker, clone/copy app, set .env, docker-compose up -d" exit 0 fi echo "Installing Docker and Docker Compose..." pct_exec "bash -c 'export DEBIAN_FRONTEND=noninteractive && apt-get update -qq && apt-get install -y -qq ca-certificates curl gnupg'" # Docker repo: source os-release in same shell so \$ID and \$VERSION_CODENAME are set pct_exec "bash -c 'source /etc/os-release; install -m 0755 -d /etc/apt/keyrings; curl -fsSL \"https://download.docker.com/linux/\$ID/gpg\" | gpg --batch --dearmor -o /etc/apt/keyrings/docker.gpg; chmod a+r /etc/apt/keyrings/docker.gpg'" pct_exec "bash -c 'source /etc/os-release; echo \"deb [arch=\$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/\$ID \$VERSION_CODENAME stable\" | tee /etc/apt/sources.list.d/docker.list > /dev/null'" pct_exec "bash -c 'apt-get update -qq && apt-get install -y -qq docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin git'" pct_exec "systemctl enable docker && systemctl start docker" if [[ -z "$REPO_URL" && -z "$REPO_PATH" ]]; then echo "REPO_URL or REPO_PATH not set. Skipping clone/copy. Create $APP_DIR and add docker-compose + .env manually, then run: docker compose -f $APP_DIR/docker-compose.yml up -d" exit 0 fi pct_exec "mkdir -p $(dirname "$APP_DIR")" if [[ -n "$REPO_PATH" && -d "$REPO_PATH" ]]; then echo "Copying repo from $REPO_PATH into container..." run_pct "push $VMID $REPO_PATH $APP_DIR" elif [[ -n "$REPO_URL" ]]; then echo "Cloning $REPO_URL into container..." pct_exec "bash -c 'git clone --depth 1 \"$REPO_URL\" \"$APP_DIR\"'" fi if [[ -n "$ENV_FILE" && -f "$ENV_FILE" ]]; then echo "Pushing .env from $ENV_FILE..." run_pct "push $VMID $ENV_FILE $APP_DIR/.env" fi echo "Starting FusionAI Creator stack (docker compose up -d)..." pct_exec "bash -c 'cd \"$APP_DIR\" && docker compose up -d'" echo "" echo "Done. Verify: curl -s http://${IP}:8000/health" echo "Studio UI: http://${IP}:8000/studio/" echo "Configure NPMplus: studio.sankofa.nexus -> http://${IP}:8000 (see SANKOFA_STUDIO_DEPLOYMENT.md)"