diff --git a/start-automaker.sh b/start-automaker.sh index ecb499b9..5d9a30a4 100755 --- a/start-automaker.sh +++ b/start-automaker.sh @@ -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/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