refactor: build-linux.sh — persistent VM with --prepare/--build/--transfer steps
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) <noreply@anthropic.com>
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user