Files
galaxy/script/deploy_remote_server_to_test_vm

207 lines
8.0 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# Builds the Integration-channel Oz CLI for Linux x86_64 (musl) and uploads it
# to the SSH integration testing VM so that remote-server integration tests run
# against the binary built from the current branch.
#
# This script is intended to be run once by CI before the integration test
# suite, not by individual test functions. The binary is placed at the
# versioned Integration channel path on the remote host, which is where the
# Integration channel's `check_binary` looks.
#
# Prerequisites (same as script/deploy_remote_server):
# On Linux, install curl, sha256sum, tar, and sshpass.
# rustup target add x86_64-unknown-linux-musl
# gcloud CLI authenticated for the warp-ssh-integration-testing project
#
# Usage:
# script/deploy_remote_server_to_test_vm
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
WORKSPACE_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
TARGET="x86_64-unknown-linux-musl"
CARGO_PROFILE="dev-remote"
FEATURES="crash_reporting,standalone,agent_mode_debug,remote_codebase_indexing"
WARP_BIN="integration"
# The integration test runner uses Channel::Integration, which maps to
# cli_command_name() = "oz-integration", remote_server_dir() = "~/.warp-dev/remote-server",
# and remote_server_binary() = "oz-integration-${pinned_version}".
# See crates/warp_core/src/channel/mod.rs, crates/remote_server/src/setup.rs,
# and crates/integration/src/bin/integration.rs.
#
# We intentionally build app/src/bin/integration.rs instead of the normal
# Local-channel app binary. The Local-channel release bundle requires
# warp-channel-config to generate local_config.json at compile time, but this CI
# path should not depend on private channel config. The Integration-channel
# binary embeds a static non-secret config instead.
REMOTE_DIR=".warp-dev/remote-server"
# Dedicated remote-server test VM, configured similarly to the SSH integration
# test VM but isolated from the general SSH integration suite.
REMOTE_USERS=(bash zsh)
REMOTE_HOST="ssh-remote-server-testing"
REMOTE_PORT="22"
PROXY_COMMAND="gcloud compute start-iap-tunnel ssh-remote-server-testing ${REMOTE_PORT} --listen-on-stdin --project=warp-ssh-integration-testing --zone=us-east4-b"
app_version() {
if [[ -n "${GIT_RELEASE_TAG:-}" ]]; then
echo "$GIT_RELEASE_TAG"
return
fi
cargo metadata --no-deps --format-version 1 \
| python3 -c 'import json, sys; print(next(p["version"] for p in json.load(sys.stdin)["packages"] if p["name"] == "remote_server" and p["manifest_path"].endswith("/crates/remote_server/Cargo.toml")))'
}
# ── Preflight checks ──────────────────────────────────────────────
source "$SCRIPT_DIR/linux/configure_musl_toolchain" "$TARGET"
if ! rustup target list --installed 2>/dev/null | grep -q "$TARGET"; then
echo "Error: $TARGET target not installed." >&2
echo "Install it with: rustup target add $TARGET" >&2
exit 1
fi
BUNDLE_VERSION="$(app_version)"
BUNDLE_BINARY_NAME="oz-integration"
BINARY_NAME="$BUNDLE_BINARY_NAME-$BUNDLE_VERSION"
# ── Build ─────────────────────────────────────────────────────────
CARGO_TARGET_DIR="${CARGO_TARGET_DIR:-$WORKSPACE_ROOT/target}"
OUTPUT_DIR="$CARGO_TARGET_DIR/$TARGET/$CARGO_PROFILE"
BUILT_BINARY="$OUTPUT_DIR/$WARP_BIN"
LOCAL_BUNDLE_DIR="$OUTPUT_DIR/remote-server-integration-bundle"
LOCAL_BUNDLE_TARBALL="$OUTPUT_DIR/remote-server-integration-bundle.tar.gz"
echo "==> Building Integration-channel Oz CLI for $TARGET (profile=$CARGO_PROFILE)"
cargo build \
-p warp \
--bin "$WARP_BIN" \
--target "$TARGET" \
--profile "$CARGO_PROFILE" \
--features "$FEATURES"
if [[ ! -f "$BUILT_BINARY" ]]; then
echo "Error: Expected binary not found at $BUILT_BINARY" >&2
exit 1
fi
BINARY_SIZE=$(du -h "$BUILT_BINARY" | cut -f1)
echo "==> Build complete ($BINARY_SIZE)"
rm -rf "$LOCAL_BUNDLE_DIR"
mkdir -p "$LOCAL_BUNDLE_DIR"
cp "$BUILT_BINARY" "$LOCAL_BUNDLE_DIR/$BUNDLE_BINARY_NAME"
"$WORKSPACE_ROOT/script/prepare_bundled_resources" \
"$LOCAL_BUNDLE_DIR/resources" \
integration \
"$CARGO_PROFILE"
tar -czf "$LOCAL_BUNDLE_TARBALL" -C "$LOCAL_BUNDLE_DIR" .
# ── Upload via SCP through GCP IAP tunnel ─────────────────────────
# The test VM uses password auth (same as the SSH integration tests).
# sshpass provides the password non-interactively for SSH/SCP.
SSH_PASSWORD="password"
SSH_OPTS=(-p "$REMOTE_PORT" -o "ProxyCommand=$PROXY_COMMAND" -o PreferredAuthentications=password -o PubkeyAuthentication=no -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null)
if command -v sshpass &>/dev/null; then
export SSHPASS="$SSH_PASSWORD"
SSH_CMD=(sshpass -e ssh)
SCP_CMD=(sshpass -e scp)
else
echo "Warning: sshpass not found, SSH/SCP will prompt for password interactively." >&2
if [[ "$(uname -s)" == "Darwin" ]]; then
echo "Install it with: brew install hudochenkov/sshpass/sshpass" >&2
elif [[ "$(uname -s)" == "Linux" ]]; then
echo "Install it with: sudo apt-get install sshpass" >&2
fi
SSH_CMD=(ssh)
SCP_CMD=(scp)
fi
for REMOTE_USER in "${REMOTE_USERS[@]}"; do
echo "==> Ensuring remote directory exists for ${REMOTE_USER}"
"${SSH_CMD[@]}" "${SSH_OPTS[@]}" "${REMOTE_USER}@${REMOTE_HOST}" "mkdir -p ~/$REMOTE_DIR"
echo "==> Stopping stale remote server daemons for ${REMOTE_USER}"
"${SSH_CMD[@]}" "${SSH_OPTS[@]}" "${REMOTE_USER}@${REMOTE_HOST}" "bash -s -- '$REMOTE_DIR'" <<'REMOTE_SCRIPT'
set -euo pipefail
REMOTE_ROOT="$HOME/$1"
mkdir -p "$REMOTE_ROOT"
shopt -s nullglob
for PID_FILE in "$REMOTE_ROOT"/*/server.pid; do
DAEMON_DIR="$(dirname "$PID_FILE")"
PID="$(cat "$PID_FILE" 2>/dev/null || true)"
if [[ "$PID" =~ ^[0-9]+$ ]] && [[ -d "/proc/$PID" ]]; then
CMDLINE="$(tr '\0' ' ' < "/proc/$PID/cmdline" 2>/dev/null || true)"
if [[ "$CMDLINE" == *remote-server-daemon* ]]; then
echo "Stopping stale remote-server daemon pid $PID"
kill "$PID" 2>/dev/null || true
for _ in {1..50}; do
if ! kill -0 "$PID" 2>/dev/null; then
break
fi
sleep 0.1
done
if kill -0 "$PID" 2>/dev/null; then
echo "Force-stopping stale remote-server daemon pid $PID"
kill -9 "$PID" 2>/dev/null || true
fi
fi
fi
rm -f "$DAEMON_DIR/server.sock" "$DAEMON_DIR/server.pid"
done
rm -f "$REMOTE_ROOT"/remote-server-integration-bundle-*.tar.gz
REMOTE_SCRIPT
TMP_BUNDLE_NAME="remote-server-integration-bundle-${GITHUB_RUN_ID:-local}-$$-${REMOTE_USER}.tar.gz"
echo "==> Uploading bundle to ${REMOTE_USER}@${REMOTE_HOST}:~/$REMOTE_DIR/$TMP_BUNDLE_NAME"
"${SCP_CMD[@]}" -P "$REMOTE_PORT" \
-o "ProxyCommand=$PROXY_COMMAND" \
-o PreferredAuthentications=password \
-o PubkeyAuthentication=no \
-o StrictHostKeyChecking=no \
-o UserKnownHostsFile=/dev/null \
"$LOCAL_BUNDLE_TARBALL" \
"${REMOTE_USER}@${REMOTE_HOST}:~/$REMOTE_DIR/$TMP_BUNDLE_NAME"
echo "==> Installing binary and resources for ${REMOTE_USER}"
# Args: <remote dir> <tarball name> <binary name in tarball> <installed name>.
# The tarball ships the unversioned binary beside its resources/ tree; the
# installed binary carries the version suffix the Integration channel
# launches. The resources tree lands at `bundled_resources`, the global
# version-independent location the daemon reads (must match
# BUNDLED_RESOURCES_DIR_NAME in crates/remote_server/src/setup.rs).
"${SSH_CMD[@]}" "${SSH_OPTS[@]}" "${REMOTE_USER}@${REMOTE_HOST}" \
"bash -s -- '$REMOTE_DIR' '$TMP_BUNDLE_NAME' '$BUNDLE_BINARY_NAME' '$BINARY_NAME'" <<'REMOTE_SCRIPT'
set -euo pipefail
cd "$HOME/$1"
tar -xzf "$2"
rm -f "$2"
rm -rf bundled_resources
mv resources bundled_resources
chmod 755 "$3"
mv -f "$3" "$4"
"./$4" --version
REMOTE_SCRIPT
done
echo ""
echo "==> Done! Binary deployed to ${REMOTE_HOST}:~/$REMOTE_DIR/$BINARY_NAME for users: ${REMOTE_USERS[*]}"
echo " Bundled resources: ~/$REMOTE_DIR/bundled_resources"