#!/usr/bin/env bash set -Eeuo pipefail TARGET="laic-backend-prod.srv" REMOTE="root@jg-serv1" REMOTE_DIR="/usr/local/laic-backend-prod" SERVICE_NAME="laic-backend-prod" SUPERVISOR_CONF_DIR="/etc/supervisor/conf.d" WAIT_TIME=3 BACKUP_DIR="$REMOTE_DIR/backup" SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" LOCAL_CONF="$SCRIPT_DIR/config/$SERVICE_NAME.conf" LOCAL_CONFIG="$SCRIPT_DIR/config-prod.yaml" LOCAL_ARTIFACT="$SCRIPT_DIR/$TARGET" cleanup() { rm -f -- "$LOCAL_ARTIFACT" } trap cleanup EXIT remote_cmd() { ssh "$REMOTE" "$@" /dev/null 2>&1 || { printf 'Missing required command: %s\n' "$1" >&2 exit 1 } } check_inputs() { require_command go require_command ssh require_command scp require_command diff [[ -f "$LOCAL_CONF" ]] || { printf 'Missing Supervisor config: %s\n' "$LOCAL_CONF" >&2; exit 1; } [[ -f "$LOCAL_CONFIG" ]] || { printf 'Missing production config: %s\n' "$LOCAL_CONFIG" >&2; exit 1; } if [[ "$REMOTE" == *your-production-host* ]]; then printf 'Set REMOTE in publish-prod.sh before deploying.\n' >&2 exit 1 fi } build_local() { printf 'Building Linux amd64 binary...\n' (cd "$SCRIPT_DIR" && GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -trimpath -ldflags='-s -w' -o "$LOCAL_ARTIFACT" .) } ensure_remote_dirs() { remote_cmd "mkdir -p '$REMOTE_DIR' '$REMOTE_DIR/config' '$REMOTE_DIR/log' '$BACKUP_DIR'" } check_remote_config() { if ! remote_cmd "test -s '$REMOTE_DIR/config.yaml'"; then printf 'Remote config.yaml is missing or empty: %s/config.yaml\n' "$REMOTE_DIR" >&2 printf 'Upload config.yaml with OBS credentials before deploying.\n' >&2 exit 1 fi } backup_old() { remote_cmd " if [ -f '$REMOTE_DIR/$TARGET' ]; then timestamp=\$(date +'%Y%m%d%H%M%S') cp '$REMOTE_DIR/$TARGET' '$BACKUP_DIR/$TARGET.'\$timestamp echo 'Previous binary backed up.' fi if [ -f '$REMOTE_DIR/config.yaml' ]; then timestamp=\$(date +'%Y%m%d%H%M%S') cp '$REMOTE_DIR/config.yaml' '$BACKUP_DIR/config.yaml.'\$timestamp fi " } upload_files() { scp "$LOCAL_ARTIFACT" "$REMOTE:$REMOTE_DIR/$TARGET.new" remote_cmd "mv -f '$REMOTE_DIR/$TARGET.new' '$REMOTE_DIR/$TARGET' && chmod 755 '$REMOTE_DIR/$TARGET'" scp "$LOCAL_CONFIG" "$REMOTE:$REMOTE_DIR/config.yaml.new" remote_cmd "mv -f '$REMOTE_DIR/config.yaml.new' '$REMOTE_DIR/config.yaml' && chmod 640 '$REMOTE_DIR/config.yaml'" } upload_supervisor_conf() { local remote_conf="$SUPERVISOR_CONF_DIR/$SERVICE_NAME.conf" if remote_cmd "test -f '$remote_conf'"; then if diff -q "$LOCAL_CONF" <(ssh "$REMOTE" "cat '$remote_conf'") >/dev/null; then printf 'Supervisor config unchanged.\n' return fi fi scp "$LOCAL_CONF" "$REMOTE:$remote_conf" remote_cmd "supervisorctl reread && supervisorctl update" } restart_service() { local status status="$(remote_cmd "supervisorctl status '$SERVICE_NAME' 2>/dev/null | awk '{print \$2}'" || true)" if [[ "$status" == RUNNING ]]; then remote_cmd "supervisorctl restart '$SERVICE_NAME'" else remote_cmd "supervisorctl start '$SERVICE_NAME'" fi sleep "$WAIT_TIME" if remote_cmd "supervisorctl status '$SERVICE_NAME' | grep -q RUNNING"; then printf 'Service is running.\n' remote_cmd "tail -n 20 '$REMOTE_DIR/log/info.log'" || true return fi printf 'Service failed to start; rolling back binary.\n' >&2 rollback exit 1 } rollback() { local latest_backup latest_backup="$(remote_cmd "ls -t '$BACKUP_DIR/$TARGET.'* 2>/dev/null | head -n 1" || true)" [[ -n "$latest_backup" ]] || { printf 'No binary backup available.\n' >&2; return 1; } remote_cmd "cp '$latest_backup' '$REMOTE_DIR/$TARGET' && chmod 755 '$REMOTE_DIR/$TARGET' && supervisorctl restart '$SERVICE_NAME'" } main() { check_inputs build_local ensure_remote_dirs backup_old upload_files upload_supervisor_conf restart_service printf 'Deployment complete.\n' } main "$@"