120 lines
4.5 KiB
YAML
120 lines
4.5 KiB
YAML
# runs-on must match the label the runner was registered with -- `heos`
|
|
# here, the way Livetrail uses `livetrail`. A job asking for a label
|
|
# nobody offers sits in the queue rather than failing.
|
|
#
|
|
# The runner has to be in host mode on the machine that serves the panel:
|
|
# it writes into DEPLOY_PATH and restarts the service. It also needs node
|
|
# 20+ on its PATH, since actions/checkout is a JavaScript action and a
|
|
# host-mode runner has nothing else to run one with.
|
|
#
|
|
# DEPLOY_PATH does not have to be a git checkout -- an empty directory the
|
|
# runner can write to is enough. See "Deploying from Gitea" in the README
|
|
# for the service and the one sudoers line the restart needs.
|
|
|
|
name: Deploy HEOS panel
|
|
|
|
on:
|
|
push:
|
|
branches:
|
|
- main
|
|
workflow_dispatch:
|
|
|
|
jobs:
|
|
deploy:
|
|
runs-on: heos
|
|
|
|
env:
|
|
DEPLOY_PATH: /var/www/html/heos
|
|
SERVICE: heos-panel
|
|
PANEL_URL: http://127.0.0.1:5443/ # WEB_PORT in config.py
|
|
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Check runner tools
|
|
run: |
|
|
echo "running as $(id -un) on $(hostname)"
|
|
command -v python3
|
|
command -v rsync
|
|
command -v curl
|
|
|
|
- name: Check deploy path
|
|
run: |
|
|
test -d "$DEPLOY_PATH"
|
|
test -w "$DEPLOY_PATH"
|
|
|
|
# `sudo -n -l <cmd>` asks "may I run this?" without prompting, so the
|
|
# job stops here with the line to add rather than deploying and then
|
|
# falling over on the restart at the very end.
|
|
- name: Check the restart is allowed without a password
|
|
run: |
|
|
SYSTEMCTL="$(command -v systemctl)"
|
|
if sudo -n -l "$SYSTEMCTL" restart "$SERVICE" >/dev/null 2>&1; then
|
|
echo "$(id -un) may restart $SERVICE"
|
|
exit 0
|
|
fi
|
|
echo "$(id -un) cannot restart $SERVICE without a password. Once, here:"
|
|
echo
|
|
echo " echo '$(id -un) ALL=(ALL) NOPASSWD: $SYSTEMCTL restart $SERVICE' \\"
|
|
echo " | sudo tee /etc/sudoers.d/$SERVICE"
|
|
echo " sudo chmod 440 /etc/sudoers.d/$SERVICE"
|
|
echo
|
|
echo "The path matters: sudo matches what it resolves from PATH"
|
|
echo "against the sudoers line, without following symlinks."
|
|
exit 1
|
|
|
|
# A throwaway virtualenv in the workspace -- this is the npm ci of a
|
|
# Python project. The one under $DEPLOY_PATH/.venv is what the running
|
|
# panel imports from, and a test run has no business touching it.
|
|
- name: Install dependencies
|
|
run: |
|
|
python3 -m venv .venv-ci
|
|
.venv-ci/bin/pip install --quiet --upgrade pip
|
|
.venv-ci/bin/pip install --quiet -r requirements.txt
|
|
|
|
# Runs against the fake HEOS and AVR servers in tests/fakes.py, so it
|
|
# needs no speakers and touches nothing on the network. Nothing has
|
|
# been deployed yet at this point, so a failure here leaves the server
|
|
# exactly as it was.
|
|
- name: Run tests
|
|
run: .venv-ci/bin/python -m unittest discover -s tests -t . --verbose
|
|
|
|
# .venv and members.json are excluded, so the runtime and the stereo
|
|
# pair's learned membership survive --delete untouched.
|
|
- name: Deploy to production
|
|
run: |
|
|
rsync -azc --no-times --delete \
|
|
--exclude "/.git/" \
|
|
--exclude "/.gitea/" \
|
|
--exclude "/.venv/" \
|
|
--exclude "/.venv-ci/" \
|
|
--exclude "/members.json" \
|
|
--exclude "__pycache__/" \
|
|
./ "$DEPLOY_PATH/"
|
|
|
|
- name: Install runtime dependencies
|
|
run: |
|
|
test -d "$DEPLOY_PATH/.venv" || python3 -m venv "$DEPLOY_PATH/.venv"
|
|
"$DEPLOY_PATH/.venv/bin/pip" install --quiet -r "$DEPLOY_PATH/requirements.txt"
|
|
|
|
# The same absolute path the check above validated, so PATH order
|
|
# cannot leave sudo matching a different one (/bin vs /usr/bin).
|
|
- name: Restart
|
|
run: sudo -n "$(command -v systemctl)" restart "$SERVICE"
|
|
|
|
- name: Wait for the panel to answer
|
|
run: |
|
|
# The home page renders from config alone, so this proves the app
|
|
# came back up without waiting on the speakers to reply.
|
|
for attempt in $(seq 1 20); do
|
|
if curl -fsS -o /dev/null "$PANEL_URL"; then
|
|
echo "panel is up after ${attempt}s"
|
|
exit 0
|
|
fi
|
|
sleep 1
|
|
done
|
|
echo "panel did not come back -- last of its log:"
|
|
systemctl status "$SERVICE" --no-pager --lines 30 || true
|
|
exit 1
|