diff --git a/warzone/scripts/build-bleeding.sh b/warzone/scripts/build-bleeding.sh new file mode 100755 index 0000000..44ca262 --- /dev/null +++ b/warzone/scripts/build-bleeding.sh @@ -0,0 +1,283 @@ +#!/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-41" +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