bliss/update-deployed.sh
Your Name 5bf756833d chore: incidental working-tree changes (not from the versioning work)
Changes already present in the working tree at the time of the versioning
commit, committed here so nothing is left dangling:

- index.js: template PUT preserves the untouched field (?? existing) and the
  preview handler tolerates a null/empty test_object
- update-deployed.sh: detect and restart a pm2-managed app process, and don't
  hard-fail when no bare Node process is found

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-03 00:27:31 -04:00

129 lines
4.1 KiB
Bash
Executable file

#!/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
PM2_TARGET=""
if command -v pm2 >/dev/null 2>&1; then
PM2_TARGET="$(pm2 jlist 2>/dev/null | APP_DIR="$APP_DIR" node -e '
let input = "";
process.stdin.on("data", chunk => input += chunk);
process.stdin.on("end", () => {
try {
const appDir = process.env.APP_DIR;
const processes = JSON.parse(input);
const match = processes.find(process => {
const env = process.pm2_env || {};
return env.pm_cwd === appDir || env.pm_exec_path === `${appDir}/index.js`;
});
if (match) process.stdout.write(String(match.pm_id));
} catch (_) {}
});
' || true)"
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
)
APP_PID="${APP_PIDS[0]:-}"
SERVICE=""
if [[ -n "$APP_PID" ]]; then
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}')
fi
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 [[ -n "$PM2_TARGET" ]]; then
echo "Restarting PM2 process $PM2_TARGET..."
pm2 restart "$PM2_TARGET" --update-env
pm2 show "$PM2_TARGET"
else
echo "Could not find a systemd or PM2 app configured for $APP_DIR." >&2
echo "The code was updated from $OLD_HEAD to $NEW_HEAD, but the process was not restarted." >&2
exit 1
fi
echo "Deployment updated successfully: $OLD_HEAD -> $NEW_HEAD"
REMOTE_SCRIPT