deploy.sh
Rama main · réplica de lectura del repositorio original.
#!/usr/bin/env bash
# deploy.sh — Deploys this repo to a remote Docker host over SSH.
#
# Reads HOST and SUDO_PASS from .env.deploy (gitignored). Example .env.deploy:
# HOST=user@1.2.3.4
# REMOTE_DIR=/home/user/patzer
# SUDO_PASS=...
# HOST_PORT=8800
#
# Usage:
# ./deploy.sh # tar source → ssh, build & start
# ./deploy.sh --no-build # restart without rebuilding the image
# ./deploy.sh --logs # tail logs after deploy
set -euo pipefail
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
env_file="$script_dir/.env.deploy"
if [[ ! -f "$env_file" ]]; then
echo ".env.deploy not found. Create it with HOST=user@host, REMOTE_DIR=/path, SUDO_PASS=... (optional)" >&2
exit 1
fi
# Parse .env.deploy without source'ing it (values may contain shell metacharacters)
HOST_TARGET=""
REMOTE_DIR=""
SUDO_PASS=""
HOST_PORT="8800"
while IFS= read -r line || [[ -n "$line" ]]; do
[[ "$line" =~ ^[[:space:]]*# ]] && continue
[[ "$line" =~ ^[[:space:]]*$ ]] && continue
if [[ "$line" =~ ^[[:space:]]*([A-Z_][A-Z0-9_]*)[[:space:]]*=[[:space:]]*(.*)$ ]]; then
key="${BASH_REMATCH[1]}"
value="${BASH_REMATCH[2]}"
# Strip surrounding quotes if present and trailing whitespace
value="${value%$'\r'}"
value="${value%\"}"; value="${value#\"}"
value="${value%\'}"; value="${value#\'}"
case "$key" in
HOST) HOST_TARGET="$value" ;;
REMOTE_DIR) REMOTE_DIR="$value" ;;
SUDO_PASS) SUDO_PASS="$value" ;;
HOST_PORT) HOST_PORT="$value" ;;
esac
fi
done < "$env_file"
if [[ -z "$HOST_TARGET" || -z "$REMOTE_DIR" ]]; then
echo "HOST and REMOTE_DIR must be set in .env.deploy" >&2
exit 1
fi
no_build=0
tail_logs=0
for arg in "$@"; do
case "$arg" in
--no-build|-NoBuild) no_build=1 ;;
--logs|-Logs) tail_logs=1 ;;
-h|--help)
sed -n '2,16p' "$0"; exit 0 ;;
*) echo "Unknown argument: $arg" >&2; exit 1 ;;
esac
done
cyan='\033[0;36m'; green='\033[0;32m'; reset='\033[0m'
log() { printf "${cyan}→ %s${reset}\n" "$*"; }
done_() { printf "${green}%s${reset}\n" "$*"; }
remote_exec() {
# shellcheck disable=SC2029
ssh "$HOST_TARGET" "$1"
}
remote_sudo_script() {
local script="$1"
local local_script remote_script rc
local_script="$(mktemp -t patzer-remote-XXXXXX.sh)"
remote_script="/tmp/patzer-remote-$RANDOM-$$.sh"
{
printf '#!/usr/bin/env bash\nset -e\n'
printf '%s\n' "$script"
} > "$local_script"
scp -q "$local_script" "${HOST_TARGET}:${remote_script}"
if [[ -n "$SUDO_PASS" ]]; then
# shellcheck disable=SC2029
ssh "$HOST_TARGET" "chmod +x '$remote_script' && echo '$SUDO_PASS' | sudo -S -p '' bash '$remote_script'; rc=\$?; rm -f '$remote_script'; exit \$rc"
else
# shellcheck disable=SC2029
ssh "$HOST_TARGET" "chmod +x '$remote_script' && sudo bash '$remote_script'; rc=\$?; rm -f '$remote_script'; exit \$rc"
fi
rc=$?
rm -f "$local_script"
return $rc
}
log "Ensuring remote dir $REMOTE_DIR exists"
remote_exec "mkdir -p '$REMOTE_DIR'"
log "Syncing source to ${HOST_TARGET}:${REMOTE_DIR}"
local_tar="$(mktemp -t patzer-deploy-XXXXXX.tar.gz)"
remote_tar="/tmp/patzer-deploy-$RANDOM-$$.tar.gz"
trap 'rm -f "$local_tar"' EXIT
tar \
--exclude='node_modules' --exclude='.git' --exclude='dist' \
--exclude='build' --exclude='data' --exclude='bin' \
--exclude='.env' --exclude='.env.deploy' \
-czf "$local_tar" -C "$script_dir" .
scp -q "$local_tar" "${HOST_TARGET}:${remote_tar}"
remote_exec "tar -xzf '$remote_tar' -C '$REMOTE_DIR' && rm -f '$remote_tar'"
if [[ $no_build -eq 0 ]]; then
log "Building image on remote (this can take a few minutes)"
remote_sudo_script "cd '$REMOTE_DIR'
HOST_PORT=$HOST_PORT docker compose build"
fi
log "Starting container"
remote_sudo_script "cd '$REMOTE_DIR'
HOST_PORT=$HOST_PORT docker compose up -d
sleep 2
docker compose ps"
if [[ $tail_logs -eq 1 ]]; then
remote_sudo_script "cd '$REMOTE_DIR'
docker compose logs --tail=50"
fi
server_ip="${HOST_TARGET##*@}"
echo
done_ "Deployed. Open http://${server_ip}:${HOST_PORT}"