From a8336945689c5a519278c827b26d40103dc8fbac Mon Sep 17 00:00:00 2001 From: Siavash Sameni Date: Sat, 28 Mar 2026 14:48:51 +0400 Subject: [PATCH] =?UTF-8?q?refactor:=20build-linux.sh=20=E2=80=94=20persis?= =?UTF-8?q?tent=20VM=20with=20--prepare/--build/--transfer=20steps?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces the single-shot ephemeral VM approach: - --prepare: create VM, install deps (Rust, cmake, etc), upload source - --build: build on VM with full output (iterate on errors) - --transfer: download binaries to target/linux-x86_64/ - --destroy: delete VM when done - --upload: re-upload source to existing VM - --all: prepare + build + transfer (VM persists) VM reuse: --prepare detects existing wzp-builder VM and just re-uploads. All steps get VM IP from hcloud server list (last created). Co-Authored-By: Claude Opus 4.6 (1M context) --- scripts/build-linux.sh | 291 ++++++++++++++++++++++++++++++----------- 1 file changed, 212 insertions(+), 79 deletions(-) diff --git a/scripts/build-linux.sh b/scripts/build-linux.sh index 2a93bf1..e873417 100755 --- a/scripts/build-linux.sh +++ b/scripts/build-linux.sh @@ -4,107 +4,240 @@ set -euo pipefail # Build WarzonePhone Linux x86_64 release binaries using a Hetzner Cloud VPS. # Prerequisites: hcloud CLI authenticated, SSH key "wz" registered. # -# Usage: ./scripts/build-linux.sh +# Usage: +# ./scripts/build-linux.sh --prepare Create VM, install deps, upload source +# ./scripts/build-linux.sh --build Build release binaries on the VM +# ./scripts/build-linux.sh --transfer Download binaries from VM to local +# ./scripts/build-linux.sh --destroy Delete the VM +# ./scripts/build-linux.sh --all Run prepare + build + transfer (no destroy) # -# Outputs: target/linux-x86_64/wzp-relay, wzp-client, wzp-bench +# The VM persists between steps so you can iterate on build errors. SSH_KEY_NAME="wz" SSH_KEY_PATH="/Users/manwe/CascadeProjects/wzp" -SERVER_NAME="wzp-builder-$(date +%s)" SERVER_TYPE="cx33" IMAGE="debian-12" REMOTE_USER="root" OUTPUT_DIR="target/linux-x86_64" +PROJECT_DIR="/Users/manwe/CascadeProjects/warzonePhone" -echo "=== WarzonePhone Linux Build ===" +SSH_OPTS="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=10" -# Ensure server gets deleted on any exit (success or failure) -cleanup() { - if [ -n "${SERVER_NAME:-}" ]; then - echo " Cleaning up server $SERVER_NAME..." - hcloud server delete "$SERVER_NAME" 2>/dev/null || true +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + +get_vm_ip() { + local ip + ip=$(hcloud server list -o columns=ipv4 -o noheader 2>/dev/null | tail -1 | tr -d ' ') + if [ -z "$ip" ]; then + echo "ERROR: No Hetzner VM found. Run --prepare first." >&2 + exit 1 fi - rm -f /tmp/wzp-src.tar.gz + echo "$ip" } -trap cleanup EXIT -# 1. Create the build server -echo "[1/7] Creating Hetzner server..." -hcloud server create \ - --name "$SERVER_NAME" \ - --type "$SERVER_TYPE" \ - --image "$IMAGE" \ - --ssh-key "$SSH_KEY_NAME" \ - --location fsn1 \ - --quiet +ssh_cmd() { + local ip + ip=$(get_vm_ip) + ssh $SSH_OPTS -i "$SSH_KEY_PATH" "$REMOTE_USER@$ip" "$@" +} -SERVER_IP=$(hcloud server ip "$SERVER_NAME") -echo " Server: $SERVER_NAME @ $SERVER_IP" +scp_cmd() { + local ip + ip=$(get_vm_ip) + scp $SSH_OPTS -i "$SSH_KEY_PATH" "$@" +} -# SSH options: skip host key check, use our key -SSH="ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=10 -i $SSH_KEY_PATH $REMOTE_USER@$SERVER_IP" -SCP="scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i $SSH_KEY_PATH" +get_vm_name() { + hcloud server list -o columns=name -o noheader 2>/dev/null | tail -1 | tr -d ' ' +} -# 2. Wait for SSH to come up -echo "[2/7] Waiting for SSH..." -for i in $(seq 1 30); do - if $SSH "echo ok" &>/dev/null; then - break +# --------------------------------------------------------------------------- +# --prepare: Create VM, install deps, upload source +# --------------------------------------------------------------------------- + +do_prepare() { + local server_name="wzp-builder" + + # Check if VM already exists + local existing + existing=$(hcloud server list -o columns=name -o noheader 2>/dev/null | grep wzp-builder || true) + if [ -n "$existing" ]; then + echo "VM already exists: $existing" + echo "Reusing it. Uploading fresh source..." + do_upload + return fi - sleep 2 -done -# 3. Install build dependencies -echo "[3/7] Installing build dependencies..." -$SSH "apt-get update -qq && apt-get install -y -qq build-essential cmake pkg-config libasound2-dev curl git > /dev/null 2>&1" + echo "[1/5] Creating Hetzner VM..." + hcloud server create \ + --name "$server_name" \ + --type "$SERVER_TYPE" \ + --image "$IMAGE" \ + --ssh-key "$SSH_KEY_NAME" \ + --location fsn1 \ + --quiet -# 4. Install Rust -echo "[4/7] Installing Rust..." -$SSH "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable > /dev/null 2>&1" + local ip + ip=$(get_vm_ip) + echo " VM: $server_name @ $ip" -# 5. Upload source code -echo "[5/7] Uploading source code..." -# Create a tarball excluding target/ and .git/ -tar czf /tmp/wzp-src.tar.gz \ - --exclude='target' \ - --exclude='.git' \ - --exclude='.claude' \ - -C /Users/manwe/CascadeProjects/warzonePhone . + # Wait for SSH + 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 -$SCP /tmp/wzp-src.tar.gz "$REMOTE_USER@$SERVER_IP:/root/wzp-src.tar.gz" -$SSH "mkdir -p /root/warzonePhone && tar xzf /root/wzp-src.tar.gz -C /root/warzonePhone" + # Install build dependencies + echo "[3/5] Installing build dependencies..." + ssh_cmd "apt-get update -qq && apt-get install -y -qq build-essential cmake pkg-config libasound2-dev curl git libstdc++-12-dev > /dev/null 2>&1" -# 6. Build release binaries (headless + audio variants) -echo "[6/8] Building all binaries..." -$SSH "source ~/.cargo/env && cd /root/warzonePhone && cargo build --release --bin wzp-relay --bin wzp-client --bin wzp-bench --bin wzp-web 2>&1" | tail -3 + # Install Rust + echo "[4/5] Installing Rust..." + ssh_cmd "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable > /dev/null 2>&1" -echo "[7/8] Building audio-enabled client..." -$SSH "source ~/.cargo/env && cd /root/warzonePhone && cargo build --release --bin wzp-client --features audio 2>&1" | tail -3 -$SSH "cp /root/warzonePhone/target/release/wzp-client /root/warzonePhone/target/release/wzp-client-audio" -$SSH "source ~/.cargo/env && cd /root/warzonePhone && cargo build --release --bin wzp-client 2>&1" | tail -1 + # Upload source + echo "[5/5] Uploading source code..." + do_upload -# 8. Download binaries + static files -echo "[8/8] Downloading binaries..." -mkdir -p "$OUTPUT_DIR/static" -$SCP "$REMOTE_USER@$SERVER_IP:/root/warzonePhone/target/release/wzp-relay" "$OUTPUT_DIR/wzp-relay" -$SCP "$REMOTE_USER@$SERVER_IP:/root/warzonePhone/target/release/wzp-client" "$OUTPUT_DIR/wzp-client" -$SCP "$REMOTE_USER@$SERVER_IP:/root/warzonePhone/target/release/wzp-client-audio" "$OUTPUT_DIR/wzp-client-audio" -$SCP "$REMOTE_USER@$SERVER_IP:/root/warzonePhone/target/release/wzp-bench" "$OUTPUT_DIR/wzp-bench" -$SCP "$REMOTE_USER@$SERVER_IP:/root/warzonePhone/target/release/wzp-web" "$OUTPUT_DIR/wzp-web" -$SCP "$REMOTE_USER@$SERVER_IP:/root/warzonePhone/crates/wzp-web/static/index.html" "$OUTPUT_DIR/static/index.html" + echo "" + echo "=== VM Ready ===" + echo "IP: $ip" + echo "SSH: ssh -i $SSH_KEY_PATH root@$ip" + echo "" + echo "Next: ./scripts/build-linux.sh --build" +} -# Show results (server is deleted by EXIT trap) -echo "" -echo "=== Build Complete ===" -ls -lh "$OUTPUT_DIR"/wzp-* -echo "" -echo "Binaries:" -echo " wzp-relay — relay daemon" -echo " wzp-client — headless client (--send-tone, --record)" -echo " wzp-client-audio — client with mic/speakers (needs libasound2)" -echo " wzp-web — web bridge (serve with static/ folder)" -echo " wzp-bench — benchmarks" -echo " static/ — web UI files" -echo "" -echo "Deploy with:" -echo " scp $OUTPUT_DIR/wzp-* user@server:~/wzp/" +do_upload() { + echo " Creating source tarball..." + tar czf /tmp/wzp-src.tar.gz \ + --exclude='target' \ + --exclude='.git' \ + --exclude='.claude' \ + --exclude='notes' \ + -C "$PROJECT_DIR" . 2>/dev/null + + local ip + ip=$(get_vm_ip) + echo " Uploading to VM..." + scp $SSH_OPTS -i "$SSH_KEY_PATH" /tmp/wzp-src.tar.gz "$REMOTE_USER@$ip:/root/wzp-src.tar.gz" 2>/dev/null + ssh_cmd "rm -rf /root/warzonePhone && mkdir -p /root/warzonePhone && tar xzf /root/wzp-src.tar.gz -C /root/warzonePhone" 2>/dev/null + rm -f /tmp/wzp-src.tar.gz + echo " Source uploaded." +} + +# --------------------------------------------------------------------------- +# --build: Build release binaries on the VM +# --------------------------------------------------------------------------- + +do_build() { + local ip + ip=$(get_vm_ip) + echo "=== Building on $ip ===" + + echo "[1/3] Building relay + client + web..." + ssh_cmd "source ~/.cargo/env && cd /root/warzonePhone && cargo build --release --bin wzp-relay --bin wzp-client --bin wzp-bench --bin wzp-web 2>&1" + + echo "" + echo "[2/3] Building audio-enabled client..." + ssh_cmd "source ~/.cargo/env && cd /root/warzonePhone && cargo build --release --bin wzp-client --features audio 2>&1" | tail -5 + ssh_cmd "cp /root/warzonePhone/target/release/wzp-client /root/warzonePhone/target/release/wzp-client-audio" + ssh_cmd "source ~/.cargo/env && cd /root/warzonePhone && cargo build --release --bin wzp-client 2>&1" | tail -3 + + echo "" + echo "[3/3] Verifying binaries..." + ssh_cmd "ls -lh /root/warzonePhone/target/release/wzp-relay /root/warzonePhone/target/release/wzp-client /root/warzonePhone/target/release/wzp-web /root/warzonePhone/target/release/wzp-bench /root/warzonePhone/target/release/wzp-client-audio" + + echo "" + echo "=== Build Complete ===" + echo "Next: ./scripts/build-linux.sh --transfer" +} + +# --------------------------------------------------------------------------- +# --transfer: Download binaries from VM to local +# --------------------------------------------------------------------------- + +do_transfer() { + local ip + ip=$(get_vm_ip) + echo "=== Downloading binaries from $ip ===" + + mkdir -p "$OUTPUT_DIR/static" + + for bin in wzp-relay wzp-client wzp-client-audio wzp-bench wzp-web; do + echo " $bin..." + scp $SSH_OPTS -i "$SSH_KEY_PATH" "$REMOTE_USER@$ip:/root/warzonePhone/target/release/$bin" "$OUTPUT_DIR/$bin" 2>/dev/null + done + + # Static files for web bridge + scp $SSH_OPTS -i "$SSH_KEY_PATH" "$REMOTE_USER@$ip:/root/warzonePhone/crates/wzp-web/static/index.html" "$OUTPUT_DIR/static/index.html" 2>/dev/null + scp $SSH_OPTS -i "$SSH_KEY_PATH" "$REMOTE_USER@$ip:/root/warzonePhone/crates/wzp-web/static/audio-processor.js" "$OUTPUT_DIR/static/audio-processor.js" 2>/dev/null + + echo "" + echo "=== Transfer Complete ===" + ls -lh "$OUTPUT_DIR"/wzp-* + echo "" + echo "Deploy with:" + echo " scp $OUTPUT_DIR/wzp-relay $OUTPUT_DIR/wzp-client user@server:~/wzp/" +} + +# --------------------------------------------------------------------------- +# --destroy: Delete the VM +# --------------------------------------------------------------------------- + +do_destroy() { + local name + name=$(get_vm_name) + if [ -z "$name" ]; then + echo "No VM to destroy." + return + fi + echo "Deleting VM: $name" + hcloud server delete "$name" + echo "Done." +} + +# --------------------------------------------------------------------------- +# Main +# --------------------------------------------------------------------------- + +case "${1:-}" in + --prepare) + do_prepare + ;; + --build) + do_build + ;; + --transfer) + do_transfer + ;; + --destroy) + do_destroy + ;; + --all) + do_prepare + do_build + do_transfer + echo "" + echo "VM is still running. Destroy with: ./scripts/build-linux.sh --destroy" + ;; + --upload) + do_upload + ;; + *) + echo "Usage: $0 {--prepare|--build|--transfer|--destroy|--all|--upload}" + echo "" + echo "Steps:" + echo " --prepare Create VM, install deps, upload source" + echo " --build Build release binaries (shows full output)" + echo " --transfer Download binaries to target/linux-x86_64/" + echo " --destroy Delete the VM" + echo " --all prepare + build + transfer (VM persists)" + echo " --upload Re-upload source to existing VM" + exit 1 + ;; +esac