Skip to content

Commit 43dc48b

Browse files
committed
Smart port conflict detection - allow redeployments
Check if port is used by same app or different app: - Add get_remote_port_process() to identify what owns the port - Add check_port_owner() to determine if deployment should proceed - Same PM2 app on same port → allow (redeployment) - Different app on same port → block with clear error - Shows current owner vs your app in error message - Provides commands to check or stop conflicting app Fixes issue where updating existing app was incorrectly blocked
1 parent f3adfe9 commit 43dc48b

2 files changed

Lines changed: 94 additions & 4 deletions

File tree

lib/commands/deploy.sh

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,23 @@ deploy_backend() {
3232
# Verify package manager is installed on remote server
3333
verify_remote_pkg_manager "$PKG_MANAGER"
3434

35-
# Check if port is available on the server
36-
if ! check_remote_port_available "$BACKEND_PORT"; then
35+
# Check if port is available or already used by this app
36+
if ! check_port_owner "$BACKEND_PORT" "$PM2_APP_NAME"; then
3737
local suggested_port
3838
suggested_port=$(suggest_available_port "$BACKEND_PORT")
39+
local current_owner
40+
current_owner=$(get_remote_port_process "$BACKEND_PORT")
3941
error "Port $BACKEND_PORT is already in use on $SSH_HOST
4042
43+
Current owner: $current_owner
44+
Your app: $PM2_APP_NAME
45+
4146
Suggested fix:
4247
1. Update shipnode.conf: BACKEND_PORT=$suggested_port
43-
2. Check what's using the port:
44-
ssh $SSH_USER@$SSH_HOST -p $SSH_PORT 'ss -tln | grep :$BACKEND_PORT'
48+
2. Check running apps:
49+
ssh $SSH_USER@$SSH_HOST -p $SSH_PORT 'pm2 list'
50+
3. Or stop the conflicting app:
51+
ssh $SSH_USER@$SSH_HOST -p $SSH_PORT 'pm2 stop $current_owner'
4552
4653
Deployment blocked to prevent port conflict."
4754
fi

lib/validation.sh

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,89 @@ check_remote_port_available() {
160160
return 0 # Port is available
161161
}
162162

163+
# Check which process is using a port on the remote server
164+
# Returns: process name or empty string
165+
# Args:
166+
# $1: Port number to check
167+
# $2: SSH user (optional, uses SSH_USER if not provided)
168+
# $3: SSH host (optional, uses SSH_HOST if not provided)
169+
# $4: SSH port (optional, uses SSH_PORT if not provided)
170+
get_remote_port_process() {
171+
local port=$1
172+
local user=${2:-$SSH_USER}
173+
local host=${3:-$SSH_HOST}
174+
local ssh_port=${4:-$SSH_PORT}
175+
176+
if [ -z "$port" ] || [ -z "$user" ] || [ -z "$host" ]; then
177+
echo ""
178+
return 1
179+
fi
180+
181+
# Get the process using the port
182+
local process
183+
process=$(ssh -o ConnectTimeout=5 -p "$ssh_port" "$user@$host" "
184+
# Try to find PID using the port, then get process name
185+
if command -v ss >/dev/null 2>&1; then
186+
pid=\$(ss -tlnp | grep \":$port \" | head -1 | grep -oP 'pid=\K[0-9]+')
187+
elif command -v netstat >/dev/null 2>&1; then
188+
pid=\$(netstat -tlnp 2>/dev/null | grep \":$port \" | head -1 | awk '{print \$7}' | cut -d'/' -f1)
189+
fi
190+
191+
if [ -n \"\$pid\" ]; then
192+
# Check if it's a PM2 process
193+
if pm2 describe \$pid >/dev/null 2>&1 || pm2 list | grep -q \"\$pid\"; then
194+
# Get PM2 app name
195+
pm2 list | grep \"\$pid\" | awk '{print \$4}' | head -1
196+
else
197+
# Get process name from PID
198+
ps -p \$pid -o comm= 2>/dev/null || echo \"unknown\"
199+
fi
200+
fi
201+
" 2>/dev/null)
202+
203+
echo "$process"
204+
}
205+
206+
# Check if port is used by our app (allowing redeployment)
207+
# Returns: 0 if same app or available, 1 if different app
208+
# Args:
209+
# $1: Port number to check
210+
# $2: PM2 app name to compare
211+
# $3: SSH user (optional, uses SSH_USER if not provided)
212+
# $4: SSH host (optional, uses SSH_HOST if not provided)
213+
# $5: SSH port (optional, uses SSH_PORT if not provided)
214+
check_port_owner() {
215+
local port=$1
216+
local expected_app=$2
217+
local user=${3:-$SSH_USER}
218+
local host=${4:-$SSH_HOST}
219+
local ssh_port=${5:-$SSH_PORT}
220+
221+
if [ -z "$port" ]; then
222+
return 0
223+
fi
224+
225+
# Check if port is available
226+
if check_remote_port_available "$port" "$user" "$host" "$ssh_port"; then
227+
return 0 # Port is free, allow deployment
228+
fi
229+
230+
# Port is in use, check who owns it
231+
local owner
232+
owner=$(get_remote_port_process "$port" "$user" "$host" "$ssh_port")
233+
234+
if [ -z "$owner" ]; then
235+
return 1 # Unknown owner, block to be safe
236+
fi
237+
238+
# Check if owner matches our expected app
239+
if [ "$owner" = "$expected_app" ]; then
240+
return 0 # Same app, allow redeployment
241+
fi
242+
243+
return 1 # Different app using the port
244+
}
245+
163246
# Suggest an available port on the remote server
164247
# Tries ports in range 3000-3010, then falls back to random high port
165248
# Args:

0 commit comments

Comments
 (0)