From 7cae29f7998789ada9626bddf8d68c923e9360f7 Mon Sep 17 00:00:00 2001 From: Franzz Date: Sun, 1 Nov 2026 23:18:55 +0100 Subject: [PATCH] Fix config file folder --- .gitea/workflows/deploy.yml | 19 +++++++++-------- README.md | 41 +++++++++++++++++++++++++------------ deploy/solar-relay.service | 2 +- 3 files changed, 40 insertions(+), 22 deletions(-) diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index 483c057..47c7917 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -27,6 +27,11 @@ jobs: shell: bash run: | set -euo pipefail + fail() { + printf '::error::%s\n' "$*" >&2 + exit 1 + } + printf 'Runner account: %s\nDeployment directory: %s\n' "$(id -un)" "$DEPLOY_PATH" command -v python3 command -v rsync command -v sudo @@ -34,8 +39,9 @@ jobs: command -v flock command -v curl python3 -c 'import sys, tomllib; assert sys.version_info >= (3, 11)' - test -d "$DEPLOY_PATH" - test -w "$DEPLOY_PATH" + test -d "$DEPLOY_PATH" || fail "Deployment directory does not exist: $DEPLOY_PATH" + test -w "$DEPLOY_PATH" || fail "Deployment directory is not writable by $(id -un): $DEPLOY_PATH" + test -r "$DEPLOY_PATH/config.toml" || fail "Config is missing or unreadable by $(id -un): $DEPLOY_PATH/config.toml. Deployment preserves this file; configure it on the Pi." python3 - <<'PY' import os from pathlib import Path @@ -44,12 +50,9 @@ jobs: if checkout == deployment or checkout in deployment.parents or deployment in checkout.parents: raise SystemExit('The runner checkout and deployment directory must not overlap') PY - test -r /etc/solar-relay/config.toml - cmp deploy/solar-relay.service /etc/systemd/system/solar-relay.service || { - echo 'Install the updated deploy/solar-relay.service and run systemctl daemon-reload first.' - exit 1 - } - sudo -n -l "$(command -v systemctl)" restart "$SERVICE" >/dev/null + test -r /etc/systemd/system/solar-relay.service || fail "Collector service unit is missing. Install deploy/solar-relay.service as /etc/systemd/system/solar-relay.service and run systemctl daemon-reload once." + cmp deploy/solar-relay.service /etc/systemd/system/solar-relay.service || fail "Installed collector unit differs from deploy/solar-relay.service. Install the updated unit and run systemctl daemon-reload." + sudo -n -l "$(command -v systemctl)" restart "$SERVICE" >/dev/null || fail "$(id -un) needs passwordless sudo for $(command -v systemctl) restart $SERVICE." - name: Create isolated test environment run: | diff --git a/README.md b/README.md index 716eb61..55b38d1 100644 --- a/README.md +++ b/README.md @@ -179,15 +179,23 @@ recover individual five-minute values. Fully local operation leaves Wi-Fi gaps v ## Raspberry Pi service Deploy the checkout to `/var/www/html/solar-relay` and create/install its venv as above. -Place configuration outside the web directory: +The service reads **`/var/www/html/solar-relay/config.toml`**. Keep your existing local +file there; deployment excludes it from both replacement and deletion. If this is a +fresh installation, create it from the example only when it does not exist: ```bash -sudo install -d -m 0755 /etc/solar-relay -sudo install -m 0644 config.example.toml /etc/solar-relay/config.toml -sudoedit /etc/solar-relay/config.toml +cd /var/www/html/solar-relay +test -e config.toml || cp config.example.toml config.toml +chmod 0644 config.toml ``` -Use these collector settings, retaining the example's other sections: +The dynamic service user needs read access to this configuration. It contains setting +values and environment variable names; keep credentials in the root-readable environment +file below. Ensure the proxy serves the status endpoint rather than repository files. + +For systemd, set the archive to its writable state directory in this local config, +retaining your other settings. The relative development setting `data/archive` points +inside the checkout, which the hardened service cannot write: ```toml [collector] @@ -202,17 +210,21 @@ Set `[upload].url` to the main website's `https://your-solar-host/index.php?a=re Put `SOLAR_RELAY_TOKEN=your-shared-token` in `/etc/solar-relay/solar-relay.env` with permissions 0600, readable by root. Configure the same token and source ID on the main website as described in its README. Tokens and API keys stay out of TOML/Git. +Create `/etc/solar-relay` with `sudo install -d -m 0755 /etc/solar-relay` when adding +that optional environment file. ```bash sudo cp deploy/solar-relay.service /etc/systemd/system/ sudo systemctl daemon-reload -sudo systemctl enable --now solar-relay -sudo journalctl -u solar-relay -f +sudo systemctl enable solar-relay ``` systemd provides `/var/lib/solar-relay` to a dynamic, unprivileged service user. -Recording/uploading remains separate from serving status. Running tests or `sample` -does not install/start the service. Initial systemd/configuration installation is manual. +Recording/uploading remains separate from serving status. The first Gitea deployment +installs the code/venv and starts the service through restart. After a manual installation, +use `systemctl start solar-relay` when the code and venv are ready. Running +tests or `sample` does not install/start the service. Initial unit installation is manual. +Inspect service logs with `sudo journalctl -u solar-relay -f` after startup. ## HTTPS reverse proxy and status page @@ -256,12 +268,13 @@ with **`solar-relay:host`**; its checkout must be separate from production. The runner needs Python 3.11+, `python3-venv`/pip, a supported 64-bit PyArrow platform, `rsync`, `flock`, `curl`, `sudo`, systemd and Node.js 20+ for `actions/checkout@v4`. -Give its account write access to the deployment directory. Install the config and unit -before the first deployment, then add a restricted sudoers rule using the actual runner +The configured runner account is `www-data`. Give it write access to the deployment +directory and keep the local config there. Install the unit before the first deployment, +then add a restricted sudoers rule using the actual runner account and the path from `command -v systemctl`: ```sudoers -gitea-runner ALL=(root) NOPASSWD: /usr/bin/systemctl restart solar-relay.service +www-data ALL=(root) NOPASSWD: /usr/bin/systemctl restart solar-relay.service ``` The workflow installs dependencies and runs tests in `.venv-ci`, checks the installed @@ -269,7 +282,9 @@ unit, serializes deployment with `flock`, syncs source, installs runtime depende inside `.venv`, restarts and checks the service, then probes the public HTTPS health endpoint with certificate verification. Runtime venv/configuration, `data/` and Parquet files are preserved. Recommended state under `/var/lib` and -configuration under `/etc` are entirely outside the sync and web directory. +the credential environment file under `/etc` are outside the sync. The local +`config.toml` is protected by the explicit rsync exclusion even when `--delete` is used. +Deployment never copies `config.example.toml` over your configuration. If the deployment directory is initially empty, enable the unit without `--now`; the first deployment creates the venv and starts it. Set up the proxy before deployment. diff --git a/deploy/solar-relay.service b/deploy/solar-relay.service index e17729d..b55d6a1 100644 --- a/deploy/solar-relay.service +++ b/deploy/solar-relay.service @@ -7,7 +7,7 @@ Wants=network-online.target Type=simple DynamicUser=yes WorkingDirectory=/var/www/html/solar-relay -ExecStart=/var/www/html/solar-relay/.venv/bin/python -m solar_relay --config /etc/solar-relay/config.toml run +ExecStart=/var/www/html/solar-relay/.venv/bin/python -m solar_relay --config /var/www/html/solar-relay/config.toml run EnvironmentFile=-/etc/solar-relay/solar-relay.env StateDirectory=solar-relay StateDirectoryMode=0700