#!/usr/bin/env bash set -euo pipefail usage() { cat <<'EOF' Usage: sudo ./scripts/deploy-do.sh --domain example.com [options] Required: --domain DOMAIN Primary domain name, for example lean-101.com Optional: --email EMAIL Email for Let's Encrypt. If omitted, TLS is skipped. --app-dir PATH Project directory on the server. Default: current repo root --app-port PORT Local Docker port exposed by compose. Default: 8080 --with-www Also configure and request TLS for www.DOMAIN --skip-certbot Skip Let's Encrypt even if --email is provided --help Show this help Examples: sudo ./scripts/deploy-do.sh --domain lean-101.com --email ops@example.com --with-www sudo ./scripts/deploy-do.sh --domain lean-101.com --skip-certbot EOF } require_root() { if [[ "${EUID}" -ne 0 ]]; then echo "Run this script with sudo or as root." exit 1 fi } require_command() { local command_name="$1" if ! command -v "${command_name}" >/dev/null 2>&1; then echo "Missing required command: ${command_name}" exit 1 fi } pick_compose_package() { if apt-cache show docker-compose-plugin >/dev/null 2>&1; then echo "docker-compose-plugin" return fi if apt-cache show docker-compose-v2 >/dev/null 2>&1; then echo "docker-compose-v2" return fi echo "" } SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" DOMAIN="" EMAIL="" APP_DIR="${REPO_ROOT}" APP_PORT="8080" WITH_WWW="false" SKIP_CERTBOT="false" while [[ $# -gt 0 ]]; do case "$1" in --domain) DOMAIN="${2:-}" shift 2 ;; --email) EMAIL="${2:-}" shift 2 ;; --app-dir) APP_DIR="${2:-}" shift 2 ;; --app-port) APP_PORT="${2:-}" shift 2 ;; --with-www) WITH_WWW="true" shift ;; --skip-certbot) SKIP_CERTBOT="true" shift ;; --help|-h) usage exit 0 ;; *) echo "Unknown argument: $1" usage exit 1 ;; esac done if [[ -z "${DOMAIN}" ]]; then echo "--domain is required." usage exit 1 fi require_root require_command apt-get if [[ ! -f "${APP_DIR}/docker-compose.yml" ]]; then echo "Could not find docker-compose.yml in ${APP_DIR}" exit 1 fi if [[ ! -f "${APP_DIR}/Dockerfile" ]]; then echo "Could not find Dockerfile in ${APP_DIR}" exit 1 fi APT_PACKAGES=( software-properties-common docker.io nginx certbot python3-certbot-nginx ) echo "Installing server packages..." apt-get update DEBIAN_FRONTEND=noninteractive apt-get install -y software-properties-common add-apt-repository -y universe apt-get update COMPOSE_PACKAGE="$(pick_compose_package)" if [[ -z "${COMPOSE_PACKAGE}" ]]; then echo "Could not find a Docker Compose package via apt." echo "Expected either docker-compose-plugin or docker-compose-v2." exit 1 fi APT_PACKAGES+=("${COMPOSE_PACKAGE}") DEBIAN_FRONTEND=noninteractive apt-get install -y "${APT_PACKAGES[@]}" echo "Enabling services..." systemctl enable --now docker systemctl enable --now nginx if command -v ufw >/dev/null 2>&1; then echo "Configuring UFW rules..." ufw allow OpenSSH >/dev/null 2>&1 || true ufw allow 'Nginx Full' >/dev/null 2>&1 || true fi PUBLIC_SITE_URL="https://${DOMAIN}" echo "Writing ${APP_DIR}/.env..." cat > "${APP_DIR}/.env" < "${APP_DIR}/docker-compose.override.yml" < "${NGINX_SITE}" <