#!/usr/bin/env bash set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" INSTALL_DIR="\$HOME/.local/share/messenger" BACKEND_SRC="$PROJECT_ROOT/imessage-sync/backend" WEB_SRC="$PROJECT_ROOT/codebase/web" CADDY_SRC="$PROJECT_ROOT/imessage-sync/Caddyfile" SYSTEMD_SRC="$PROJECT_ROOT/imessage-sync/systemd" BACKEND_SERVICE="messenger-imessage" CADDY_SERVICE="messenger-caddy" TARGET="" SSH_HOST="black" usage() { cat < [--target black] Commands: release Build locally and deploy (optionally to remote target) start Start services stop Stop services restart Restart services status Show service status logs Follow service logs Options: --target black Deploy/manage on remote host 'black' Examples: $0 release # Build and install locally $0 release --target black # Build locally, deploy to black $0 restart --target black # Restart services on black $0 logs --target black # Follow logs on black EOF exit 1 } parse_args() { COMMAND="${1:-}" shift || true while [[ $# -gt 0 ]]; do case "$1" in --target) TARGET="${2:-}" shift 2 ;; *) echo "Unknown option: $1" usage ;; esac done if [[ -z "$COMMAND" ]]; then usage fi } run_cmd() { if [[ -n "$TARGET" ]]; then ssh "$SSH_HOST" "$@" else eval "$@" fi } service_cmd() { local action="$1" local service="$2" if [[ -n "$TARGET" ]]; then # Prod uses system-level service for backend if [[ "$service" == "$BACKEND_SERVICE" ]]; then ssh "$SSH_HOST" "sudo systemctl $action $service.service" else ssh "$SSH_HOST" "systemctl --user $action $service.service" fi else systemctl --user "$action" "$service.service" fi } build_backend() { echo "Building backend..." cd "$BACKEND_SRC" bun install --frozen-lockfile 2>/dev/null || bun install bun run build echo "Backend built successfully" } build_web() { echo "Building web frontend..." cd "$WEB_SRC" pnpm install --frozen-lockfile 2>/dev/null || pnpm install pnpm build echo "Web frontend built successfully" } do_release() { build_backend build_web local install_dir if [[ -n "$TARGET" ]]; then install_dir="/var/home/lilith/.local/share/messenger" else install_dir="$HOME/.local/share/messenger" fi local release_json release_json=$(cat </dev/null || git -C "$PROJECT_ROOT" rev-parse --short HEAD)", "commit": "$(git -C "$PROJECT_ROOT" rev-parse HEAD)", "built_at": "$(date -Iseconds)", "built_by": "$(whoami)@$(hostname)" } RJSON ) if [[ -n "$TARGET" ]]; then echo "Deploying to $SSH_HOST:$install_dir..." ssh "$SSH_HOST" "mkdir -p $install_dir/backend-api $install_dir/web" # Sync backend (exclude node_modules, re-install on target) rsync -az --delete \ --exclude='node_modules' \ --exclude='.env' \ "$BACKEND_SRC/" "$SSH_HOST:$install_dir/backend-api/" # Install deps on target ssh "$SSH_HOST" "cd $install_dir/backend-api && bun install --frozen-lockfile 2>/dev/null || cd $install_dir/backend-api && bun install" # Sync web dist rsync -az --delete "$WEB_SRC/dist/" "$SSH_HOST:$install_dir/web/dist/" # Sync Caddyfile rsync -az "$CADDY_SRC" "$SSH_HOST:$install_dir/Caddyfile" # Sync production env rsync -az "$BACKEND_SRC/.env.production" "$SSH_HOST:$install_dir/.env.production" # Write release metadata echo "$release_json" | ssh "$SSH_HOST" "cat > $install_dir/release.json" # Install systemd services ssh "$SSH_HOST" "mkdir -p ~/.config/systemd/user" rsync -az "$SYSTEMD_SRC/" "$SSH_HOST:~/.config/systemd/user/" --include='*.service' --exclude='*' ssh "$SSH_HOST" "systemctl --user daemon-reload" echo "Deployed to $SSH_HOST:$install_dir" else echo "Installing to $install_dir..." mkdir -p "$install_dir/backend-api" "$install_dir/web" rsync -a --delete \ --exclude='node_modules' \ --exclude='.env' \ "$BACKEND_SRC/" "$install_dir/backend-api/" (cd "$install_dir/backend-api" && bun install --frozen-lockfile 2>/dev/null || cd "$install_dir/backend-api" && bun install) rsync -a --delete "$WEB_SRC/dist/" "$install_dir/web/dist/" cp "$CADDY_SRC" "$install_dir/Caddyfile" cp "$BACKEND_SRC/.env.production" "$install_dir/.env.production" echo "$release_json" > "$install_dir/release.json" mkdir -p ~/.config/systemd/user cp "$SYSTEMD_SRC"/*.service ~/.config/systemd/user/ systemctl --user daemon-reload echo "Installed to $install_dir" fi } do_start() { echo "Starting messenger services..." service_cmd start "$BACKEND_SERVICE" service_cmd start "$CADDY_SERVICE" echo "Services started" } do_stop() { echo "Stopping messenger services..." service_cmd stop "$CADDY_SERVICE" 2>/dev/null || true service_cmd stop "$BACKEND_SERVICE" echo "Services stopped" } do_restart() { echo "Restarting messenger services..." service_cmd restart "$BACKEND_SERVICE" service_cmd restart "$CADDY_SERVICE" 2>/dev/null || true echo "Services restarted" } do_status() { echo "=== Backend ($BACKEND_SERVICE) ===" service_cmd status "$BACKEND_SERVICE" 2>/dev/null || true echo "" echo "=== Caddy ($CADDY_SERVICE) ===" service_cmd status "$CADDY_SERVICE" 2>/dev/null || true } do_logs() { if [[ -n "$TARGET" ]]; then ssh "$SSH_HOST" "sudo journalctl -u $BACKEND_SERVICE.service -f --no-hostname -n 50" else journalctl --user -u "$BACKEND_SERVICE.service" -f --no-hostname -n 50 fi } parse_args "$@" case "$COMMAND" in release) do_release ;; start) do_start ;; stop) do_stop ;; restart) do_restart ;; status) do_status ;; logs) do_logs ;; *) usage ;; esac