diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8d51136..b74a003 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -146,6 +146,72 @@ jobs: LISTEN_PORT=17480 SOCKS_PORT=17479 + # Track PIDs for cleanup + CONNECTOR_PID="" + LISTENER_PID="" + TARGET_PID="" + + # --- Cross-platform cleanup helpers --- + + # kill_process: kill a process by PID, with OS-specific fallback + kill_process() { + local pid="$1" + if [ -z "$pid" ]; then return 0; fi + if command -v taskkill &>/dev/null; then + taskkill //PID "$pid" //F 2>/dev/null || true + else + kill "$pid" 2>/dev/null || true + fi + } + + # kill_by_port: kill whatever process is listening on a given port + kill_by_port() { + local port="$1" + if command -v lsof &>/dev/null; then + lsof -tiTCP:"$port" -sTCP:LISTEN 2>/dev/null | xargs kill 2>/dev/null || true + fi + if command -v netstat &>/dev/null; then + # Works on Windows (Git Bash) and Unix; extract PIDs from netstat output + local pids + pids=$(netstat -ano 2>/dev/null | grep ":${port} " | grep "LISTENING" | awk '{print $5}' | sort -u) || true + for p in $pids; do + # Skip empty, "0.0.0.0", or non-numeric PIDs + if [ -n "$p" ] && [ "$p" != "0.0.0.0" ] && [ "$p" -eq "$p" ] 2>/dev/null; then + kill_process "$p" + fi + done + fi + if command -v fuser &>/dev/null; then + fuser -k "$port/tcp" 2>/dev/null || true + fi + } + + # cleanup: called by trap on EXIT (success or failure) + cleanup() { + echo "Running E2E cleanup..." + # Kill tracked PIDs first (reverse order) + kill_process "$CONNECTOR_PID" + kill_process "$LISTENER_PID" + kill_process "$TARGET_PID" + sleep 1 + # Fallback: port-based cleanup for any stragglers + for PORT in $SOCKS_PORT $LISTEN_PORT $TARGET_PORT; do + kill_by_port "$PORT" + done + # Wait for tracked PIDs (best-effort) + wait "$CONNECTOR_PID" 2>/dev/null || true + wait "$LISTENER_PID" 2>/dev/null || true + wait "$TARGET_PID" 2>/dev/null || true + # Remove temp credentials + if [ -n "$CRED_DIR" ] && [ -d "$CRED_DIR" ]; then + rm -rf "$CRED_DIR" + fi + echo "E2E cleanup done." + } + + # Register cleanup trap: runs on EXIT (covers success, failure, SIGINT, SIGTERM) + trap cleanup EXIT + # 1. Generate credentials $RUN generate --out "$CRED_DIR" @@ -187,20 +253,4 @@ jobs: RESULT=$(curl -sf --proxy "socks5h://127.0.0.1:$SOCKS_PORT" "http://127.0.0.1:$TARGET_PORT/" 2>&1 | head -5 || true) echo "E2E response: $RESULT" - # 6. Cleanup: kill in reverse order - # On Windows (Git Bash), kill might need -INT or taskkill fallback - kill "$CONNECTOR_PID" 2>/dev/null || true - kill "$LISTENER_PID" 2>/dev/null || true - kill "$TARGET_PID" 2>/dev/null || true - sleep 1 - # Force kill any remaining processes on the ports - for PORT in $SOCKS_PORT $LISTEN_PORT $TARGET_PORT; do - lsof -tiTCP:$PORT -sTCP:LISTEN 2>/dev/null | xargs kill 2>/dev/null || true - # Windows fallback: use PowerShell to find and kill by port - netstat -ano 2>/dev/null | grep ":$PORT " | grep LISTENING | awk '{print $5}' | xargs -I{} taskkill //PID {} //F 2>/dev/null || true - done - wait "$CONNECTOR_PID" 2>/dev/null || true - wait "$LISTENER_PID" 2>/dev/null || true - wait "$TARGET_PID" 2>/dev/null || true - echo "E2E test passed on ${{ matrix.os }}"