From 4bc352b37380b4a1b00604b55343e8a2aebf0810 Mon Sep 17 00:00:00 2001 From: Nik Afiq Date: Sat, 24 Jan 2026 19:50:55 +0900 Subject: [PATCH] Add deploy script for Nginx configuration management and site deployment --- deploy-nginx.sh | 228 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 228 insertions(+) create mode 100644 deploy-nginx.sh diff --git a/deploy-nginx.sh b/deploy-nginx.sh new file mode 100644 index 0000000..c650f94 --- /dev/null +++ b/deploy-nginx.sh @@ -0,0 +1,228 @@ +#!/usr/bin/env bash +set -euo pipefail + +# --------------------------- +# Config +# --------------------------- +REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +NGINX_ETC="/etc/nginx" +SITES_AVAIL="$NGINX_ETC/sites-available" +SITES_ENABLED="$NGINX_ETC/sites-enabled" +SNIPPETS="$NGINX_ETC/snippets" +CONFD="$NGINX_ETC/conf.d" + +# Where to deploy static files from ./http and ./robots.txt +WEB_ROOT="${WEB_ROOT:-/var/www/nginx-conf}" + +# Which sites to ensure are enabled (symlinked) +ENABLE_SITES=( + "default" + "nik4nao.home.arpa" + "nik4nao.xyz" + "prv-api.nik4nao.xyz" +) + +# Backup location +BACKUP_BASE="/var/backups/nginx-conf-deploy" + +# --------------------------- +# Helpers +# --------------------------- +usage() { + cat <<'EOF' +Usage: + sudo ./deploy-nginx.sh [--dry-run] + +Options: + --dry-run Show actions, do not write/reload. +Env: + WEB_ROOT=/some/path Override static deploy directory (default: /var/www/nginx-conf) +EOF +} + +need_root() { + if [[ "${EUID}" -ne 0 ]]; then + echo "ERROR: run as root (use sudo)." >&2 + exit 1 + fi +} + +log() { echo "==> $*"; } + +DRY_RUN=0 +if [[ "${1:-}" == "--dry-run" ]]; then + DRY_RUN=1 +elif [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then + usage + exit 0 +elif [[ -n "${1:-}" ]]; then + echo "Unknown option: $1" >&2 + usage + exit 1 +fi + +need_root + +timestamp="$(date +%Y%m%d-%H%M%S)" +backup_dir="${BACKUP_BASE}/${timestamp}" +backup_tar="${backup_dir}/backup.tar.gz" + +mkdir_p() { + if (( DRY_RUN )); then + log "[dry-run] mkdir -p $*" + else + mkdir -p "$@" + fi +} + +copy_file() { + local src="$1" + local dst="$2" + if [[ ! -f "$src" ]]; then + echo "ERROR: missing source file: $src" >&2 + exit 1 + fi + if (( DRY_RUN )); then + log "[dry-run] install -m 0644 $src -> $dst" + else + install -m 0644 -D "$src" "$dst" + fi +} + +sync_dir() { + local src="$1" + local dst="$2" + if [[ ! -d "$src" ]]; then + echo "ERROR: missing source dir: $src" >&2 + exit 1 + fi + if (( DRY_RUN )); then + log "[dry-run] rsync -a --delete $src/ -> $dst/" + else + rsync -a --delete "${src}/" "${dst}/" + fi +} + +make_symlink() { + local target="$1" + local linkpath="$2" + if (( DRY_RUN )); then + log "[dry-run] ln -sfn $target $linkpath" + else + ln -sfn "$target" "$linkpath" + fi +} + +backup_paths=() + +add_backup_path() { + local p="$1" + if [[ -e "$p" || -L "$p" ]]; then + backup_paths+=("$p") + fi +} + +do_backup() { + mkdir_p "$backup_dir" + # Backup only what we touch (and their symlinks) + add_backup_path "$NGINX_ETC/nginx.conf" + add_backup_path "$CONFD/upstreams.conf" + add_backup_path "$SNIPPETS/proxy-common.conf" + for s in "${ENABLE_SITES[@]}"; do + add_backup_path "$SITES_AVAIL/$s" + add_backup_path "$SITES_ENABLED/$s" + done + + if (( DRY_RUN )); then + log "[dry-run] tar -czf $backup_tar -P ${backup_paths[*]}" + return + fi + + if ((${#backup_paths[@]} == 0)); then + log "No existing paths found to backup (first deploy?)." + # still create empty backup dir for consistency + return + fi + + log "Creating backup: $backup_tar" + tar -czf "$backup_tar" -P "${backup_paths[@]}" +} + +restore_backup() { + if (( DRY_RUN )); then + log "[dry-run] restore from $backup_tar" + return + fi + if [[ ! -f "$backup_tar" ]]; then + log "No backup tar found to restore: $backup_tar" + return + fi + log "Restoring from backup..." + tar -xzf "$backup_tar" -P +} + +nginx_test() { + if (( DRY_RUN )); then + log "[dry-run] nginx -t" + return 0 + fi + nginx -t +} + +nginx_reload() { + if (( DRY_RUN )); then + log "[dry-run] systemctl reload nginx" + return 0 + fi + systemctl reload nginx +} + +# --------------------------- +# Deploy +# --------------------------- +log "Repo: $REPO_DIR" +log "Deploying to: $NGINX_ETC" +log "Static WEB_ROOT: $WEB_ROOT" + +do_backup + +# Ensure target dirs exist +mkdir_p "$SITES_AVAIL" "$SITES_ENABLED" "$SNIPPETS" "$CONFD" "$WEB_ROOT" + +# Copy core config/snippets +copy_file "$REPO_DIR/nginx.conf" "$NGINX_ETC/nginx.conf" +copy_file "$REPO_DIR/upstreams.conf" "$CONFD/upstreams.conf" +copy_file "$REPO_DIR/proxy-common.conf" "$SNIPPETS/proxy-common.conf" + +# Copy site configs (repo *.conf -> /etc/nginx/sites-available/) +copy_file "$REPO_DIR/default.conf" "$SITES_AVAIL/default" +copy_file "$REPO_DIR/nik4nao.home.arpa.conf" "$SITES_AVAIL/nik4nao.home.arpa" +copy_file "$REPO_DIR/nik4nao.xyz.conf" "$SITES_AVAIL/nik4nao.xyz" +copy_file "$REPO_DIR/prv-api.nik4nao.xyz.conf" "$SITES_AVAIL/prv-api.nik4nao.xyz" + +# Enable sites (symlinks) +for s in "${ENABLE_SITES[@]}"; do + make_symlink "$SITES_AVAIL/$s" "$SITES_ENABLED/$s" +done + +# Deploy static content +sync_dir "$REPO_DIR/http" "$WEB_ROOT" +if [[ -f "$REPO_DIR/robots.txt" ]]; then + copy_file "$REPO_DIR/robots.txt" "$WEB_ROOT/robots.txt" +fi + +# Test + reload (rollback on failure) +log "Testing nginx config..." +if nginx_test; then + log "Reloading nginx..." + nginx_reload + log "Done ✅" +else + log "nginx -t failed ❌" + log "Rolling back..." + restore_backup + log "Re-testing after rollback..." + nginx_test || true + exit 2 +fi \ No newline at end of file