104 lines
4.5 KiB
YAML
104 lines
4.5 KiB
YAML
# Register the Pi runner with the label solar-relay:host.
|
|
# Install the systemd unit and the restricted sudoers rule described in README.md once.
|
|
name: Deploy Solar Relay
|
|
|
|
on:
|
|
push:
|
|
branches:
|
|
- main
|
|
workflow_dispatch:
|
|
|
|
jobs:
|
|
deploy:
|
|
runs-on: solar-relay
|
|
|
|
env:
|
|
DEPLOY_PATH: /var/www/html/solar-relay
|
|
SERVICE: solar-relay.service
|
|
HEALTH_URL: https://rpioffice.nest.lutran.ch/solar-relay/healthz
|
|
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
with:
|
|
persist-credentials: false
|
|
|
|
- name: Check runner and deployment setup
|
|
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
|
|
command -v systemctl
|
|
command -v flock
|
|
command -v curl
|
|
python3 -c 'import sys, tomllib; assert sys.version_info >= (3, 11)'
|
|
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
|
|
checkout = Path.cwd().resolve()
|
|
deployment = Path(os.environ['DEPLOY_PATH']).resolve()
|
|
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/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: |
|
|
python3 -m venv .venv-ci
|
|
.venv-ci/bin/python -m pip install --only-binary=pyarrow -r requirements.txt
|
|
|
|
- name: Run tests
|
|
run: .venv-ci/bin/python -m unittest discover -s tests -v
|
|
|
|
- name: Deploy and restart
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
# Also serializes deployments on Gitea versions that lack workflow concurrency.
|
|
exec 9>"$DEPLOY_PATH/.deploy.lock"
|
|
flock -w 120 9
|
|
rsync -azc --no-times --delete \
|
|
--exclude '/.git/' \
|
|
--exclude '/.gitea/' \
|
|
--exclude '/.venv/' \
|
|
--exclude '/.venv-ci/' \
|
|
--exclude '/.deploy.lock' \
|
|
--exclude '/config.toml' \
|
|
--exclude '/.env' \
|
|
--exclude '/data/' \
|
|
--exclude '__pycache__/' \
|
|
--exclude '*.parquet' \
|
|
./ "$DEPLOY_PATH/"
|
|
if [ ! -x "$DEPLOY_PATH/.venv/bin/python" ]; then
|
|
python3 -m venv "$DEPLOY_PATH/.venv"
|
|
fi
|
|
"$DEPLOY_PATH/.venv/bin/python" -c 'import sys; assert sys.version_info >= (3, 11)'
|
|
"$DEPLOY_PATH/.venv/bin/python" -m pip install --only-binary=pyarrow -r "$DEPLOY_PATH/requirements.txt"
|
|
sudo -n "$(command -v systemctl)" restart "$SERVICE"
|
|
# Catch an invalid config or missing token that triggers an immediate restart loop.
|
|
sleep 10
|
|
if ! systemctl is-active --quiet "$SERVICE"; then
|
|
systemctl status "$SERVICE" --no-pager --lines 30 || true
|
|
exit 1
|
|
fi
|
|
if [ "$(systemctl show --property=NRestarts --value "$SERVICE")" != "0" ]; then
|
|
echo 'The collector restarted unexpectedly after deployment.'
|
|
systemctl status "$SERVICE" --no-pager --lines 30 || true
|
|
exit 1
|
|
fi
|
|
systemctl status "$SERVICE" --no-pager --lines 10
|
|
curl --fail --silent --show-error --connect-timeout 3 --max-time 5 \
|
|
--retry 4 --retry-delay 2 "$HEALTH_URL"
|