mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-01-30 06:12:03 +00:00
feat: auto-discover available ports when defaults are in use (#614)
* feat: auto-discover available ports when defaults are in use Instead of prompting the user to kill processes or manually enter alternative ports, the launcher now automatically finds the next available ports when the defaults (3007/3008) are already in use. This enables running the built Electron app alongside web development mode without conflicts - web dev will automatically use the next available ports (e.g., 3009/3010) when Electron is running. Changes: - Add find_next_available_port() function that searches up to 100 ports - Update resolve_port_conflicts() to auto-select ports without prompts - Update check_ports() for consistency (currently unused but kept) - Add safety check to ensure web and server ports don't conflict * fix: sanitize PIDs to single line for centered display * feat: add user choice for port conflicts with auto-select as default When ports are in use, users can now choose: - [Enter] Auto-select available ports (default, recommended) - [K] Kill processes and use default ports - [C] Choose custom ports manually - [X] Cancel Pressing Enter without typing anything will auto-select the next available ports, making it easy to quickly continue when running alongside an existing Electron instance. * fix: improve port discovery error handling and code quality Address PR review feedback: - Extract magic number 100 to PORT_SEARCH_MAX_ATTEMPTS constant - Fix find_next_available_port to return nothing on failure instead of the busy port, preventing misleading "auto-selected" messages - Update all callers to handle port discovery failure with clear error messages showing the searched range - Simplify PID formatting using xargs instead of tr|sed|sed pipeline
This commit is contained in:
committed by
GitHub
parent
43481c2bab
commit
02a7a54736
@@ -34,6 +34,7 @@ fi
|
||||
# Port configuration
|
||||
DEFAULT_WEB_PORT=3007
|
||||
DEFAULT_SERVER_PORT=3008
|
||||
PORT_SEARCH_MAX_ATTEMPTS=100
|
||||
WEB_PORT=$DEFAULT_WEB_PORT
|
||||
SERVER_PORT=$DEFAULT_SERVER_PORT
|
||||
|
||||
@@ -453,6 +454,25 @@ is_port_in_use() {
|
||||
[ -n "$pids" ] && [ "$pids" != " " ]
|
||||
}
|
||||
|
||||
# Find the next available port starting from a given port
|
||||
# Returns the port on stdout if found, nothing if all ports in range are busy
|
||||
# Exit code: 0 if found, 1 if no available port in range
|
||||
find_next_available_port() {
|
||||
local start_port=$1
|
||||
local port=$start_port
|
||||
|
||||
for ((i=0; i<PORT_SEARCH_MAX_ATTEMPTS; i++)); do
|
||||
if ! is_port_in_use "$port"; then
|
||||
echo "$port"
|
||||
return 0
|
||||
fi
|
||||
port=$((port + 1))
|
||||
done
|
||||
|
||||
# No free port found in the scan range
|
||||
return 1
|
||||
}
|
||||
|
||||
kill_port() {
|
||||
local port=$1
|
||||
local pids
|
||||
@@ -491,9 +511,7 @@ kill_port() {
|
||||
}
|
||||
|
||||
check_ports() {
|
||||
show_cursor
|
||||
stty echo icanon 2>/dev/null || true
|
||||
|
||||
# Auto-discover available ports (no user interaction required)
|
||||
local web_in_use=false
|
||||
local server_in_use=false
|
||||
|
||||
@@ -506,72 +524,46 @@ check_ports() {
|
||||
|
||||
if [ "$web_in_use" = true ] || [ "$server_in_use" = true ]; then
|
||||
echo ""
|
||||
local max_port
|
||||
if [ "$web_in_use" = true ]; then
|
||||
local pids
|
||||
pids=$(get_pids_on_port "$DEFAULT_WEB_PORT")
|
||||
echo "${C_YELLOW}⚠${RESET} Port $DEFAULT_WEB_PORT is in use by process(es): $pids"
|
||||
# Get PIDs and convert newlines to spaces for display
|
||||
pids=$(get_pids_on_port "$DEFAULT_WEB_PORT" | xargs)
|
||||
echo "${C_YELLOW}Port $DEFAULT_WEB_PORT in use (PID: $pids), finding alternative...${RESET}"
|
||||
max_port=$((DEFAULT_WEB_PORT + PORT_SEARCH_MAX_ATTEMPTS - 1))
|
||||
if ! WEB_PORT=$(find_next_available_port "$DEFAULT_WEB_PORT"); then
|
||||
echo "${C_RED}Error: No free web port in range ${DEFAULT_WEB_PORT}-${max_port}${RESET}"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
if [ "$server_in_use" = true ]; then
|
||||
local pids
|
||||
pids=$(get_pids_on_port "$DEFAULT_SERVER_PORT")
|
||||
echo "${C_YELLOW}⚠${RESET} Port $DEFAULT_SERVER_PORT is in use by process(es): $pids"
|
||||
# Get PIDs and convert newlines to spaces for display
|
||||
pids=$(get_pids_on_port "$DEFAULT_SERVER_PORT" | xargs)
|
||||
echo "${C_YELLOW}Port $DEFAULT_SERVER_PORT in use (PID: $pids), finding alternative...${RESET}"
|
||||
max_port=$((DEFAULT_SERVER_PORT + PORT_SEARCH_MAX_ATTEMPTS - 1))
|
||||
if ! SERVER_PORT=$(find_next_available_port "$DEFAULT_SERVER_PORT"); then
|
||||
echo "${C_RED}Error: No free server port in range ${DEFAULT_SERVER_PORT}-${max_port}${RESET}"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Ensure web and server ports don't conflict with each other
|
||||
if [ "$WEB_PORT" -eq "$SERVER_PORT" ]; then
|
||||
local conflict_start=$((SERVER_PORT + 1))
|
||||
max_port=$((conflict_start + PORT_SEARCH_MAX_ATTEMPTS - 1))
|
||||
if ! SERVER_PORT=$(find_next_available_port "$conflict_start"); then
|
||||
echo "${C_RED}Error: No free server port in range ${conflict_start}-${max_port}${RESET}"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
while true; do
|
||||
read -r -p "What would you like to do? (k)ill processes, (u)se different ports, or (c)ancel: " choice
|
||||
case "$choice" in
|
||||
[kK]|[kK][iI][lL][lL])
|
||||
if [ "$web_in_use" = true ]; then
|
||||
kill_port "$DEFAULT_WEB_PORT"
|
||||
else
|
||||
echo "${C_GREEN}✓${RESET} Port $DEFAULT_WEB_PORT is available"
|
||||
fi
|
||||
if [ "$server_in_use" = true ]; then
|
||||
kill_port "$DEFAULT_SERVER_PORT"
|
||||
else
|
||||
echo "${C_GREEN}✓${RESET} Port $DEFAULT_SERVER_PORT is available"
|
||||
fi
|
||||
break
|
||||
;;
|
||||
[uU]|[uU][sS][eE])
|
||||
# Collect both ports first
|
||||
read -r -p "Enter web port (default $DEFAULT_WEB_PORT): " input_web
|
||||
input_web=${input_web:-$DEFAULT_WEB_PORT}
|
||||
read -r -p "Enter server port (default $DEFAULT_SERVER_PORT): " input_server
|
||||
input_server=${input_server:-$DEFAULT_SERVER_PORT}
|
||||
|
||||
# Validate both before assigning either
|
||||
if ! validate_port "$input_web" "Web port"; then
|
||||
continue
|
||||
fi
|
||||
if ! validate_port "$input_server" "Server port"; then
|
||||
continue
|
||||
fi
|
||||
|
||||
# Assign atomically after both validated
|
||||
WEB_PORT=$input_web
|
||||
SERVER_PORT=$input_server
|
||||
echo "${C_GREEN}Using ports: Web=$WEB_PORT, Server=$SERVER_PORT${RESET}"
|
||||
break
|
||||
;;
|
||||
[cC]|[cC][aA][nN][cC][eE][lL])
|
||||
echo "${C_MUTE}Cancelled.${RESET}"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "${C_RED}Invalid choice. Please enter k, u, or c.${RESET}"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
echo ""
|
||||
echo "${C_GREEN}✓ Auto-selected available ports: Web=$WEB_PORT, Server=$SERVER_PORT${RESET}"
|
||||
else
|
||||
echo "${C_GREEN}✓${RESET} Port $DEFAULT_WEB_PORT is available"
|
||||
echo "${C_GREEN}✓${RESET} Port $DEFAULT_SERVER_PORT is available"
|
||||
fi
|
||||
|
||||
hide_cursor
|
||||
stty -echo -icanon 2>/dev/null || true
|
||||
}
|
||||
|
||||
validate_terminal_size() {
|
||||
@@ -791,37 +783,70 @@ resolve_port_conflicts() {
|
||||
|
||||
if is_port_in_use "$DEFAULT_WEB_PORT"; then
|
||||
web_in_use=true
|
||||
web_pids=$(get_pids_on_port "$DEFAULT_WEB_PORT")
|
||||
# Get PIDs and convert newlines to spaces for display
|
||||
web_pids=$(get_pids_on_port "$DEFAULT_WEB_PORT" | xargs)
|
||||
fi
|
||||
if is_port_in_use "$DEFAULT_SERVER_PORT"; then
|
||||
server_in_use=true
|
||||
server_pids=$(get_pids_on_port "$DEFAULT_SERVER_PORT")
|
||||
# Get PIDs and convert newlines to spaces for display
|
||||
server_pids=$(get_pids_on_port "$DEFAULT_SERVER_PORT" | xargs)
|
||||
fi
|
||||
|
||||
if [ "$web_in_use" = true ] || [ "$server_in_use" = true ]; then
|
||||
echo ""
|
||||
if [ "$web_in_use" = true ]; then
|
||||
center_print "⚠ Port $DEFAULT_WEB_PORT is in use by process(es): $web_pids" "$C_YELLOW"
|
||||
center_print "Port $DEFAULT_WEB_PORT in use (PID: $web_pids)" "$C_YELLOW"
|
||||
fi
|
||||
if [ "$server_in_use" = true ]; then
|
||||
center_print "⚠ Port $DEFAULT_SERVER_PORT is in use by process(es): $server_pids" "$C_YELLOW"
|
||||
center_print "Port $DEFAULT_SERVER_PORT in use (PID: $server_pids)" "$C_YELLOW"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Show options
|
||||
center_print "What would you like to do?" "$C_WHITE"
|
||||
echo ""
|
||||
center_print "[K] Kill processes and continue" "$C_GREEN"
|
||||
center_print "[U] Use different ports" "$C_MUTE"
|
||||
center_print "[C] Cancel" "$C_RED"
|
||||
center_print "[Enter] Auto-select available ports (Recommended)" "$C_GREEN"
|
||||
center_print "[K] Kill processes and use default ports" "$C_MUTE"
|
||||
center_print "[C] Choose custom ports" "$C_MUTE"
|
||||
center_print "[X] Cancel" "$C_RED"
|
||||
echo ""
|
||||
|
||||
while true; do
|
||||
local choice_pad=$(( (TERM_COLS - 20) / 2 ))
|
||||
printf "%${choice_pad}s" ""
|
||||
read -r -p "Choice: " choice
|
||||
read -r -p "Choice [Enter]: " choice
|
||||
|
||||
case "$choice" in
|
||||
""|[aA]|[aA][uU][tT][oO])
|
||||
# Auto-select: find next available ports
|
||||
echo ""
|
||||
local max_port=$((DEFAULT_WEB_PORT + PORT_SEARCH_MAX_ATTEMPTS - 1))
|
||||
if [ "$web_in_use" = true ]; then
|
||||
if ! WEB_PORT=$(find_next_available_port "$DEFAULT_WEB_PORT"); then
|
||||
center_print "No free web port in range ${DEFAULT_WEB_PORT}-${max_port}" "$C_RED"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
max_port=$((DEFAULT_SERVER_PORT + PORT_SEARCH_MAX_ATTEMPTS - 1))
|
||||
if [ "$server_in_use" = true ]; then
|
||||
if ! SERVER_PORT=$(find_next_available_port "$DEFAULT_SERVER_PORT"); then
|
||||
center_print "No free server port in range ${DEFAULT_SERVER_PORT}-${max_port}" "$C_RED"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
# Ensure web and server ports don't conflict with each other
|
||||
if [ "$WEB_PORT" -eq "$SERVER_PORT" ]; then
|
||||
local conflict_start=$((SERVER_PORT + 1))
|
||||
max_port=$((conflict_start + PORT_SEARCH_MAX_ATTEMPTS - 1))
|
||||
if ! SERVER_PORT=$(find_next_available_port "$conflict_start"); then
|
||||
center_print "No free server port in range ${conflict_start}-${max_port}" "$C_RED"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
center_print "✓ Auto-selected available ports:" "$C_GREEN"
|
||||
center_print " Web: $WEB_PORT | Server: $SERVER_PORT" "$C_PRI"
|
||||
break
|
||||
;;
|
||||
[kK]|[kK][iI][lL][lL])
|
||||
echo ""
|
||||
if [ "$web_in_use" = true ]; then
|
||||
@@ -836,7 +861,7 @@ resolve_port_conflicts() {
|
||||
fi
|
||||
break
|
||||
;;
|
||||
[uU]|[uU][sS][eE])
|
||||
[cC]|[cC][hH][oO][oO][sS][eE])
|
||||
echo ""
|
||||
local input_pad=$(( (TERM_COLS - 40) / 2 ))
|
||||
# Collect both ports first
|
||||
@@ -861,14 +886,14 @@ resolve_port_conflicts() {
|
||||
center_print "Using ports: Web=$WEB_PORT, Server=$SERVER_PORT" "$C_GREEN"
|
||||
break
|
||||
;;
|
||||
[cC]|[cC][aA][nN][cC][eE][lL])
|
||||
[xX]|[xX][cC][aA][nN][cC][eE][lL])
|
||||
echo ""
|
||||
center_print "Cancelled." "$C_MUTE"
|
||||
echo ""
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
center_print "Invalid choice. Please enter K, U, or C." "$C_RED"
|
||||
center_print "Invalid choice. Press Enter for auto-select, or K/C/X." "$C_RED"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
Reference in New Issue
Block a user