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>
129 lines
4.1 KiB
Bash
Executable file
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
|