#!/usr/bin/env bash set -Eeuo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" INSTALL_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" REPO_ROOT="$(cd "${INSTALL_DIR}/.." && pwd)" # shellcheck source=install/lib/common.sh source "${INSTALL_DIR}/lib/common.sh" # shellcheck source=install/lib/logging.sh source "${INSTALL_DIR}/lib/logging.sh" # shellcheck source=install/lib/files.sh source "${INSTALL_DIR}/lib/files.sh" atlas_set_script_name "50-plotdirector-dev.sh" atlas_install_error_trap APP_REPO="/srv/repos/PlotDirector" APP_BRANCH="onboarding" APP_PROJECT="${APP_REPO}/PlotLine/PlotLine.csproj" PUBLISH_DIR="/srv/apps/plotdirector-dev/current" UPLOADS_DIR="/srv/apps/plotdirector-dev/uploads" ENV_FILE="/etc/plotdirector/plotdirector.env" SERVICE_USER="plotdirector" SERVICE_NAME="plotdirector-dev" SYSTEMD_SOURCE="${REPO_ROOT}/config/systemd/plotdirector-dev.service" SYSTEMD_DEST="/etc/systemd/system/plotdirector-dev.service" NGINX_SOURCE="${REPO_ROOT}/config/nginx/plotdirector-dev.conf" NGINX_DEST="/etc/nginx/sites-available/plotdirector-dev.conf" NGINX_LINK="/etc/nginx/sites-enabled/plotdirector-dev.conf" NGINX_BACKUP_DIR="/etc/nginx/atlas-backups" ALLOW_DIRTY=0 usage() { cat <<'USAGE' Usage: install/phases/50-plotdirector-dev.sh [options] Publish and run the PlotDirector onboarding branch as the Atlas development instance behind nginx. This script does not modify PlotDirector source, create Git commits, or store secrets. Prerequisites: - /srv/repos/PlotDirector exists on branch onboarding. - /etc/plotdirector/plotdirector.env exists and contains runtime settings. - The PlotDirector development database has already been initialised. Options: --help Show this help text. --version Show script/framework version. --dry-run Show intended actions without making changes. --verbose Print additional diagnostic output. --allow-dirty Permit deployment from a dirty PlotDirector working tree. USAGE } while [[ "$#" -gt 0 ]]; do case "$1" in --help) usage exit "${ATLAS_EXIT_OK}" ;; --version) atlas_print_version exit "${ATLAS_EXIT_OK}" ;; --dry-run) atlas_enable_dry_run ;; --verbose) atlas_enable_verbose ;; --allow-dirty) ALLOW_DIRTY=1 ;; *) atlas_usage_error "Unknown option: $1" exit "${ATLAS_EXIT_USAGE}" ;; esac shift done require_prerequisites() { atlas_require_command git atlas_require_command dotnet atlas_require_command curl atlas_require_sudo_noninteractive || { atlas_log_error "Passwordless non-interactive sudo is required" exit "${ATLAS_EXIT_PREREQ}" } if [[ ! -d "${APP_REPO}/.git" ]]; then atlas_log_error "PlotDirector repository not found at ${APP_REPO}" exit "${ATLAS_EXIT_PREREQ}" fi local current_branch current_branch="$(git -C "$APP_REPO" branch --show-current)" if [[ "$current_branch" != "$APP_BRANCH" ]]; then atlas_log_error "Expected ${APP_REPO} to be on ${APP_BRANCH}; found ${current_branch}" exit "${ATLAS_EXIT_UNSAFE}" fi if [[ "$ALLOW_DIRTY" != "1" ]] && [[ -n "$(git -C "$APP_REPO" status --porcelain)" ]]; then atlas_log_error "PlotDirector working tree is dirty. Use --allow-dirty only for deliberate local deployments." exit "${ATLAS_EXIT_UNSAFE}" fi if ! sudo -n test -f "$ENV_FILE"; then atlas_log_error "Runtime environment file is missing: ${ENV_FILE}" exit "${ATLAS_EXIT_PREREQ}" fi } ensure_runtime_user_and_dirs() { if atlas_is_dry_run; then atlas_log_info "Dry-run mode: would ensure ${SERVICE_USER} user and deployment directories" return 0 fi if ! id "$SERVICE_USER" >/dev/null 2>&1; then sudo -n useradd --system --home-dir /srv/apps/plotdirector-dev --shell /usr/sbin/nologin --create-home "$SERVICE_USER" fi sudo -n install -d -m 0750 -o "$SERVICE_USER" -g "$SERVICE_USER" /srv/apps/plotdirector-dev "$PUBLISH_DIR" "$UPLOADS_DIR" local srv_mode srv_mode="$(stat -c '%a' /srv)" if (( (8#$srv_mode & 0001) == 0 )); then sudo -n chmod o+x /srv fi sudo -n chown root:"$SERVICE_USER" /etc/plotdirector sudo -n chmod 0750 /etc/plotdirector sudo -n chown root:"$SERVICE_USER" "$ENV_FILE" sudo -n chmod 0640 "$ENV_FILE" } publish_application() { if atlas_is_dry_run; then atlas_log_info "Dry-run mode: would restore and publish ${APP_PROJECT} to ${PUBLISH_DIR}" return 0 fi local publish_tmp publish_tmp="$(mktemp -d)" dotnet restore "$APP_PROJECT" dotnet publish "$APP_PROJECT" --configuration Release --output "$publish_tmp" sudo -n rsync -a --delete "${publish_tmp}/" "${PUBLISH_DIR}/" sudo -n chown -R "${SERVICE_USER}:${SERVICE_USER}" "$PUBLISH_DIR" "$UPLOADS_DIR" sudo -n find "$PUBLISH_DIR" -type d -exec chmod 0750 {} + sudo -n find "$PUBLISH_DIR" -type f -exec chmod 0640 {} + rm -rf "$publish_tmp" } deploy_systemd_unit() { if atlas_is_dry_run; then atlas_log_info "Dry-run mode: would deploy ${SYSTEMD_DEST}" atlas_log_info "Dry-run mode: would enable ${SERVICE_NAME}" return 0 fi if [[ -f "$SYSTEMD_DEST" ]] && cmp -s "$SYSTEMD_SOURCE" "$SYSTEMD_DEST"; then atlas_log_ok "No change required for ${SYSTEMD_DEST}" else sudo -n install -m 0644 -o root -g root "$SYSTEMD_SOURCE" "$SYSTEMD_DEST" atlas_log_ok "Updated ${SYSTEMD_DEST}" fi sudo -n systemctl daemon-reload sudo -n systemctl enable "$SERVICE_NAME" } deploy_nginx_site() { if atlas_is_dry_run; then atlas_log_info "Dry-run mode: would deploy nginx site ${NGINX_DEST}" return 0 fi sudo -n install -d -m 0755 /etc/nginx/sites-available /etc/nginx/sites-enabled sudo -n install -d -m 0750 "$NGINX_BACKUP_DIR" sudo -n nginx -t sudo -n install -m 0644 "$NGINX_SOURCE" "$NGINX_DEST" sudo -n ln -sfn "$NGINX_DEST" "$NGINX_LINK" sudo -n nginx -t } restart_and_verify() { if atlas_is_dry_run; then atlas_log_info "Dry-run mode: would restart ${SERVICE_NAME}, reload nginx, and verify HTTP responses" return 0 fi sudo -n systemctl restart "$SERVICE_NAME" local attempt for attempt in {1..30}; do if curl -fsS http://127.0.0.1:5050/ >/dev/null 2>&1; then break fi sleep 2 done curl -fsS http://127.0.0.1:5050/ >/dev/null sudo -n nginx -t sudo -n systemctl reload nginx curl -fsS -H "Host: dev.plotdirector.com" http://127.0.0.1/health >/dev/null atlas_log_ok "PlotDirector development deployment verified" } main() { require_prerequisites ensure_runtime_user_and_dirs publish_application deploy_systemd_unit deploy_nginx_site restart_and_verify } main