#!/usr/bin/env bash set -euo pipefail # Build featherChat Linux x86_64 bleeding-edge binaries on Hetzner Cloud. # Uses latest Fedora VM + Arch Linux Docker container for the actual build. # # Usage: # ./scripts/build-bleeding.sh --all Create VM + build + download # ./scripts/build-bleeding.sh --ship Build + deploy to all servers + destroy # ./scripts/build-bleeding.sh --prepare Create VM only # ./scripts/build-bleeding.sh --build Build in Arch Docker container # ./scripts/build-bleeding.sh --transfer Download binaries # ./scripts/build-bleeding.sh --destroy Delete VM VM_NAME="fc-bleeding" SSH_KEY_NAME="wz" SSH_KEY_PATH="/Users/manwe/CascadeProjects/wzp" SERVER_TYPE="cx33" IMAGE="fedora-43" REMOTE_USER="root" OUTPUT_DIR="target/linux-x86_64-bleeding" PROJECT_DIR="/Users/manwe/CascadeProjects/featherChat/warzone" SSH_OPTS="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=10" BINS="warzone-server warzone-client" # Production servers (shared with build-linux.sh) PROD_SERVERS=("root@mequ" "root@kh3rad3ree") PROD_SERVICE="warzone-server" PROD_BIN_DIR="/home/warzone" # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- get_vm_ip() { local ip ip=$(hcloud server list -o columns=name,ipv4 -o noheader 2>/dev/null | grep "$VM_NAME" | awk '{print $2}' | tr -d ' ') if [ -z "$ip" ]; then echo "ERROR: No VM '$VM_NAME' found. Run --prepare first." >&2 exit 1 fi echo "$ip" } ssh_cmd() { local ip ip=$(get_vm_ip) ssh $SSH_OPTS -i "$SSH_KEY_PATH" "$REMOTE_USER@$ip" "$@" } # --------------------------------------------------------------------------- # --prepare: Create Fedora VM, install Docker # --------------------------------------------------------------------------- do_prepare() { local existing existing=$(hcloud server list -o columns=name -o noheader 2>/dev/null | grep "$VM_NAME" || true) if [ -n "$existing" ]; then echo "VM already exists: $existing" echo "Reusing it. Uploading fresh source..." do_upload return fi echo "[1/5] Creating Hetzner VM: $VM_NAME ($SERVER_TYPE, $IMAGE)..." hcloud server create \ --name "$VM_NAME" \ --type "$SERVER_TYPE" \ --image "$IMAGE" \ --ssh-key "$SSH_KEY_NAME" \ --location fsn1 \ --quiet local ip ip=$(get_vm_ip) echo " VM: $VM_NAME @ $ip" echo "[2/5] Waiting for SSH..." for i in $(seq 1 30); do if ssh $SSH_OPTS -i "$SSH_KEY_PATH" "$REMOTE_USER@$ip" "echo ok" &>/dev/null; then break fi sleep 2 done echo "[3/5] Installing Docker on Fedora..." ssh_cmd "dnf install -y -q docker > /dev/null 2>&1 && systemctl start docker && systemctl enable docker" 2>/dev/null echo "[4/5] Pulling Arch Linux Docker image..." ssh_cmd "docker pull archlinux:latest" 2>/dev/null echo "[5/5] Uploading source..." do_upload echo "" echo "=== VM Ready ===" echo "IP: $ip" echo "Next: ./scripts/build-bleeding.sh --build" } do_upload() { echo " Creating source tarball..." tar czf /tmp/fc-bleeding-src.tar.gz \ --exclude='target' \ --exclude='.git' \ --exclude='.claude' \ --exclude='warzone-phone' \ --exclude='notes' \ -C "$PROJECT_DIR" . 2>/dev/null local ip ip=$(get_vm_ip) local size size=$(du -h /tmp/fc-bleeding-src.tar.gz | cut -f1) echo " Uploading $size to VM..." scp $SSH_OPTS -i "$SSH_KEY_PATH" /tmp/fc-bleeding-src.tar.gz "$REMOTE_USER@$ip:/root/fc-bleeding-src.tar.gz" 2>/dev/null ssh_cmd "rm -rf /root/featherChat && mkdir -p /root/featherChat && tar xzf /root/fc-bleeding-src.tar.gz -C /root/featherChat" 2>/dev/null rm -f /tmp/fc-bleeding-src.tar.gz echo " Source uploaded." } # --------------------------------------------------------------------------- # --build: Build inside Arch Linux Docker container # --------------------------------------------------------------------------- do_build() { local ip ip=$(get_vm_ip) echo "=== Bleeding Edge Build on $ip ===" echo " Host: Fedora ($IMAGE)" echo " Build: Arch Linux (Docker, latest)" echo "" echo "[1/3] Building in Arch Linux container..." ssh_cmd "docker run --rm -v /root/featherChat:/build -w /build archlinux:latest bash -c ' # Install deps pacman -Sy --noconfirm base-devel pkg-config openssl rustup wasm-pack > /dev/null 2>&1 rustup default stable 2>/dev/null rustup target add wasm32-unknown-unknown 2>/dev/null # Build WASM echo \"[wasm] Building...\" wasm-pack build crates/warzone-wasm --target web --out-dir ../../wasm-pkg 2>&1 | tail -3 # Build release binaries echo \"[cargo] Building release...\" cargo build --release --bin warzone-server --bin warzone-client 2>&1 '" echo "" echo "[2/3] Verifying binaries..." ssh_cmd "ls -lh /root/featherChat/target/release/warzone-server /root/featherChat/target/release/warzone-client" echo "" echo "[3/3] Checking linked libraries..." ssh_cmd "docker run --rm -v /root/featherChat:/build archlinux:latest ldd /build/target/release/warzone-server | head -10" echo "" echo "=== Build Complete ===" echo "Next: ./scripts/build-bleeding.sh --transfer" } # --------------------------------------------------------------------------- # --transfer: Download binaries # --------------------------------------------------------------------------- do_transfer() { local ip ip=$(get_vm_ip) echo "=== Downloading bleeding-edge binaries from $ip ===" mkdir -p "$OUTPUT_DIR" for bin in $BINS; do echo " $bin..." scp $SSH_OPTS -i "$SSH_KEY_PATH" "$REMOTE_USER@$ip:/root/featherChat/target/release/$bin" "$OUTPUT_DIR/$bin" 2>/dev/null done # Copy federation example scp $SSH_OPTS -i "$SSH_KEY_PATH" "$REMOTE_USER@$ip:/root/featherChat/federation.example.json" "$OUTPUT_DIR/" 2>/dev/null || true echo "" echo "=== Transfer Complete ===" ls -lh "$OUTPUT_DIR"/warzone-* echo "" echo "Built with: Arch Linux latest (bleeding edge)" echo "Deploy: scp $OUTPUT_DIR/warzone-server $OUTPUT_DIR/warzone-client user@server:~/warzone/" } # --------------------------------------------------------------------------- # --destroy # --------------------------------------------------------------------------- do_destroy() { local existing existing=$(hcloud server list -o columns=name -o noheader 2>/dev/null | grep "$VM_NAME" || true) if [ -z "$existing" ]; then echo "No VM '$VM_NAME' to destroy." return fi echo "Deleting VM: $VM_NAME" hcloud server delete "$VM_NAME" echo "Done." } # --------------------------------------------------------------------------- # --update-all / --ship # --------------------------------------------------------------------------- do_update() { local host="$1" echo "=== Updating $host (bleeding) ===" ssh "$host" "systemctl stop $PROD_SERVICE 2>/dev/null || true" scp "$OUTPUT_DIR/warzone-server" "$OUTPUT_DIR/warzone-client" "$host:$PROD_BIN_DIR/" ssh "$host" "chmod +x $PROD_BIN_DIR/warzone-server $PROD_BIN_DIR/warzone-client && systemctl start $PROD_SERVICE" sleep 1 local status status=$(ssh "$host" "systemctl is-active $PROD_SERVICE 2>/dev/null" || true) echo " $host: $status" } do_update_all() { for host in "${PROD_SERVERS[@]}"; do do_update "$host" done } do_ship() { echo "========================================" echo " SHIPPING (bleeding edge) to production" echo "========================================" echo "" do_prepare echo "" do_build echo "" do_transfer echo "" do_update_all echo "" do_destroy echo "" echo "========================================" echo " BLEEDING EDGE SHIP COMPLETE" echo "========================================" } # --------------------------------------------------------------------------- # Main # --------------------------------------------------------------------------- case "${1:-}" in --prepare) do_prepare ;; --build) do_build ;; --transfer) do_transfer ;; --destroy) do_destroy ;; --upload) do_upload ;; --all) do_prepare do_build do_transfer echo "VM still running. Destroy: ./scripts/build-bleeding.sh --destroy" ;; --ship) do_ship ;; --update-all) do_update_all ;; *) echo "Usage: $0 " echo "" echo " --all Create Fedora VM + build in Arch Docker + download" echo " --ship Build + deploy to all servers + destroy VM" echo " --prepare Create VM, install Docker, upload source" echo " --build Build in Arch Linux Docker container" echo " --transfer Download binaries to $OUTPUT_DIR" echo " --destroy Delete the VM" echo " --upload Re-upload source to existing VM" echo " --update-all Deploy bleeding binaries to all servers" echo "" echo "Output: $OUTPUT_DIR/warzone-{server,client}" echo "Host VM: Fedora latest | Build: Arch Linux latest (Docker)" exit 1 ;; esac