#!/usr/bin/env bash # # Install the Thailand Water Level Monitor as a hardened systemd service. # # Creates a dedicated system user, deploys the code to /opt, builds a uv-managed # virtualenv, installs the systemd unit, and enables the service. Idempotent: # safe to re-run to update an existing install. # # Usage (as root, from a checkout of the repo): # sudo bash scripts/install.sh # # Override defaults via environment variables: # APP_DIR=/opt/thailand-water-monitor SERVICE_USER=water-monitor sudo -E bash scripts/install.sh # set -euo pipefail APP_DIR="${APP_DIR:-/opt/thailand-water-monitor}" SERVICE_USER="${SERVICE_USER:-water-monitor}" SERVICE_GROUP="${SERVICE_GROUP:-${SERVICE_USER}}" SERVICE_NAME="water-monitor.service" # Resolve the repo root (parent of this scripts/ directory). SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" log() { printf '\033[1;32m==>\033[0m %s\n' "$*"; } warn() { printf '\033[1;33m[warn]\033[0m %s\n' "$*"; } die() { printf '\033[1;31m[error]\033[0m %s\n' "$*" >&2; exit 1; } [ "$(id -u)" -eq 0 ] || die "This script must be run as root (use sudo)." # 1. Dedicated system user/group (no login, no home) -------------------------- if ! getent group "${SERVICE_GROUP}" >/dev/null; then log "Creating group ${SERVICE_GROUP}" groupadd --system "${SERVICE_GROUP}" fi if ! id "${SERVICE_USER}" >/dev/null 2>&1; then log "Creating system user ${SERVICE_USER}" useradd --system --no-create-home --shell /usr/sbin/nologin \ --gid "${SERVICE_GROUP}" "${SERVICE_USER}" fi # 2. Deploy code to APP_DIR ---------------------------------------------------- log "Deploying code to ${APP_DIR}" mkdir -p "${APP_DIR}" if command -v rsync >/dev/null 2>&1; then rsync -a --delete \ --exclude '.git' --exclude '.venv' --exclude 'venv' \ --exclude '__pycache__' --exclude '*.pyc' \ --exclude '*.db' --exclude '.env' --exclude 'stations.json' \ "${REPO_DIR}/" "${APP_DIR}/" else warn "rsync not found; falling back to cp (will not prune deleted files)" cp -r "${REPO_DIR}/." "${APP_DIR}/" fi # 3. Build the uv-managed virtualenv ------------------------------------------ if ! command -v uv >/dev/null 2>&1; then log "Installing uv" curl -LsSf https://astral.sh/uv/install.sh | env UV_INSTALL_DIR=/usr/local/bin sh fi UV="$(command -v uv)" log "Creating virtualenv at ${APP_DIR}/venv" cd "${APP_DIR}" # Named 'venv' (not uv's default .venv) to match the systemd unit's ExecStart. "${UV}" venv venv "${UV}" pip install --python venv/bin/python -r requirements.txt # 4. Environment file ---------------------------------------------------------- if [ ! -f "${APP_DIR}/.env" ]; then if [ -f "${REPO_DIR}/.env" ]; then log "Copying .env from checkout" cp "${REPO_DIR}/.env" "${APP_DIR}/.env" else warn "No .env found. Copy .env.example to ${APP_DIR}/.env and fill in" warn "MATRIX_ACCESS_TOKEN / MATRIX_ROOM_ID and DB settings before starting." fi fi # 5. Ownership (service user needs write access for logs / stations.json) ------ log "Setting ownership to ${SERVICE_USER}:${SERVICE_GROUP}" chown -R "${SERVICE_USER}:${SERVICE_GROUP}" "${APP_DIR}" # 6. Install and enable the systemd unit -------------------------------------- log "Installing systemd unit" install -m 0644 "${SCRIPT_DIR}/${SERVICE_NAME}" "/etc/systemd/system/${SERVICE_NAME}" systemctl daemon-reload systemctl enable "${SERVICE_NAME}" log "Done." echo echo "Next steps:" echo " sudo systemctl start ${SERVICE_NAME}" echo " systemctl status ${SERVICE_NAME}" echo " sudo journalctl -u ${SERVICE_NAME} -f"