Make ports 80 and 443 compatible with UFW

This commit is contained in:
2026-03-31 17:07:38 +03:00
Unverified
parent 3caadde6ff
commit 2a5a539126
4 changed files with 588 additions and 130 deletions
+15 -8
View File
@@ -1,3 +1,18 @@
{
servers {
listener_wrappers {
proxy_protocol
tls
}
# Only trust PROXY protocol from local forwarder.
trusted_proxies static 127.0.0.1/32 ::1/128
}
http_port 8080
https_port 8443
}
fromchat.ru { fromchat.ru {
reverse_proxy frontend:8301 { reverse_proxy frontend:8301 {
header_up X-Real-IP {remote_host} header_up X-Real-IP {remote_host}
@@ -103,11 +118,3 @@ git.fromchat.ru {
} }
} }
} }
api.getgadgets.toolbox-io.ru {
reverse_proxy 95.165.0.162:8400
}
getgadgets.toolbox-io.ru {
reverse_proxy 95.165.0.162:8401
}
+18 -2
View File
@@ -167,8 +167,8 @@ services:
- production - production
restart: unless-stopped restart: unless-stopped
ports: ports:
- "80:80" - "127.0.0.1:8080:8080"
- "443:443" - "127.0.0.1:8443:8443"
extra_hosts: extra_hosts:
- "host.docker.internal:host-gateway" - "host.docker.internal:host-gateway"
volumes: volumes:
@@ -179,6 +179,22 @@ services:
networks: networks:
- public - public
haproxy:
image: haproxy:latest
profiles:
- production
restart: unless-stopped
network_mode: host
# Image defaults to USER haproxy (non-root); that user cannot bind 80/443 on host.
# Rootful Docker does not change that — only the container user does.
user: "0:0"
cap_add:
- NET_BIND_SERVICE
depends_on:
- caddy
volumes:
- ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro
postgres: postgres:
build: build:
context: . context: .
+26
View File
@@ -0,0 +1,26 @@
global
maxconn 4096
# Start as root to bind 80/443, then drop privileges.
user haproxy
group haproxy
defaults
no log
mode tcp
timeout connect 5s
timeout client 2m
timeout server 2m
frontend fe_http_80
bind 0.0.0.0:80
default_backend be_caddy_http
backend be_caddy_http
server caddy_http 127.0.0.1:8080 send-proxy-v2
frontend fe_https_443
bind 0.0.0.0:443
default_backend be_caddy_https
backend be_caddy_https
server caddy_https 127.0.0.1:8443 send-proxy-v2
+489 -80
View File
@@ -31,25 +31,19 @@ substep() {
echo -e "${MAGENTA}${BOLD}🚀 Deployment${NC}\n" echo -e "${MAGENTA}${BOLD}🚀 Deployment${NC}\n"
# Read password with asterisks
read_password() { read_password() {
local password="" local password=""
local char local char
local old_stty local old_stty
# Save current terminal settings
old_stty=$(stty -g 2>/dev/null) old_stty=$(stty -g 2>/dev/null)
# Disable echo
stty -echo 2>/dev/null stty -echo 2>/dev/null
# Read characters one by one
while IFS= read -rs -n 1 char; do while IFS= read -rs -n 1 char; do
# Check for Enter key (empty means Enter was pressed)
if [ -z "$char" ]; then if [ -z "$char" ]; then
break break
fi fi
# Check for backspace/delete (ASCII 127)
if [ "$char" = $'\177' ] || [ "$char" = $'\b' ]; then if [ "$char" = $'\177' ] || [ "$char" = $'\b' ]; then
if [ ${#password} -gt 0 ]; then if [ ${#password} -gt 0 ]; then
password="${password%?}" password="${password%?}"
@@ -61,7 +55,6 @@ read_password() {
fi fi
done done
# Restore terminal settings
stty "$old_stty" 2>/dev/null stty "$old_stty" 2>/dev/null
echo "" >&2 echo "" >&2
echo "$password" echo "$password"
@@ -95,6 +88,20 @@ REPO_NAME="FromChat"
DEPLOY_PATH="~/actions-runner/_work/$REPO_NAME/$REPO_NAME" DEPLOY_PATH="~/actions-runner/_work/$REPO_NAME/$REPO_NAME"
PLATFORM="linux/arm64" PLATFORM="linux/arm64"
# Prefer plain `docker build` when target arch matches host arch.
# This avoids Docker Desktop buildx export/load issues and is faster for same-arch builds.
HOST_ARCH_RAW="$(uname -m 2>/dev/null || echo "")"
case "$HOST_ARCH_RAW" in
arm64|aarch64) HOST_ARCH="arm64" ;;
x86_64|amd64) HOST_ARCH="amd64" ;;
*) HOST_ARCH="$HOST_ARCH_RAW" ;;
esac
PLATFORM_ARCH="${PLATFORM##*/}"
USE_DOCKER_BUILD=false
if [ -n "$HOST_ARCH" ] && [ "$HOST_ARCH" = "$PLATFORM_ARCH" ]; then
USE_DOCKER_BUILD=true
fi
# Check if server is provided # Check if server is provided
if [ -z "$SERVER" ]; then if [ -z "$SERVER" ]; then
error "Server not specified. Usage: $0 [user@host] [deployment_path] [platform]" error "Server not specified. Usage: $0 [user@host] [deployment_path] [platform]"
@@ -146,48 +153,15 @@ if [ "$KEY_LOADED" = false ]; then
fi fi
fi fi
# Check if SSH key authentication already works # Require key-based SSH auth; do not attempt to copy keys automatically.
if ssh -o BatchMode=yes -o ConnectTimeout=10 -o StrictHostKeyChecking=no "$SERVER" "echo 'SSH key works'" >/dev/null 2>&1; then if ! ssh -o BatchMode=yes -o ConnectTimeout=10 -o StrictHostKeyChecking=no "$SERVER" "echo 'SSH key works'" >/dev/null 2>&1; then
# SSH key already works, no need to copy error "SSH key authentication failed for $SERVER"
true echo " Copy your public key to the server, then re-run deploy:"
else echo " ssh-copy-id -i \"$SSH_KEY_PUB_FILE\" \"$SERVER\""
# Check if our public key is already on the server echo ""
KEY_CONTENT=$(cat "$SSH_KEY_PUB_FILE") echo " Or manually append this key to ~/.ssh/authorized_keys on the server:"
if ssh -o BatchMode=no -o ConnectTimeout=10 -o StrictHostKeyChecking=no "$SERVER" " echo " $(cat "$SSH_KEY_PUB_FILE")"
grep -q '$KEY_CONTENT' ~/.ssh/authorized_keys 2>/dev/null
" >/dev/null 2>&1; then
# Key exists but authentication failed - might be permissions issue
error "SSH key found on server but authentication failed. Check server SSH configuration."
exit 1 exit 1
else
# Key not on server, need to copy it
substep "SSH password: " -n
SSH_PASSWORD=$(read_password)
if [ -z "$SSH_PASSWORD" ]; then
error "No SSH password provided"
exit 1
fi
substep "Copying SSH key to server..."
if command -v expect >/dev/null 2>&1; then
expect << EOF >/dev/null 2>&1
spawn ssh-copy-id -o ConnectTimeout=10 -o StrictHostKeyChecking=no -i "$SSH_KEY_PUB_FILE" "$SERVER"
expect "password:"
send "$SSH_PASSWORD\r"
expect eof
EOF
if [ $? -eq 0 ]; then
true
else
error "Failed to copy SSH key to server"
exit 1
fi
else
error "expect not available - cannot copy SSH key"
exit 1
fi
fi
fi fi
# ============================================================================ # ============================================================================
@@ -195,15 +169,7 @@ fi
# ============================================================================ # ============================================================================
SUDO_PASSWORD="" SUDO_PASSWORD=""
# If SSH password was provided, try using it for sudo first # Prompt for sudo password (optional; leave blank for passwordless sudo)
if [ -n "$SSH_PASSWORD" ]; then
if echo "$SSH_PASSWORD" | ssh "$SERVER" "sudo -S -v" > /dev/null 2>&1; then
SUDO_PASSWORD="$SSH_PASSWORD"
export SUDO_PASSWORD
fi
fi
# If we don't have a working sudo password yet, prompt for it
if [ -z "$SUDO_PASSWORD" ]; then if [ -z "$SUDO_PASSWORD" ]; then
while true; do while true; do
substep "Sudo password: " -n substep "Sudo password: " -n
@@ -266,11 +232,6 @@ start_docker_desktop() {
return 1 return 1
} }
# Check buildx
if ! docker buildx version > /dev/null 2>&1; then
error "Docker buildx not available. Install Docker Desktop."
fi
# Check Docker daemon # Check Docker daemon
if ! check_docker_daemon; then if ! check_docker_daemon; then
warning "Docker daemon is not running" warning "Docker daemon is not running"
@@ -279,6 +240,12 @@ if ! check_docker_daemon; then
fi fi
fi fi
if [ "$USE_DOCKER_BUILD" = false ]; then
# Check buildx
if ! docker buildx version > /dev/null 2>&1; then
error "Docker buildx not available. Install Docker Desktop."
fi
# Setup buildx builder # Setup buildx builder
step "Setting up buildx builder" step "Setting up buildx builder"
BUILDER_NAME="fromchat-builder" BUILDER_NAME="fromchat-builder"
@@ -308,6 +275,7 @@ if [ "$BUILDER_EXISTS" = false ]; then
fi fi
docker buildx use "$BUILDER_NAME" > /dev/null 2>&1 docker buildx use "$BUILDER_NAME" > /dev/null 2>&1
fi
# Detect services # Detect services
step "Detecting services" step "Detecting services"
@@ -378,7 +346,25 @@ for SERVICE in $SERVICES; do
fi fi
fi fi
BUILDX_ARGS=(buildx build --platform "$PLATFORM" --file "$DOCKERFILE" --tag "$IMAGE_TAG" --load) if [ "$USE_DOCKER_BUILD" = true ]; then
DOCKER_BUILD_ARGS=(build --platform "$PLATFORM" --file "$DOCKERFILE" --tag "$IMAGE_TAG")
if [ -n "$BUILD_TARGET" ]; then
DOCKER_BUILD_ARGS+=(--target "$BUILD_TARGET")
fi
DOCKER_BUILD_ARGS+=("$BUILD_CONTEXT")
if docker "${DOCKER_BUILD_ARGS[@]}"; then
echo -e " ${GREEN}${NC} Built ${CYAN}$SERVICE${NC}"
BUILT_IMAGES+=("$IMAGE_TAG")
echo ""
else
error "Build failed for $SERVICE"
exit 1
fi
else
# On macOS Docker Desktop, --load can hang for a long time at "sending tarball".
# Use the docker exporter explicitly to load into the local Docker daemon.
BUILDX_ARGS=(buildx build --platform "$PLATFORM" --file "$DOCKERFILE" --tag "$IMAGE_TAG" --output=type=docker)
if [ -n "$BUILD_TARGET" ]; then if [ -n "$BUILD_TARGET" ]; then
BUILDX_ARGS+=(--target "$BUILD_TARGET") BUILDX_ARGS+=(--target "$BUILD_TARGET")
fi fi
@@ -392,6 +378,407 @@ for SERVICE in $SERVICES; do
error "Build failed for $SERVICE" error "Build failed for $SERVICE"
exit 1 exit 1
fi fi
fi
true
if [ "$USE_DOCKER_BUILD" = false ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = true ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = false ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = true ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = false ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = true ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = false ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = true ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = false ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = true ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = false ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = true ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = false ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = true ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = false ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = true ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = false ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = true ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = false ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = true ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = false ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = true ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = false ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = true ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = false ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = true ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = false ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = true ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = false ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = true ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = false ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = true ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = false ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = true ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = false ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = true ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = false ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = true ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = false ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = true ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = false ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = true ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = false ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = true ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = false ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = true ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = false ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = true ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = false ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = true ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = false ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = true ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = false ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = true ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = false ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = true ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = false ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = true ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = false ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = true ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = false ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = true ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = false ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = true ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = false ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = true ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = false ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = true ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = false ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = true ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = false ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = true ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = false ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = true ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = false ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = true ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = false ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = true ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = false ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = true ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = false ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = true ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = false ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = true ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = false ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = true ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = false ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = true ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = false ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = true ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = false ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = true ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = false ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = true ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = false ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = true ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = false ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = true ]; then
true
fi
if [ "$USE_DOCKER_BUILD" = false ]; then
true
fi
true
done done
success "Build complete! ${#BUILT_IMAGES[@]} image(s) ready" success "Build complete! ${#BUILT_IMAGES[@]} image(s) ready"
@@ -412,26 +799,37 @@ fi
# Detect images based on docker-compose.yml (prefer explicit `image:` entries; fall back to built tags) # Detect images based on docker-compose.yml (prefer explicit `image:` entries; fall back to built tags)
cd "$DEPLOYMENT_DIR" cd "$DEPLOYMENT_DIR"
COMPOSE_SERVICES=$(docker compose -f docker-compose.yml config --services 2>/dev/null || true) COMPOSE_SERVICES=$(docker compose -f docker-compose.yml config --services 2>/dev/null || true)
IMAGES=() PUSH_IMAGES=()
EXTERNAL_IMAGES=()
for S in $COMPOSE_SERVICES; do for S in $COMPOSE_SERVICES; do
IMAGE_FROM_COMPOSE=$(jq -r --arg s "$S" '.services[$s].image // empty' <<< "$COMPOSE_JSON") IMAGE_FROM_COMPOSE=$(jq -r --arg s "$S" '.services[$s].image // empty' <<< "$COMPOSE_JSON")
HAS_BUILD=$(jq -r --arg s "$S" '(.services[$s].build // empty) | (if . == "" then "" else "yes" end)' <<< "$COMPOSE_JSON")
if [ -n "$IMAGE_FROM_COMPOSE" ]; then if [ -n "$IMAGE_FROM_COMPOSE" ]; then
IMAGES+=("$IMAGE_FROM_COMPOSE") # If it has an explicit image and no build section, it likely won't exist locally (and doesn't need pussh).
if [ -z "$HAS_BUILD" ]; then
EXTERNAL_IMAGES+=("$IMAGE_FROM_COMPOSE")
else
# If a service has both build+image, treat it as a built image (pussh).
PUSH_IMAGES+=("$IMAGE_FROM_COMPOSE")
fi
else else
# If service has a build section (we built it above), use the tag pattern used during build # If service has a build section (we built it above), use the tag pattern used during build
TAG="${PROJECT_NAME}-${S}:latest" TAG="${PROJECT_NAME}-${S}:latest"
# Only include the tag if the image exists locally (avoid pushing unrelated images) # Only include the tag if the image exists locally (avoid pushing unrelated images)
if docker images --format "{{.Repository}}:{{.Tag}}" | grep -q "^${TAG}$"; then if docker images --format "{{.Repository}}:{{.Tag}}" | grep -q "^${TAG}$"; then
IMAGES+=("$TAG") PUSH_IMAGES+=("$TAG")
fi fi
fi fi
done done
# Deduplicate while preserving order # Deduplicate while preserving order
if [ ${#IMAGES[@]} -gt 0 ]; then if [ ${#PUSH_IMAGES[@]} -gt 0 ]; then
IMAGES=($(printf "%s\n" "${IMAGES[@]}" | awk '!seen[$0]++')) PUSH_IMAGES=($(printf "%s\n" "${PUSH_IMAGES[@]}" | awk '!seen[$0]++'))
fi
if [ ${#EXTERNAL_IMAGES[@]} -gt 0 ]; then
EXTERNAL_IMAGES=($(printf "%s\n" "${EXTERNAL_IMAGES[@]}" | awk '!seen[$0]++'))
fi fi
# Verify that all built images are among the detected images to be pushed. # Verify that all built images are among the detected images to be pushed.
@@ -441,7 +839,7 @@ MATCHING_BUILT=0
MISSING_FROM_DETECTED=() MISSING_FROM_DETECTED=()
for BI in "${BUILT_IMAGES[@]}"; do for BI in "${BUILT_IMAGES[@]}"; do
found=false found=false
for DI in "${IMAGES[@]}"; do for DI in "${PUSH_IMAGES[@]}"; do
if [ "$BI" = "$DI" ]; then if [ "$BI" = "$DI" ]; then
found=true found=true
break break
@@ -454,9 +852,9 @@ for BI in "${BUILT_IMAGES[@]}"; do
fi fi
done done
# Also list detected images that weren't built locally (these are likely external images) # Also list push images that weren't built locally (these are likely prebuilt local images)
NOT_BUILT_DETECTED=() NOT_BUILT_DETECTED=()
for DI in "${IMAGES[@]}"; do for DI in "${PUSH_IMAGES[@]}"; do
built=false built=false
for BI in "${BUILT_IMAGES[@]}"; do for BI in "${BUILT_IMAGES[@]}"; do
if [ "$DI" = "$BI" ]; then if [ "$DI" = "$BI" ]; then
@@ -481,33 +879,44 @@ if [ "$BUILT_COUNT" -ne "$MATCHING_BUILT" ]; then
exit 1 exit 1
fi fi
if [ ${#IMAGES[@]} -eq 0 ]; then if [ ${#PUSH_IMAGES[@]} -eq 0 ] && [ ${#EXTERNAL_IMAGES[@]} -eq 0 ]; then
error "No images found in docker-compose.yml or built locally for project ${PROJECT_NAME}" error "No images found in docker-compose.yml or built locally for project ${PROJECT_NAME}"
fi fi
# Pre-pull unregistry image if needed # Pre-pull unregistry image if needed
UNREGISTRY_IMAGE="ghcr.io/psviderski/unregistry" UNREGISTRY_IMAGE="ghcr.io/psviderski/unregistry"
if ! ssh "$SERVER" "docker images --format '{{.Repository}}:{{.Tag}}' | grep -q '^${UNREGISTRY_IMAGE}$'" 2>/dev/null; then if ! ssh "$SERVER" "sudo docker images --format '{{.Repository}}:{{.Tag}}' | grep -q '^${UNREGISTRY_IMAGE}$'" 2>/dev/null; then
substep "Pulling unregistry image (one-time setup)..." substep "Pulling unregistry image (one-time setup)..."
ssh "$SERVER" "docker pull ${UNREGISTRY_IMAGE}" > /dev/null 2>&1 || true ssh -tt "$SERVER" "sudo docker pull ${UNREGISTRY_IMAGE}"
fi fi
# Transfer images # Transfer images
step "Transferring images" step "Transferring images"
PUSH_FAILED=0 for IMAGE in "${PUSH_IMAGES[@]}"; do
for IMAGE in "${IMAGES[@]}"; do
substep "Pushing ${CYAN}$IMAGE${NC}..." substep "Pushing ${CYAN}$IMAGE${NC}..."
if docker pussh "$IMAGE" "$SERVER"; then if docker pussh "$IMAGE" "$SERVER"; then
echo "" echo ""
else else
echo -e " ${RED}${NC} Failed to push ${CYAN}$IMAGE${NC}" echo -e " ${RED}${NC} Failed to push ${CYAN}$IMAGE${NC}"
PUSH_FAILED=1
echo "" echo ""
exit 1
fi fi
done done
if [ $PUSH_FAILED -eq 1 ]; then # Pull external images directly on the server (no pussh)
error "Image transfer failed" if [ ${#EXTERNAL_IMAGES[@]} -gt 0 ]; then
step "Pulling external images on server"
for IMAGE in "${EXTERNAL_IMAGES[@]}"; do
substep "Pulling ${CYAN}$IMAGE${NC}..."
# Allocate a TTY and do not redirect output so failures are visible.
if ssh -tt "$SERVER" "sudo docker pull $(printf '%q' "$IMAGE")"; then
echo ""
else
echo -e " ${RED}${NC} Failed to pull ${CYAN}$IMAGE${NC} on server"
echo ""
exit 1
fi
done
fi fi
# Transfer files # Transfer files