Add install.sh and fix service unit placeholder
- scripts/install.sh: one-command hardened deploy (creates the water-monitor system user, deploys to /opt, builds a uv-managed venv, installs and enables the systemd unit). Idempotent; excludes .env/*.db/stations.json from sync so runtime state is preserved. - Fix placeholder Documentation= URL in water-monitor.service. - README: document the script as the primary systemd install path, with manual steps kept as a fallback.
This commit is contained in:
@@ -244,7 +244,7 @@ water_level{station_code="P.1"}
|
||||
```sql
|
||||
-- Latest readings from all stations
|
||||
SELECT s.station_code, s.english_name, m.water_level, m.discharge
|
||||
FROM stations s
|
||||
FROM stations s
|
||||
JOIN water_measurements m ON s.id = m.station_id
|
||||
WHERE m.timestamp = (SELECT MAX(timestamp) FROM water_measurements WHERE station_id = s.id);
|
||||
```
|
||||
@@ -267,14 +267,32 @@ docker run -d \
|
||||
|
||||
### Systemd Service (Linux)
|
||||
|
||||
```bash
|
||||
# Copy service file
|
||||
sudo cp scripts/water-monitor.service /etc/systemd/system/
|
||||
The install script sets everything up: a dedicated `water-monitor` system user,
|
||||
a deploy to `/opt/thailand-water-monitor`, a uv-managed virtualenv, and the
|
||||
enabled systemd unit.
|
||||
|
||||
# Enable and start
|
||||
```bash
|
||||
# From a checkout of the repo, as root:
|
||||
sudo bash scripts/install.sh
|
||||
|
||||
# Then start and check:
|
||||
sudo systemctl start water-monitor.service
|
||||
systemctl status water-monitor.service
|
||||
```
|
||||
|
||||
Fill in `/opt/thailand-water-monitor/.env` (Matrix token/room, DB settings)
|
||||
before starting if the script reports it is missing.
|
||||
|
||||
<details>
|
||||
<summary>Manual setup (if you prefer not to use the script)</summary>
|
||||
|
||||
```bash
|
||||
sudo useradd --system --no-create-home --shell /usr/sbin/nologin water-monitor
|
||||
sudo cp scripts/water-monitor.service /etc/systemd/system/
|
||||
sudo systemctl enable water-monitor.service
|
||||
sudo systemctl start water-monitor.service
|
||||
```
|
||||
</details>
|
||||
|
||||
### Migration for Existing Systems
|
||||
|
||||
@@ -475,7 +493,7 @@ See [docs/PROJECT_STRUCTURE.md](docs/PROJECT_STRUCTURE.md) for detailed architec
|
||||
The project includes comprehensive Gitea Actions workflows:
|
||||
|
||||
- **🧪 CI/CD Pipeline** - Automated testing, building, and deployment
|
||||
- **🔒 Security Scanning** - Daily vulnerability and dependency checks
|
||||
- **🔒 Security Scanning** - Daily vulnerability and dependency checks
|
||||
- **📚 Documentation** - Automated API docs and validation
|
||||
- **🚀 Release Management** - Automated releases with multi-arch Docker builds
|
||||
|
||||
|
||||
Executable
+96
@@ -0,0 +1,96 @@
|
||||
#!/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"
|
||||
@@ -1,6 +1,6 @@
|
||||
[Unit]
|
||||
Description=Thailand Water Level Monitor
|
||||
Documentation=https://github.com/your-username/thailand-water-monitor
|
||||
Documentation=https://git.b4l.co.th/B4L/Northern-Thailand-Ping-River-Monitor
|
||||
After=network.target
|
||||
Wants=network-online.target
|
||||
|
||||
|
||||
Reference in New Issue
Block a user