From 4fa50fd49e975b7c02abfb88833e5b391159e469 Mon Sep 17 00:00:00 2001 From: CSnap Date: Sun, 24 Aug 2025 19:40:09 -0400 Subject: [PATCH] Changing approach --- bonfire_turnkey.sh | 705 +++++++++++++++++++++++++++++---------------- 1 file changed, 463 insertions(+), 242 deletions(-) diff --git a/bonfire_turnkey.sh b/bonfire_turnkey.sh index aab970a..337dd8d 100644 --- a/bonfire_turnkey.sh +++ b/bonfire_turnkey.sh @@ -1,207 +1,421 @@ #!/usr/bin/env bash +# bonfire_turnkey_asdf_fixed.sh +# All-in-one Bonfire installer using ASDF-managed Erlang/Elixir/Node (fixed: early curl/gnupg + pgdg key) +# Target: Ubuntu 24.04 / Debian 12+ +# +# Run as root: +# sudo bash ./bonfire_turnkey_asdf_fixed.sh +# +# Notes: +# - Building Erlang/Elixir with asdf may take a long time on first run. +# - By default the script checks DNS A/AAAA for the provided domain; use --skip-dns-check to bypass. +# - This script attempts to be idempotent where reasonable. set -euo pipefail -# ============================ -# Bonfire Bare-Metal Installer -# Ubuntu 24.04 LTS (Noble) -# ============================ +### --------------------------- +### Configuration defaults +### --------------------------- +APP_USER="bonfire" +APP_HOME="/opt/${APP_USER}" +APP_DIR="${APP_HOME}/app" +APP_PORT="4000" +PG_DB="bonfire" +PG_USER_DEFAULT="bonfire_user" +PG_VER_PREFERRED="17" +ASDF_VERSION="v0.14.0" +KERL_CONFIGURE_OPTIONS="--without-javac --without-wx" +SKIP_DNS_CHECK=0 -echo "=== Bonfire Bare-Metal Installer (Ubuntu 24.04) ===" +### --------------------------- +### Helpers +### --------------------------- +log(){ printf "\n\033[1;36m==> %s\033[0m\n" "$*"; } +info(){ printf "\033[1;32m[info]\033[0m %s\n" "$*"; } +warn(){ printf "\033[1;33m[warn]\033[0m %s\n" "$*"; } +err(){ printf "\033[1;31m[error]\033[0m %s\n" "$*"; } +die(){ err "$*"; exit 1; } -# ---------- prompts ---------- -read -rp "Enter your domain name (FQDN, e.g., example.com): " DOMAIN -while [[ -z "${DOMAIN}" ]]; do - echo "Domain cannot be empty." - read -rp "Enter your domain name: " DOMAIN +require_root(){ + if [[ $EUID -ne 0 ]]; then + die "This script must be run as root (sudo)." + fi +} +require_root + +### --------------------------- +### CLI args +### --------------------------- +while [[ $# -gt 0 ]]; do + case "$1" in + --skip-dns-check) SKIP_DNS_CHECK=1; shift;; + *) die "Unknown argument: $1";; + esac done -read -rp "Contact email for Let's Encrypt (e.g., admin@${DOMAIN}): " EMAIL -while [[ -z "${EMAIL}" ]]; do - echo "Email cannot be empty." - read -rp "Contact email for Let's Encrypt: " EMAIL -done +### --------------------------- +### Interactive prompts +### --------------------------- +read -rp "Enter your domain (FQDN, e.g. bonfire.example.com): " DOMAIN +while [[ -z "$DOMAIN" ]]; do read -rp "Domain cannot be empty. Enter domain: " DOMAIN; done + +read -rp "Contact email for Let's Encrypt (admin@${DOMAIN}): " EMAIL +while [[ -z "$EMAIL" ]]; do read -rp "Email cannot be empty. Enter contact email: " EMAIL; done read -rp "Bonfire flavour [social|ember|community|cooperation] (default: social): " FLAVOUR FLAVOUR=${FLAVOUR:-social} -read -rp "PostgreSQL username (default: bonfire_user): " DB_USER -DB_USER=${DB_USER:-bonfire_user} -DB_NAME="bonfire" +read -rp "PostgreSQL role to create (default: ${PG_USER_DEFAULT}): " PG_USER +PG_USER=${PG_USER:-$PG_USER_DEFAULT} +PG_PASS="$(openssl rand -base64 24)" -# we'll generate a random DB password -DB_PASSWORD="$(openssl rand -base64 24)" +log "Inputs summary:" +info "Domain: ${DOMAIN}" +info "Email: ${EMAIL}" +info "Flavour: ${FLAVOUR}" +info "Postgres role: ${PG_USER} (password auto-generated)" -# ---------- FQDN validation ---------- +### --------------------------- +### Validate FQDN (basic) +### --------------------------- FQDN_REGEX='^([a-zA-Z0-9](-*[a-zA-Z0-9])*)(\.([a-zA-Z0-9](-*[a-zA-Z0-9])*))*\.[a-zA-Z]{2,}$' -if [[ ! "${DOMAIN}" =~ ${FQDN_REGEX} ]]; then - echo "❌ Invalid FQDN: ${DOMAIN}" - exit 1 +if [[ ! "$DOMAIN" =~ $FQDN_REGEX ]]; then + die "Invalid FQDN: $DOMAIN" fi -# ---------- DNS checks (A/AAAA must match this server) ---------- -echo "=== Checking DNS for ${DOMAIN} ===" -# public IPv4 -SERVER_V4="$(curl -4 -fsS https://api.ipify.org || true)" -# public IPv6 (may be empty if host lacks IPv6) -SERVER_V6="$(curl -6 -fsS https://api64.ipify.org || true)" +### --------------------------- +### System update & ensure curl/gnupg early +### --------------------------- +log "APT update & ensure curl/gnupg are present (required early for repos)" +export DEBIAN_FRONTEND=noninteractive +apt-get update -y +apt-get upgrade -y -# need dig -if ! command -v dig >/dev/null 2>&1; then - echo "Installing dnsutils for DNS checks..." - sudo apt update && sudo apt install -y dnsutils -fi +# Ensure curl / gnupg are present early for repo keys +apt-get install -y --no-install-recommends curl wget gnupg gpg-agent dirmngr lsb-release apt-transport-https ca-certificates || true -DOMAIN_A="$(dig +short A "${DOMAIN}" | head -n1 || true)" -DOMAIN_AAAA="$(dig +short AAAA "${DOMAIN}" | head -n1 || true)" +### --------------------------- +### Install build deps (may include some heavy packages needed for asdf builds) +### --------------------------- +log "Installing build dependencies (Erlang/Elixir builds need several packages)" +apt-get install -y --no-install-recommends \ + build-essential automake autoconf m4 libncurses5-dev libncursesw5-dev \ + libssl-dev libreadline-dev zlib1g-dev pkg-config unzip \ + libxml2-utils xsltproc fop libxslt1.1 libxslt1-dev \ + libwxgtk-webview3.2-dev libglu1-mesa-dev libgl1-mesa-dev \ + libpng-dev libssh-dev unixodbc-dev default-jdk rsync jq dnsutils git || true -echo "Server IPv4: ${SERVER_V4:-}" -echo "Server IPv6: ${SERVER_V6:-}" -echo "Domain A: ${DOMAIN_A:-}" -echo "Domain AAAA: ${DOMAIN_AAAA:-}" - -MISMATCH=true -if [[ -n "${SERVER_V4}" && -n "${DOMAIN_A}" && "${SERVER_V4}" == "${DOMAIN_A}" ]]; then - MISMATCH=false -fi -if [[ -n "${SERVER_V6}" && -n "${DOMAIN_AAAA}" && "${SERVER_V6,,}" == "${DOMAIN_AAAA,,}" ]]; then - MISMATCH=false -fi - -if [[ "${MISMATCH}" == "true" ]]; then - echo "❌ DNS for ${DOMAIN} does not resolve to this server." - echo " Server IPv4: ${SERVER_V4:-}, Domain A: ${DOMAIN_A:-}" - echo " Server IPv6: ${SERVER_V6:-}, Domain AAAA: ${DOMAIN_AAAA:-}" - echo "➡️ Fix your DNS A/AAAA records, wait for propagation, then re-run." - exit 1 -fi - -echo "✅ DNS looks good." - -# ---------- system prep ---------- -echo "=== Updating system packages ===" -sudo apt update && sudo apt -y upgrade - -echo "=== Installing base tools ===" -sudo apt install -y \ - curl git build-essential ca-certificates jq \ - libssl-dev libffi-dev libncurses5-dev \ - libwxgtk3.0-gtk3-dev libgl1-mesa-dev libglu1-mesa-dev \ - libpng-dev libssh-dev unixodbc-dev pkg-config - -echo "=== Installing 'just' command runner ===" -sudo apt install -y just - -echo "=== Installing Node.js 20 + Yarn ===" -curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - -sudo apt install -y nodejs -npm install -g yarn - -# ---------- PostgreSQL 17 + PostGIS ---------- -echo "=== Installing PostgreSQL 17 + PostGIS ===" -echo "deb [signed-by=/usr/share/keyrings/pgdg.gpg] http://apt.postgresql.org/pub/repos/apt/ noble-pgdg main" | sudo tee /etc/apt/sources.list.d/pgdg.list >/dev/null -curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo gpg --dearmor -o /usr/share/keyrings/pgdg.gpg -sudo apt update -sudo apt install -y postgresql-17 postgresql-17-postgis-3 - -echo "=== Creating PostgreSQL role & database ===" -sudo -u postgres psql -tAc "DO \$\$ BEGIN - IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = '${DB_USER}') THEN - CREATE ROLE ${DB_USER} LOGIN PASSWORD '${DB_PASSWORD}'; - END IF; -END \$\$;" -sudo -u postgres psql -tAc "SELECT 1 FROM pg_database WHERE datname='${DB_NAME}'" | grep -q 1 || sudo -u postgres createdb -O "${DB_USER}" "${DB_NAME}" -sudo -u postgres psql -d "${DB_NAME}" -c "CREATE EXTENSION IF NOT EXISTS postgis;" - -# ---------- asdf + mise (match .tool-versions) ---------- -echo "=== Installing asdf (plugins: erlang, elixir) ===" -if [[ ! -d "$HOME/.asdf" ]]; then - git clone https://github.com/asdf-vm/asdf.git "$HOME/.asdf" --branch v0.14.0 -fi -grep -q 'asdf.sh' "$HOME/.bashrc" || echo '. "$HOME/.asdf/asdf.sh"' >> "$HOME/.bashrc" -# shellcheck source=/dev/null -source "$HOME/.asdf/asdf.sh" || true -asdf plugin-add erlang || true -asdf plugin-add elixir || true - -echo "=== Installing mise ===" -curl -fsSL https://mise.jdx.dev/install.sh | sh -grep -q 'mise activate' "$HOME/.bashrc" || echo 'eval "$("$HOME/.local/bin/mise" activate bash)"' >> "$HOME/.bashrc" -eval "$("$HOME/.local/bin/mise" activate bash)" || true - -# ---------- Bonfire clone & tool install ---------- -echo "=== Cloning Bonfire repository ===" -cd "$HOME" -if [[ ! -d "bonfire" ]]; then - git clone --depth 1 https://github.com/bonfire-networks/bonfire-app.git bonfire -fi -cd bonfire - -echo "=== Installing Erlang/Elixir via mise (from .tool-versions) ===" -"$HOME/.local/bin/mise" install - -# ---------- Elixir bug-window check for Pathex ---------- -WITH_PATHEX=1 -if [[ -f ".tool-versions" ]]; then - ELIXIR_LINE="$(grep -E '^elixir[[:space:]]' .tool-versions || true)" - if [[ -n "${ELIXIR_LINE}" ]]; then - ELIXIR_VER="$(echo "${ELIXIR_LINE}" | awk '{print $2}')" - # strip possible 'ref:' etc; keep x.y.z - ELIXIR_VER_CLEAN="$(echo "${ELIXIR_VER}" | grep -oE '[0-9]+\.[0-9]+(\.[0-9]+)?' || true)" - if [[ -n "${ELIXIR_VER_CLEAN}" ]]; then - # compare versions: if >=1.17 and <1.17.3 => WITH_PATHEX=0 - vcmp () { printf '%s\n%s\n' "$1" "$2" | sort -V | head -n1; } - if [[ "$(vcmp "1.17.0" "${ELIXIR_VER_CLEAN}")" == "1.17.0" && "$(vcmp "${ELIXIR_VER_CLEAN}" "1.17.3")" == "${ELIXIR_VER_CLEAN}" && "${ELIXIR_VER_CLEAN}" != "1.17.3" ]]; then - WITH_PATHEX=0 - echo "⚠️ Elixir ${ELIXIR_VER_CLEAN} falls in bug window (>=1.17,<1.17.3). Disabling Pathex (WITH_PATHEX=0)." - fi - fi +# optional: try to install just if in apt repos +if ! command -v just >/dev/null 2>&1; then + if apt-cache show just >/dev/null 2>&1; then + apt-get install -y just || true fi fi -# ---------- environment ---------- -echo "=== Exporting environment ===" -grep -q 'FLAVOUR=' "$HOME/.bashrc" || { - echo "export FLAVOUR=${FLAVOUR}" >> "$HOME/.bashrc" - echo "export MIX_ENV=prod" >> "$HOME/.bashrc" - echo "export WITH_DOCKER=no" >> "$HOME/.bashrc" -} -export FLAVOUR="${FLAVOUR}" +### --------------------------- +### PostgreSQL repository key setup (PGDG) - idempotent +### --------------------------- +# ------------------------- +# PGDG key + repo (idempotent, modern Ubuntu/Debian) +# ------------------------- +PG_KEY_URL="https://www.postgresql.org/media/keys/ACCC4CF8.asc" +TRUSTED_KEY="/etc/apt/trusted.gpg.d/postgresql.gpg" +CODENAME="$(lsb_release -cs || echo noble)" +LIST_FILE="/etc/apt/sources.list.d/pgdg.list" + +# ensure curl & gpg are present +apt-get update -y +apt-get install -y --no-install-recommends curl gnupg lsb-release apt-transport-https ca-certificates || true + +# import/dearmor the key if missing +if [[ ! -f "${TRUSTED_KEY}" ]]; then + info "Importing PostgreSQL signing key to ${TRUSTED_KEY}..." + curl -fsSL "${PG_KEY_URL}" | gpg --dearmor -o "${TRUSTED_KEY}" \ + || { rm -f "${TRUSTED_KEY}"; die "Failed to fetch/dearmor PGDG key"; } + chmod 644 "${TRUSTED_KEY}" + chown root:root "${TRUSTED_KEY}" +else + info "PGDG key already present at ${TRUSTED_KEY}; skipping import." +fi + +# write the repo list (idempotent) +echo "deb http://apt.postgresql.org/pub/repos/apt ${CODENAME}-pgdg main" > "${LIST_FILE}" +chmod 644 "${LIST_FILE}" +info "PGDG APT entry written to ${LIST_FILE}" + +# update package lists +apt-get update -y || die "apt-get update failed after adding PGDG repo" +# ------------------------- + +### --------------------------- +### Install PostgreSQL + PostGIS +### --------------------------- +log "Installing PostgreSQL and PostGIS (attempting preferred ${PG_VER_PREFERRED})" +if apt-cache policy "postgresql-${PG_VER_PREFERRED}" | grep -q Candidate; then + apt-get install -y "postgresql-${PG_VER_PREFERRED}" "postgresql-${PG_VER_PREFERRED}-postgis-3" postgis || true +else + apt-get install -y postgresql postgis postgresql-contrib || true +fi +systemctl enable --now postgresql || true + +### --------------------------- +### Set locale to UTF-8 (avoid Elixir latin1 warning) +### --------------------------- +log "Ensuring UTF-8 locale" +apt-get install -y locales || true +if ! grep -q "en_US.UTF-8 UTF-8" /etc/locale.gen 2>/dev/null; then + echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen +fi +locale-gen en_US.UTF-8 >/dev/null || true +update-locale LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 +export LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 +export ELIXIR_ERL_OPTIONS="+fnu" + +### --------------------------- +### Create bonfire user and set ownership +### --------------------------- +log "Creating system user '${APP_USER}' and preparing ${APP_HOME}" +if id -u "${APP_USER}" >/dev/null 2>&1; then + info "User ${APP_USER} already exists" +else + adduser --system --group --home "${APP_HOME}" --shell /bin/bash "${APP_USER}" || true +fi +mkdir -p "${APP_HOME}" +chown -R "${APP_USER}:${APP_USER}" "${APP_HOME}" +chmod 750 "${APP_HOME}" + +# limited sudo for helper scripts (reload nginx, systemctl) +echo "${APP_USER} ALL=(ALL) NOPASSWD:/usr/sbin/nginx,/bin/systemctl" > "/etc/sudoers.d/${APP_USER}" +chmod 0440 "/etc/sudoers.d/${APP_USER}" + +### --------------------------- +### Install ASDF for bonfire user +### --------------------------- +log "Installing asdf for ${APP_USER} if missing" +if [[ ! -d "${APP_HOME}/.asdf" ]]; then + sudo -u "${APP_USER}" bash -lc "git clone https://github.com/asdf-vm/asdf.git ${APP_HOME}/.asdf --branch ${ASDF_VERSION}" +fi + +# ensure .bashrc loads asdf +if ! sudo -u "${APP_USER}" bash -lc "grep -q 'asdf.sh' ${APP_HOME}/.bashrc" >/dev/null 2>&1; then + cat >> "${APP_HOME}/.bashrc" <<'ASDFRC' +# ASDF +. "$HOME/.asdf/asdf.sh" +. "$HOME/.asdf/completions/asdf.bash" +ASDFRC + chown "${APP_USER}:${APP_USER}" "${APP_HOME}/.bashrc" +fi + +# create local bin dir (nodejs plugin helper uses it) +sudo -u "${APP_USER}" bash -lc "mkdir -p ${APP_HOME}/.local/bin && chmod 755 ${APP_HOME}/.local/bin" + +### --------------------------- +### Clone Bonfire repository +### --------------------------- +log "Cloning Bonfire repository into ${APP_DIR}" +if [[ ! -d "${APP_DIR}" ]]; then + sudo -u "${APP_USER}" git clone --depth 1 https://github.com/bonfire-networks/bonfire-app.git "${APP_DIR}" +else + info "Repository already present; updating..." + sudo -u "${APP_USER}" bash -lc "cd '${APP_DIR}' && git fetch --depth 1 origin && git reset --hard origin/HEAD" +fi + +### --------------------------- +### Install ASDF plugins & runtimes as bonfire user +### --------------------------- +log "Installing ASDF plugins (erlang, elixir, nodejs) and runtimes (may be slow)" +sudo -u "${APP_USER}" bash -lc " +set -euo pipefail +export HOME='${APP_HOME}' +. \"\$HOME/.asdf/asdf.sh\" + +asdf plugin add erlang || true +asdf plugin add elixir || true +asdf plugin add nodejs || true + +# nodejs key import helper (if present) +if [[ -f \"\$HOME/.asdf/plugins/nodejs/bin/import-release-team-keyring\" ]]; then + bash \"\$HOME/.asdf/plugins/nodejs/bin/import-release-team-keyring\" || true +fi + +cd '${APP_DIR}' +if [[ -f .tool-versions ]]; then + export KERL_CONFIGURE_OPTIONS=\"${KERL_CONFIGURE_OPTIONS}\" + asdf install || true +else + export KERL_CONFIGURE_OPTIONS=\"${KERL_CONFIGURE_OPTIONS}\" + EL_VER=\$(asdf list-all elixir | tail -n1 || true) + ERL_VER=\$(asdf list-all erlang | tail -n1 || true) + NODE_VER=\$(asdf list-all nodejs | tail -n1 || true) + if [[ -n \"\$ERL_VER\" ]]; then asdf install erlang \"\$ERL_VER\" || true; asdf global erlang \"\$ERL_VER\"; fi + if [[ -n \"\$EL_VER\" ]]; then asdf install elixir \"\$EL_VER\" || true; asdf global elixir \"\$EL_VER\"; fi + if [[ -n \"\$NODE_VER\" ]]; then asdf install nodejs \"\$NODE_VER\" || true; asdf global nodejs \"\$NODE_VER\"; fi +fi + +mix local.hex --force || true +mix local.rebar --force || true + +echo '[info] ASDF runtimes installed (or attempted).' +" + +### --------------------------- +### Verify mix is available +### --------------------------- +if ! sudo -u "${APP_USER}" bash -lc ". ${APP_HOME}/.asdf/asdf.sh && command -v mix >/dev/null 2>&1"; then + die "mix not available for ${APP_USER}. Check ASDF install logs above." +fi + +### --------------------------- +### DNS validation (unless skipped) +### --------------------------- +if [[ "${SKIP_DNS_CHECK}" -eq 0 ]]; then + log "Checking DNS A/AAAA records for ${DOMAIN}" + SERVER_V4="$(curl -4 -fsS https://api.ipify.org || true)" + SERVER_V6="$(curl -6 -fsS https://api64.ipify.org || true)" + DOMAIN_A="$(dig +short A "${DOMAIN}" | head -n1 || true)" + DOMAIN_AAAA="$(dig +short AAAA "${DOMAIN}" | head -n1 || true)" + echo " Server IPv4: ${SERVER_V4:-} Domain A: ${DOMAIN_A:-}" + echo " Server IPv6: ${SERVER_V6:-} Domain AAAA: ${DOMAIN_AAAA:-}" + DNS_OK=false + if [[ -n "${SERVER_V4}" && "${SERVER_V4}" == "${DOMAIN_A}" ]]; then DNS_OK=true; fi + if [[ -n "${SERVER_V6}" && -n "${DOMAIN_AAAA}" && "${SERVER_V6,,}" == "${DOMAIN_AAAA,,}" ]]; then DNS_OK=true; fi + if [[ "${DNS_OK}" != "true" ]]; then + die "DNS for ${DOMAIN} does not point to this server's public IP. Fix DNS or re-run with --skip-dns-check." + fi +fi + +### --------------------------- +### PostgreSQL role / DB / extensions +### --------------------------- +log "Configuring PostgreSQL role/database and enabling PostGIS & citext" +sudo -u postgres psql -v ON_ERROR_STOP=1 </dev/null 2>&1; then + just config || true +fi + +SECRET_KEY_BASE=\$(openssl rand -hex 64) +SIGNING_SALT=\$(openssl rand -hex 16) +ENCRYPTION_SALT=\$(openssl rand -hex 16) +MEILI_MASTER_KEY=\$(openssl rand -hex 32) + +cat > .env < .env </dev/null 2>&1; then + ELV=\$(elixir -v | sed -n 's/.*Elixir \\([0-9]\\+\\.[0-9]\\+\\.[0-9]\\+\\).*/\\1/p' || true) + if printf '%s\n' 1.17.0 1.17.1 1.17.2 | grep -qx \"\$ELV\"; then + sed -i 's/^WITH_PATHEX=.*/WITH_PATHEX=0/' .env || true + echo '[warn] Elixir in bug-window; disabled WITH_PATHEX in .env' + fi +fi -# ---------- Nginx + Let's Encrypt ---------- -echo "=== Installing Nginx + Certbot ===" -sudo apt install -y nginx certbot python3-certbot-nginx +mix local.hex --force || true +mix local.rebar --force || true +mix deps.get --only prod || mix deps.get || true +# Assets +if [[ -d assets ]]; then + cd assets + if command -v yarn >/dev/null 2>&1; then + yarn install --frozen-lockfile || yarn install || true + NODE_ENV=production yarn build || true + else + npm ci || npm install || true + NODE_ENV=production npm run build || true + fi + cd - +fi + +if command -v just >/dev/null 2>&1; then + just setup-prod || true + just rel-build || true +else + MIX_ENV=prod mix compile + MIX_ENV=prod mix assets.deploy || true + MIX_ENV=prod mix ecto.setup || true + MIX_ENV=prod mix release +fi + +echo '[info] Bonfire build attempted (see output above).' +" + +# verify release binary +if [[ ! -x "${APP_DIR}/_build/prod/rel/bonfire/bin/bonfire" ]]; then + die "Release binary missing at ${APP_DIR}/_build/prod/rel/bonfire/bin/bonfire — check the build output above." +fi + +### --------------------------- +### Nginx config + certbot +### --------------------------- +log "Writing nginx site config and reloading" SITE_AVAIL="/etc/nginx/sites-available/bonfire" SITE_LINK="/etc/nginx/sites-enabled/bonfire" -APP_UPSTREAM="http://127.0.0.1:4000" +APP_UPSTREAM="http://127.0.0.1:${APP_PORT}" -echo "=== Writing Nginx site config ===" -sudo tee "${SITE_AVAIL}" >/dev/null < "${SITE_AVAIL}" </dev/null < "${SERVICE_PATH}" < "${HOME}/bonfire/reload-nginx.sh" <<'EOS' +### --------------------------- +### Helper scripts (bonfire user) +### --------------------------- +log "Installing helper scripts into ${APP_DIR}" +sudo -u "${APP_USER}" bash -lc "cat > '${APP_DIR}/reload-nginx.sh' <<'EOS' #!/usr/bin/env bash set -e sudo nginx -t sudo systemctl reload nginx -echo "Nginx reloaded." +echo 'Nginx reloaded.' EOS -chmod +x "${HOME}/bonfire/reload-nginx.sh" +chmod +x '${APP_DIR}/reload-nginx.sh' || true" -cat > "${HOME}/bonfire/bonfire-deploy.sh" <<'EOS' +sudo -u "${APP_USER}" bash -lc "cat > '${APP_DIR}/bonfire-deploy.sh' <<'EOS' #!/usr/bin/env bash set -euo pipefail -cd "$(dirname "$0")" - -echo "Pulling latest code..." -git pull --ff-only - -echo "Ensuring tool versions are installed (mise)..." -~/.local/bin/mise install - -echo "Updating prod config..." -just setup-prod - -echo "Building release..." -just rel-build - -echo "Restarting Bonfire..." -sudo systemctl restart bonfire - -echo "Reloading Nginx..." -./reload-nginx.sh - -echo "Deploy complete." +cd \"\$(dirname \"\$0\")\" +. \"\$HOME/.asdf/asdf.sh\" 2>/dev/null || true +echo 'Pulling latest code...' +git pull --ff-only || true +echo 'Running production setup and rebuild...' +just setup-prod || true +just rel-build || true +echo 'Restarting service...' +sudo systemctl restart bonfire || true +echo 'Reloading nginx...' +./reload-nginx.sh || true +echo 'Deploy complete.' EOS -chmod +x "${HOME}/bonfire/bonfire-deploy.sh" +chmod +x '${APP_DIR}/bonfire-deploy.sh' || true" -# ---------- done ---------- -echo -echo "=== Bonfire setup complete! 🎉 ===" -echo "Site: https://${DOMAIN}" -echo -echo "Systemd service controls:" -echo " sudo systemctl status bonfire" -echo " sudo systemctl restart bonfire" -echo " sudo systemctl stop bonfire" -echo -echo "Database credentials:" -echo " DB_USER: ${DB_USER}" -echo " DB_PASSWORD: ${DB_PASSWORD}" -echo " DB_NAME: ${DB_NAME}" -echo -echo "Deploy updates with:" -echo " ${HOME}/bonfire/bonfire-deploy.sh" -echo "Reload Nginx with:" -echo " ${HOME}/bonfire/reload-nginx.sh" -echo -echo "Certbot auto-renewal is enabled. To force renew:" -echo " sudo certbot renew && sudo systemctl reload nginx" +### --------------------------- +### Final summary +### --------------------------- +log "Done (or attempted). Summary:" +cat <