#!/usr/bin/env bash set -Eeuo pipefail REMOTE="${BLISS_DEPLOY_REMOTE:-root@134.122.14.193}" APP_DIR="${BLISS_DEPLOY_APP_DIR:-/home/nodejs/bliss2}" ROOT_DIR="$(cd "$(dirname "$0")" && pwd)" LOCAL_BRANCH="${BLISS_DEPLOY_BRANCH:-main}" TRANSFER_DIR="$(mktemp -d)" CONTROL_SOCKET="$TRANSFER_DIR/ssh-control" LOCAL_BUNDLE="$TRANSFER_DIR/bliss.bundle" cleanup() { ssh -S "$CONTROL_SOCKET" -O exit "$REMOTE" >/dev/null 2>&1 || true rm -rf "$TRANSFER_DIR" } trap cleanup EXIT echo "Updating $REMOTE:$APP_DIR" echo "You may be prompted for the droplet passcode." git -C "$ROOT_DIR" rev-parse --verify "$LOCAL_BRANCH" >/dev/null echo "Bundling committed local branch $LOCAL_BRANCH..." git -C "$ROOT_DIR" bundle create "$LOCAL_BUNDLE" "$LOCAL_BRANCH" ssh -M -S "$CONTROL_SOCKET" -o ControlPersist=60 -o ConnectTimeout=15 -fnNT "$REMOTE" REMOTE_BUNDLE="$(ssh -S "$CONTROL_SOCKET" "$REMOTE" mktemp /tmp/bliss-deploy.XXXXXX.bundle)" scp -q -o ControlPath="$CONTROL_SOCKET" "$LOCAL_BUNDLE" "$REMOTE:$REMOTE_BUNDLE" ssh -S "$CONTROL_SOCKET" "$REMOTE" bash -s -- "$APP_DIR" "$REMOTE_BUNDLE" "$LOCAL_BRANCH" <<'REMOTE_SCRIPT' set -Eeuo pipefail APP_DIR="$1" DEPLOY_BUNDLE="$2" DEPLOY_BRANCH="$3" cleanup_bundle() { rm -f "$DEPLOY_BUNDLE" } trap cleanup_bundle EXIT cd "$APP_DIR" test -f package.json || { echo "No package.json in $APP_DIR" >&2; exit 1; } test -d .git || { echo "$APP_DIR is not a Git checkout" >&2; exit 1; } if [[ -n "$(git status --porcelain --untracked-files=no)" ]]; then echo "Refusing to pull: the deployed checkout has uncommitted tracked changes:" >&2 git status --short --untracked-files=no >&2 exit 1 fi OLD_HEAD="$(git rev-parse HEAD)" OLD_LOCK="$(sha256sum package-lock.json 2>/dev/null | awk '{print $1}' || true)" echo "Pulling Git changes..." git fetch "$DEPLOY_BUNDLE" "$DEPLOY_BRANCH" git merge --ff-only FETCH_HEAD NEW_HEAD="$(git rev-parse HEAD)" NEW_LOCK="$(sha256sum package-lock.json 2>/dev/null | awk '{print $1}' || true)" if [[ ! -d node_modules || "$OLD_LOCK" != "$NEW_LOCK" ]]; then echo "Installing production dependencies..." if [[ -f package-lock.json ]]; then npm ci --omit=dev else npm install --omit=dev fi else echo "Dependencies unchanged; skipping npm install." fi mapfile -t APP_PIDS < <( for process_dir in /proc/[0-9]*; do [[ "$(readlink "$process_dir/cwd" 2>/dev/null || true)" == "$APP_DIR" ]] || continue [[ "$(cat "$process_dir/comm" 2>/dev/null || true)" == "node" ]] || continue basename "$process_dir" done ) if [[ "${#APP_PIDS[@]}" -eq 0 ]]; then echo "No running Node process found for $APP_DIR; refusing to guess how to start it." >&2 exit 1 fi APP_PID="${APP_PIDS[0]}" SERVICE="" while IFS= read -r unit; do [[ -n "$unit" ]] || continue if [[ "$(systemctl show -p MainPID --value "$unit" 2>/dev/null || true)" == "$APP_PID" ]]; then SERVICE="$unit" break fi done < <(systemctl list-units --type=service --all --no-legend --plain 2>/dev/null | awk '{print $1}') if [[ -n "$SERVICE" ]]; then echo "Restarting systemd service $SERVICE..." systemctl restart "$SERVICE" systemctl is-active --quiet "$SERVICE" systemctl --no-pager --full status "$SERVICE" | sed -n '1,12p' elif command -v pm2 >/dev/null 2>&1 && pm2 pid all 2>/dev/null | grep -qx "$APP_PID"; then echo "Restarting PM2 process $APP_PID..." pm2 restart "$APP_PID" --update-env pm2 show "$APP_PID" else echo "Found Node PID $APP_PID, but it is not managed by systemd or PM2." >&2 echo "The code was updated from $OLD_HEAD to $NEW_HEAD, but the process was not restarted." >&2 echo "Refusing to kill it without knowing the correct startup mechanism." >&2 exit 1 fi echo "Deployment updated successfully: $OLD_HEAD -> $NEW_HEAD" REMOTE_SCRIPT