#!/bin/bash # # Build Archipelago Auto-Installer ISO from LIVE SERVER STATE # # This captures the CURRENT STATE of the development server and packages # it into an auto-installer ISO - exactly like Start9/Umbrel. # # Usage: ./build-from-live-server.sh [dev-server-ip] # set -e DEV_SERVER="${1:-archipelago@192.168.1.228}" SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" WORK_DIR="$SCRIPT_DIR/build/live-snapshot" OUTPUT_DIR="$SCRIPT_DIR/results" echo "╔════════════════════════════════════════════════════════════════╗" echo "║ Building Archipelago ISO from LIVE SERVER ║" echo "╚════════════════════════════════════════════════════════════════╝" echo "" echo "📡 Development Server: $DEV_SERVER" echo "" # Check for required tools CONTAINER_CMD="" if command -v docker >/dev/null 2>&1; then CONTAINER_CMD="docker" elif command -v podman >/dev/null 2>&1; then CONTAINER_CMD="podman" else echo "❌ Missing docker or podman" exit 1 fi if ! command -v xorriso >/dev/null 2>&1; then echo "❌ Missing xorriso" exit 1 fi if ! command -v ssh >/dev/null 2>&1; then echo "❌ Missing ssh" exit 1 fi echo "Using container runtime: $CONTAINER_CMD" echo "" mkdir -p "$WORK_DIR" mkdir -p "$OUTPUT_DIR" # ============================================================================= # STEP 1: Capture LIVE SERVER state # ============================================================================= echo "📦 Step 1: Capturing live server state..." echo "" SNAPSHOT_DIR="$WORK_DIR/live-server-snapshot" rm -rf "$SNAPSHOT_DIR" mkdir -p "$SNAPSHOT_DIR" # Create directory structure mkdir -p "$SNAPSHOT_DIR/bin" mkdir -p "$SNAPSHOT_DIR/web-ui" mkdir -p "$SNAPSHOT_DIR/configs" mkdir -p "$SNAPSHOT_DIR/apps" mkdir -p "$SNAPSHOT_DIR/scripts" # Capture backend binary echo " Capturing backend binary..." scp "$DEV_SERVER:/usr/local/bin/archipelago" "$SNAPSHOT_DIR/bin/" 2>/dev/null || { echo " ⚠️ Backend binary not found on server" echo " Using local build if available..." if [ -f "$SCRIPT_DIR/../core/target/release/archipelago" ]; then # Build on Linux if we're on macOS if [[ "$OSTYPE" == "darwin"* ]]; then echo " Building backend for Linux..." cd "$SCRIPT_DIR/../core" $CONTAINER_CMD build --platform linux/amd64 -t archipelago-backend -f - . <<'DOCKERFILE' FROM rust:1.93-bookworm as builder WORKDIR /build COPY . . RUN cargo build --release --bin archipelago DOCKERFILE BACKEND_CONTAINER=$($CONTAINER_CMD create --platform linux/amd64 archipelago-backend) $CONTAINER_CMD cp "$BACKEND_CONTAINER:/build/target/release/archipelago" "$SNAPSHOT_DIR/bin/" $CONTAINER_CMD rm "$BACKEND_CONTAINER" cd "$SCRIPT_DIR" else cp "$SCRIPT_DIR/../core/target/release/archipelago" "$SNAPSHOT_DIR/bin/" fi fi } if [ -f "$SNAPSHOT_DIR/bin/archipelago" ]; then chmod +x "$SNAPSHOT_DIR/bin/archipelago" echo " ✅ Backend: $(du -h "$SNAPSHOT_DIR/bin/archipelago" | cut -f1)" else echo " ❌ No backend binary available" exit 1 fi # Capture frontend (Web UI) echo " Capturing frontend (Web UI)..." rsync -az --delete "$DEV_SERVER:/opt/archipelago/web-ui/" "$SNAPSHOT_DIR/web-ui/" 2>/dev/null || { echo " ⚠️ Web UI not found on server" echo " Using local build..." if [ -d "$SCRIPT_DIR/../web/dist/neode-ui" ]; then cp -r "$SCRIPT_DIR/../web/dist/neode-ui/"* "$SNAPSHOT_DIR/web-ui/" elif [ -d "$SCRIPT_DIR/../neode-ui/dist" ]; then cp -r "$SCRIPT_DIR/../neode-ui/dist/"* "$SNAPSHOT_DIR/web-ui/" else echo " Building frontend..." cd "$SCRIPT_DIR/../neode-ui" npm run build cp -r "$SCRIPT_DIR/../web/dist/neode-ui/"* "$SNAPSHOT_DIR/web-ui/" cd "$SCRIPT_DIR" fi } if [ -d "$SNAPSHOT_DIR/web-ui" ] && [ "$(ls -A "$SNAPSHOT_DIR/web-ui")" ]; then echo " ✅ Web UI: $(du -sh "$SNAPSHOT_DIR/web-ui" | cut -f1)" else echo " ❌ No web UI available" exit 1 fi # Capture Nginx config echo " Capturing Nginx config..." scp "$DEV_SERVER:/etc/nginx/sites-available/default" "$SNAPSHOT_DIR/configs/nginx-default.conf" 2>/dev/null || \ echo " ⚠️ Using default Nginx config" # Capture systemd service echo " Capturing systemd service..." scp "$DEV_SERVER:/etc/systemd/system/archipelago.service" "$SNAPSHOT_DIR/configs/archipelago.service" 2>/dev/null || { echo " Creating default service..." cat > "$SNAPSHOT_DIR/configs/archipelago.service" <<'SERVICE' [Unit] Description=Archipelago Backend After=network-online.target Wants=network-online.target [Service] Type=simple User=archipelago Environment="ARCHIPELAGO_BIND=0.0.0.0:5678" Environment="ARCHIPELAGO_DEV_MODE=true" ExecStart=/usr/local/bin/archipelago Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target SERVICE } # Capture app manifests echo " Capturing app manifests..." if [ -d "$SCRIPT_DIR/../apps" ]; then cp -r "$SCRIPT_DIR/../apps/"* "$SNAPSHOT_DIR/apps/" 2>/dev/null || true fi echo " ✅ Live server state captured" echo "" # ============================================================================= # STEP 2: Create base rootfs with captured state # ============================================================================= echo "📦 Step 2: Creating base system with live server state..." echo "" ROOTFS_TAR="$WORK_DIR/archipelago-rootfs-live.tar" # Build rootfs with Docker/Podman cat > "$WORK_DIR/Dockerfile.rootfs" <<'DOCKERFILE' FROM debian:bookworm ENV DEBIAN_FRONTEND=noninteractive # Install all required packages RUN apt-get update && apt-get install -y \ linux-image-amd64 \ grub-efi-amd64 \ grub-efi-amd64-signed \ shim-signed \ systemd \ systemd-sysv \ dbus \ sudo \ network-manager \ openssh-server \ nginx \ podman \ curl \ wget \ htop \ vim-tiny \ ca-certificates \ locales \ console-setup \ keyboard-configuration \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* # Configure locale RUN echo "en_US.UTF-8 UTF-8" > /etc/locale.gen && locale-gen # Create archipelago user RUN useradd -m -s /bin/bash -G sudo archipelago && \ echo "archipelago:archipelago" | chpasswd && \ echo "archipelago ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/archipelago # Set hostname RUN echo "archipelago" > /etc/hostname # Configure SSH RUN mkdir -p /etc/ssh && \ sed -i 's/#PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config || true && \ sed -i 's/#PasswordAuthentication.*/PasswordAuthentication yes/' /etc/ssh/sshd_config || true # Create directories RUN mkdir -p /var/lib/archipelago/{data,config,containers} && \ mkdir -p /etc/archipelago && \ mkdir -p /opt/archipelago/{bin,scripts,web-ui} && \ chown -R archipelago:archipelago /var/lib/archipelago /opt/archipelago # Clean up RUN apt-get clean && \ rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* DOCKERFILE echo " Building base rootfs..." $CONTAINER_CMD build --platform linux/amd64 -t archipelago-rootfs-live -f "$WORK_DIR/Dockerfile.rootfs" "$WORK_DIR" echo " Exporting filesystem..." $CONTAINER_CMD create --platform linux/amd64 --name archipelago-rootfs-live-tmp archipelago-rootfs-live $CONTAINER_CMD export archipelago-rootfs-live-tmp > "$ROOTFS_TAR" $CONTAINER_CMD rm archipelago-rootfs-live-tmp echo " ✅ Base rootfs created: $(du -h "$ROOTFS_TAR" | cut -f1)" echo "" # ============================================================================= # STEP 3: Inject live server files into rootfs # ============================================================================= echo "📦 Step 3: Injecting live server files..." echo "" ROOTFS_DIR="$WORK_DIR/rootfs-extract" rm -rf "$ROOTFS_DIR" mkdir -p "$ROOTFS_DIR" echo " Extracting rootfs..." tar -xf "$ROOTFS_TAR" -C "$ROOTFS_DIR" echo " Copying backend binary..." cp "$SNAPSHOT_DIR/bin/archipelago" "$ROOTFS_DIR/usr/local/bin/" chmod +x "$ROOTFS_DIR/usr/local/bin/archipelago" echo " Copying web UI..." rm -rf "$ROOTFS_DIR/opt/archipelago/web-ui" mkdir -p "$ROOTFS_DIR/opt/archipelago/web-ui" cp -r "$SNAPSHOT_DIR/web-ui/"* "$ROOTFS_DIR/opt/archipelago/web-ui/" echo " Configuring Nginx..." if [ -f "$SNAPSHOT_DIR/configs/nginx-default.conf" ]; then cp "$SNAPSHOT_DIR/configs/nginx-default.conf" "$ROOTFS_DIR/etc/nginx/sites-available/archipelago" else cat > "$ROOTFS_DIR/etc/nginx/sites-available/archipelago" <<'NGINXCONF' server { listen 80 default_server; listen [::]:80 default_server; root /opt/archipelago/web-ui; index index.html; server_name _; # Proxy API requests to backend location /rpc/ { proxy_pass http://localhost:5678/rpc/; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } location /ws/ { proxy_pass http://localhost:5678/ws/; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; } location /health { proxy_pass http://localhost:5678/health; } # Serve static files location / { try_files $uri $uri/ /index.html; } } NGINXCONF fi rm -f "$ROOTFS_DIR/etc/nginx/sites-enabled/default" ln -sf /etc/nginx/sites-available/archipelago "$ROOTFS_DIR/etc/nginx/sites-enabled/archipelago" echo " Configuring systemd service..." cp "$SNAPSHOT_DIR/configs/archipelago.service" "$ROOTFS_DIR/etc/systemd/system/archipelago.service" # Enable services (create symlinks) mkdir -p "$ROOTFS_DIR/etc/systemd/system/multi-user.target.wants" ln -sf /etc/systemd/system/archipelago.service "$ROOTFS_DIR/etc/systemd/system/multi-user.target.wants/archipelago.service" ln -sf /lib/systemd/system/NetworkManager.service "$ROOTFS_DIR/etc/systemd/system/multi-user.target.wants/NetworkManager.service" ln -sf /lib/systemd/system/ssh.service "$ROOTFS_DIR/etc/systemd/system/multi-user.target.wants/ssh.service" ln -sf /lib/systemd/system/nginx.service "$ROOTFS_DIR/etc/systemd/system/multi-user.target.wants/nginx.service" echo " Copying app manifests..." if [ -d "$SNAPSHOT_DIR/apps" ]; then mkdir -p "$ROOTFS_DIR/etc/archipelago/apps" cp -r "$SNAPSHOT_DIR/apps/"* "$ROOTFS_DIR/etc/archipelago/apps/" 2>/dev/null || true fi echo " Repacking rootfs with live server state..." cd "$ROOTFS_DIR" tar -cf "$ROOTFS_TAR" . cd "$SCRIPT_DIR" echo " ✅ Live server files injected" echo "" # ============================================================================= # STEP 4: Create installer ISO (reuse existing auto-installer logic) # ============================================================================= echo "📦 Step 4: Creating auto-installer ISO..." echo "" # Now call the existing auto-installer script but skip the rootfs build # since we already have it with live server state export ROOTFS_TAR="$ROOTFS_TAR" export SKIP_ROOTFS_BUILD="true" # Run the rest of build-auto-installer-iso.sh logic here... # (Download Debian Live base, create auto-install.sh, package ISO) echo " Downloading Debian Live base..." BASE_ISO="$WORK_DIR/debian-live-installer.iso" if [ ! -f "$BASE_ISO" ] || [ $(stat -f%z "$BASE_ISO" 2>/dev/null || stat -c%s "$BASE_ISO" 2>/dev/null || echo 0) -lt 100000000 ]; then curl -L -# -o "$BASE_ISO" \ "https://cdimage.debian.org/debian-cd/current-live/amd64/iso-hybrid/debian-live-12.9.0-amd64-standard.iso" fi echo " Extracting installer base..." INSTALLER_ISO="$WORK_DIR/installer-iso" rm -rf "$INSTALLER_ISO" mkdir -p "$INSTALLER_ISO" cd "$INSTALLER_ISO" 7z x -y "$BASE_ISO" >/dev/null 2>&1 cd "$SCRIPT_DIR" # Copy archipelago files ARCH_DIR="$INSTALLER_ISO/archipelago" mkdir -p "$ARCH_DIR" cp "$ROOTFS_TAR" "$ARCH_DIR/rootfs.tar" echo " ✅ Archipelago components added (rootfs: $(du -h "$ARCH_DIR/rootfs.tar" | cut -f1))" echo "" # Continue with ISO creation... echo "📦 Step 5: Creating final bootable ISO..." # (Rest of xorriso logic from build-auto-installer-iso.sh) OUTPUT_ISO="$OUTPUT_DIR/archipelago-live-$(date +%Y%m%d-%H%M%S).iso" # Get MBR ISOHDPFX="$WORK_DIR/isohdpfx.bin" dd if="$INSTALLER_ISO/isolinux/isolinux.bin" of="$ISOHDPFX" bs=432 count=1 2>/dev/null xorriso -as mkisofs -o "$OUTPUT_ISO" \ -volid "ARCHIPELAGO" \ -J -R \ -isohybrid-mbr "$ISOHDPFX" \ -c isolinux/boot.cat \ -b isolinux/isolinux.bin \ -no-emul-boot -boot-load-size 4 -boot-info-table \ -eltorito-alt-boot \ -e boot/grub/efi.img \ -no-emul-boot \ -isohybrid-gpt-basdat \ "$INSTALLER_ISO" echo "" echo "╔════════════════════════════════════════════════════════════════╗" echo "║ ✅ LIVE SERVER ISO CREATED! ║" echo "╚════════════════════════════════════════════════════════════════╝" echo "" echo "📀 Output: $OUTPUT_ISO" echo " Size: $(du -h "$OUTPUT_ISO" | cut -f1)" echo " MD5: $(md5sum "$OUTPUT_ISO" 2>/dev/null || md5 "$OUTPUT_ISO" | awk '{print $NF}')" echo "" echo "🎉 This ISO contains the EXACT state of your dev server!" echo "" echo "To flash:" echo " cd image-recipe && ./write-usb-dd.sh /dev/diskX" echo ""