mirror of
https://github.com/OpenRouterTeam/spawn.git
synced 2026-04-28 03:49:31 +00:00
When multiple clouds run in parallel, they generate the same app name
(e.g. e2e-claude-TIMESTAMP) and write to the same temp files
(.exit/.stdout/.stderr), causing data corruption.
- Include ACTIVE_CLOUD in make_app_name: e2e-gcp-claude-TIMESTAMP
- Use ${app_name} instead of ${agent} for provision temp files
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: L <6723574+louisgv@users.noreply.github.com>
190 lines
5.1 KiB
Bash
190 lines
5.1 KiB
Bash
#!/bin/bash
|
|
# e2e/lib/common.sh — Constants, logging, env validation for multi-cloud E2E
|
|
set -eo pipefail
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Constants
|
|
# ---------------------------------------------------------------------------
|
|
ALL_AGENTS="claude openclaw zeroclaw codex opencode kilocode"
|
|
PROVISION_TIMEOUT="${PROVISION_TIMEOUT:-480}"
|
|
INSTALL_WAIT="${INSTALL_WAIT:-120}"
|
|
INPUT_TEST_TIMEOUT="${INPUT_TEST_TIMEOUT:-120}"
|
|
|
|
# Active cloud (set by load_cloud_driver)
|
|
ACTIVE_CLOUD=""
|
|
|
|
# Cloud log prefix for multi-cloud parallel output
|
|
CLOUD_LOG_PREFIX=""
|
|
|
|
# Colors
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
CYAN='\033[0;36m'
|
|
BOLD='\033[1m'
|
|
NC='\033[0m'
|
|
|
|
# Tracked instances for cleanup on exit
|
|
_TRACKED_APPS=""
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Logging (with optional cloud prefix for parallel output)
|
|
# ---------------------------------------------------------------------------
|
|
log_header() {
|
|
printf "\n${BOLD}${BLUE}%s=== %s ===${NC}\n" "${CLOUD_LOG_PREFIX}" "$1"
|
|
}
|
|
|
|
log_step() {
|
|
printf "${CYAN}%s -> %s${NC}\n" "${CLOUD_LOG_PREFIX}" "$1"
|
|
}
|
|
|
|
log_ok() {
|
|
printf "${GREEN}%s [PASS] %s${NC}\n" "${CLOUD_LOG_PREFIX}" "$1"
|
|
}
|
|
|
|
log_err() {
|
|
printf "${RED}%s [FAIL] %s${NC}\n" "${CLOUD_LOG_PREFIX}" "$1"
|
|
}
|
|
|
|
log_warn() {
|
|
printf "${YELLOW}%s [WARN] %s${NC}\n" "${CLOUD_LOG_PREFIX}" "$1"
|
|
}
|
|
|
|
log_info() {
|
|
printf "${BLUE}%s [INFO] %s${NC}\n" "${CLOUD_LOG_PREFIX}" "$1"
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# load_cloud_driver CLOUD
|
|
#
|
|
# Sources the cloud-specific driver and creates generic wrappers.
|
|
# ---------------------------------------------------------------------------
|
|
load_cloud_driver() {
|
|
local cloud="$1"
|
|
ACTIVE_CLOUD="${cloud}"
|
|
|
|
# Resolve driver file (relative to this script's location)
|
|
local driver_dir
|
|
driver_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/clouds"
|
|
local driver_file="${driver_dir}/${cloud}.sh"
|
|
|
|
if [ ! -f "${driver_file}" ]; then
|
|
log_err "Cloud driver not found: ${driver_file}"
|
|
return 1
|
|
fi
|
|
|
|
source "${driver_file}"
|
|
|
|
# Create generic wrappers that delegate to cloud-specific functions
|
|
eval "cloud_validate_env() { _${cloud}_validate_env \"\$@\"; }"
|
|
eval "cloud_headless_env() { _${cloud}_headless_env \"\$@\"; }"
|
|
eval "cloud_provision_verify() { _${cloud}_provision_verify \"\$@\"; }"
|
|
eval "cloud_exec() { _${cloud}_exec \"\$@\"; }"
|
|
eval "cloud_exec_long() { _${cloud}_exec_long \"\$@\"; }"
|
|
eval "cloud_teardown() { _${cloud}_teardown \"\$@\"; }"
|
|
eval "cloud_cleanup_stale() { _${cloud}_cleanup_stale \"\$@\"; }"
|
|
|
|
# Optional: per-cloud parallelism cap (returns max agents to run concurrently)
|
|
if type "_${cloud}_max_parallel" >/dev/null 2>&1; then
|
|
eval "cloud_max_parallel() { _${cloud}_max_parallel \"\$@\"; }"
|
|
else
|
|
# Default: no cap (return a large number)
|
|
eval "cloud_max_parallel() { printf '99'; }"
|
|
fi
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# require_common_env
|
|
#
|
|
# Validates tools and env vars common to ALL clouds (bun, jq, OPENROUTER_API_KEY).
|
|
# Cloud-specific validation is handled by cloud_validate_env().
|
|
# ---------------------------------------------------------------------------
|
|
require_common_env() {
|
|
local missing=0
|
|
|
|
if ! command -v jq >/dev/null 2>&1; then
|
|
log_err "jq not found. Install via: brew install jq / apt install jq"
|
|
missing=1
|
|
fi
|
|
|
|
if ! command -v bun >/dev/null 2>&1; then
|
|
log_err "bun not found. Install from https://bun.sh"
|
|
missing=1
|
|
fi
|
|
|
|
if [ -z "${OPENROUTER_API_KEY:-}" ]; then
|
|
log_err "OPENROUTER_API_KEY is not set"
|
|
missing=1
|
|
fi
|
|
|
|
if [ "${missing}" -eq 1 ]; then
|
|
return 1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# require_env
|
|
#
|
|
# Validates common env + active cloud-specific env.
|
|
# ---------------------------------------------------------------------------
|
|
require_env() {
|
|
if ! require_common_env; then
|
|
return 1
|
|
fi
|
|
|
|
if ! cloud_validate_env; then
|
|
return 1
|
|
fi
|
|
|
|
log_ok "Environment validated"
|
|
return 0
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Helpers
|
|
# ---------------------------------------------------------------------------
|
|
make_app_name() {
|
|
local agent="$1"
|
|
local ts
|
|
ts=$(date +%s)
|
|
# Include ACTIVE_CLOUD to avoid name collisions in multi-cloud parallel runs
|
|
if [ -n "${ACTIVE_CLOUD:-}" ]; then
|
|
printf "e2e-%s-%s-%s" "${ACTIVE_CLOUD}" "${agent}" "${ts}"
|
|
else
|
|
printf "e2e-%s-%s" "${agent}" "${ts}"
|
|
fi
|
|
}
|
|
|
|
format_duration() {
|
|
local seconds="$1"
|
|
local mins=$((seconds / 60))
|
|
local secs=$((seconds % 60))
|
|
printf "%dm %ds" "${mins}" "${secs}"
|
|
}
|
|
|
|
track_app() {
|
|
local app_name="$1"
|
|
if [ -z "${_TRACKED_APPS}" ]; then
|
|
_TRACKED_APPS="${app_name}"
|
|
else
|
|
_TRACKED_APPS="${_TRACKED_APPS} ${app_name}"
|
|
fi
|
|
}
|
|
|
|
untrack_app() {
|
|
local app_name="$1"
|
|
local new_list=""
|
|
for app in ${_TRACKED_APPS}; do
|
|
if [ "${app}" != "${app_name}" ]; then
|
|
if [ -z "${new_list}" ]; then
|
|
new_list="${app}"
|
|
else
|
|
new_list="${new_list} ${app}"
|
|
fi
|
|
fi
|
|
done
|
|
_TRACKED_APPS="${new_list}"
|
|
}
|