Skip to content

Commit 6647983

Browse files
owineclaude
andcommitted
fix: use __EMPTY__ placeholder for empty arguments through eval
Empty string arguments are lost when passed through eval in ssh_retry. Use __EMPTY__ placeholder for empty COMPOSE_ARGS and CRITICAL_SERVICES to ensure they survive the eval chain and are properly converted back to empty strings on the remote side. This fixes the bug where COMPOSE_ARGS="" was being lost, causing the remote script to incorrectly parse arguments: - Expected: $1=stack1, $2=stack2, $3=TARGET_REF, $4="" - Actually got: $1=stack1, $2=stack2, $3=TARGET_REF (missing $4!) - Result: COMPOSE_ARGS got TARGET_REF value, causing "no such service" error Also removed debug output from deploy-stacks.sh. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent 7e54a2d commit 6647983

File tree

2 files changed

+30
-11
lines changed

2 files changed

+30
-11
lines changed

scripts/deployment/deploy-stacks.sh

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,15 @@ log_info "Target ref: $TARGET_REF"
9797
# Execute deployment via SSH with retry
9898
# Use 'env' on remote side to set environment variables for the remote bash session
9999
# Use printf %q to properly escape arguments for eval in ssh_retry
100+
# Empty COMPOSE_ARGS uses placeholder to survive eval
100101
STACKS_ESCAPED=$(printf '%q ' $STACKS)
101102
TARGET_REF_ESCAPED=$(printf '%q' "$TARGET_REF")
102-
COMPOSE_ARGS_ESCAPED=$(printf '%q' "$COMPOSE_ARGS")
103+
104+
if [ -z "$COMPOSE_ARGS" ]; then
105+
COMPOSE_ARGS_ESCAPED="__EMPTY__"
106+
else
107+
COMPOSE_ARGS_ESCAPED=$(printf '%q' "$COMPOSE_ARGS")
108+
fi
103109

104110
ssh_retry 3 10 "ssh -o \"StrictHostKeyChecking no\" $SSH_USER@$SSH_HOST env OP_SERVICE_ACCOUNT_TOKEN=\"$OP_TOKEN\" GIT_FETCH_TIMEOUT=\"$GIT_FETCH_TIMEOUT\" GIT_CHECKOUT_TIMEOUT=\"$GIT_CHECKOUT_TIMEOUT\" IMAGE_PULL_TIMEOUT=\"$IMAGE_PULL_TIMEOUT\" SERVICE_STARTUP_TIMEOUT=\"$SERVICE_STARTUP_TIMEOUT\" VALIDATION_ENV_TIMEOUT=\"$VALIDATION_ENV_TIMEOUT\" VALIDATION_SYNTAX_TIMEOUT=\"$VALIDATION_SYNTAX_TIMEOUT\" /bin/bash -s $STACKS_ESCAPED $TARGET_REF_ESCAPED $COMPOSE_ARGS_ESCAPED" << 'EOF'
105111
set -e
@@ -117,22 +123,20 @@ ssh_retry 3 10 "ssh -o \"StrictHostKeyChecking no\" $SSH_USER@$SSH_HOST env OP_S
117123
118124
TOTAL_ARGS=$#
119125
120-
# Debug: Show what arguments were received
121-
echo "DEBUG: Received $TOTAL_ARGS arguments:"
122-
for i in $(seq 1 $TOTAL_ARGS); do
123-
eval "arg=\${$i}"
124-
echo " \$$i = [$arg]"
125-
done
126-
127126
# Validate minimum arguments (at least 1 stack + TARGET_REF + COMPOSE_ARGS)
128127
if [ $TOTAL_ARGS -lt 3 ]; then
129128
echo "❌ Insufficient arguments: expected at least 3 (stacks, target-ref, compose-args), got $TOTAL_ARGS"
130129
exit 1
131130
fi
132131
133-
# Last argument is always COMPOSE_ARGS (could be empty)
132+
# Last argument is always COMPOSE_ARGS (could be empty placeholder)
134133
COMPOSE_ARGS="${!TOTAL_ARGS}"
135134
135+
# Convert __EMPTY__ placeholder back to empty string
136+
if [ "$COMPOSE_ARGS" = "__EMPTY__" ]; then
137+
COMPOSE_ARGS=""
138+
fi
139+
136140
# Second-to-last argument is always TARGET_REF
137141
TARGET_REF="${@:$((TOTAL_ARGS-1)):1}"
138142

scripts/deployment/rollback-stacks.sh

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,20 @@ log_info "Initiating rollback to $PREVIOUS_SHA"
104104

105105
# Execute rollback via SSH with retry
106106
# Use printf %q to properly escape arguments for eval in ssh_retry
107+
# Empty strings use placeholder to survive eval
107108
PREVIOUS_SHA_ESCAPED=$(printf '%q' "$PREVIOUS_SHA")
108-
COMPOSE_ARGS_ESCAPED=$(printf '%q' "$COMPOSE_ARGS")
109-
CRITICAL_SERVICES_ESCAPED=$(printf '%q' "$CRITICAL_SERVICES")
109+
110+
if [ -z "$COMPOSE_ARGS" ]; then
111+
COMPOSE_ARGS_ESCAPED="__EMPTY__"
112+
else
113+
COMPOSE_ARGS_ESCAPED=$(printf '%q' "$COMPOSE_ARGS")
114+
fi
115+
116+
if [ -z "$CRITICAL_SERVICES" ]; then
117+
CRITICAL_SERVICES_ESCAPED="__EMPTY__"
118+
else
119+
CRITICAL_SERVICES_ESCAPED=$(printf '%q' "$CRITICAL_SERVICES")
120+
fi
110121

111122
ROLLBACK_RESULT=$(ssh_retry 3 10 "ssh -o \"StrictHostKeyChecking no\" $SSH_USER@$SSH_HOST env OP_SERVICE_ACCOUNT_TOKEN=\"$OP_TOKEN\" GIT_FETCH_TIMEOUT=\"$GIT_FETCH_TIMEOUT\" GIT_CHECKOUT_TIMEOUT=\"$GIT_CHECKOUT_TIMEOUT\" IMAGE_PULL_TIMEOUT=\"$IMAGE_PULL_TIMEOUT\" SERVICE_STARTUP_TIMEOUT=\"$SERVICE_STARTUP_TIMEOUT\" VALIDATION_ENV_TIMEOUT=\"$VALIDATION_ENV_TIMEOUT\" VALIDATION_SYNTAX_TIMEOUT=\"$VALIDATION_SYNTAX_TIMEOUT\" /bin/bash -s $PREVIOUS_SHA_ESCAPED $COMPOSE_ARGS_ESCAPED $CRITICAL_SERVICES_ESCAPED" << 'EOF'
112123
set -e
@@ -116,6 +127,10 @@ ROLLBACK_RESULT=$(ssh_retry 3 10 "ssh -o \"StrictHostKeyChecking no\" $SSH_USER@
116127
COMPOSE_ARGS="$2"
117128
CRITICAL_SERVICES="$3"
118129
130+
# Convert __EMPTY__ placeholders back to empty strings
131+
[ "$COMPOSE_ARGS" = "__EMPTY__" ] && COMPOSE_ARGS=""
132+
[ "$CRITICAL_SERVICES" = "__EMPTY__" ] && CRITICAL_SERVICES=""
133+
119134
# OP_SERVICE_ACCOUNT_TOKEN and timeouts are passed via 'env' command on remote side
120135
# They are already in the environment, no need to export again
121136

0 commit comments

Comments
 (0)