Files
heos/.gitea/workflows/deploy.yml
T
franzzandClaude Opus 5 4cff820070
Deploy HEOS panel / deploy (push) Failing after 0s
Check out with git rather than actions/checkout
actions/checkout is a JavaScript action, so a host-mode runner needs node
on its PATH to run it and fails with "Cannot find: node in PATH" without
one. Every step is plain shell now, which needs nothing of the runner
beyond git, python3, rsync and curl.

The clone is shallow and takes its URL from the live checkout's remote, so
there is no URL or token in the workflow. It works because the runner
already has to run as the user that owns that checkout.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 23:35:29 +02:00

118 lines
4.2 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. See
# deploy/heos-panel.service for the unit and the one sudoers line the
# restart needs.
#
# Every step here is plain shell, on purpose. actions/checkout is a
# JavaScript action, and a host-mode runner can only run those if node is
# on its PATH -- swapping the clone below back to `uses: actions/checkout`
# brings back "Cannot find: node in PATH" on a runner without it.
#
# DEPLOY_PATH is also where you edit: an rsync --delete lands on top of
# whatever is sitting there uncommitted, so commit before you push.
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:5005/ # WEB_PORT in config.py
SRC: src # the clone, inside the workspace
steps:
- name: Check runner tools
run: |
command -v git
command -v python3
command -v rsync
command -v curl
- name: Check deploy path
run: |
test -d "$DEPLOY_PATH"
test -w "$DEPLOY_PATH"
- name: Check the restart is allowed without a password
run: sudo -n systemctl is-active "$SERVICE" || true
# A shallow clone of the pushed branch, taking the remote from the
# live checkout so there is no URL or token written down here. It
# works because the runner runs as the user that owns that checkout,
# which is the same reason it can write to DEPLOY_PATH at all.
- name: Checkout
run: |
rm -rf "$SRC"
git init --quiet "$SRC"
git -C "$SRC" remote add origin "$(git -C "$DEPLOY_PATH" remote get-url origin)"
git -C "$SRC" fetch --quiet --depth 1 origin "${GITHUB_REF_NAME:-main}"
git -C "$SRC" checkout --quiet FETCH_HEAD
git -C "$SRC" --no-pager log -1 --oneline
# A throwaway virtualenv in the workspace: 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
working-directory: src
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.
- name: Run tests
working-directory: src
run: .venv-ci/bin/python -m unittest discover -s tests -t . --verbose
- name: Deploy to production
working-directory: src
run: |
rsync -azc --no-times --delete \
--exclude "/.git/" \
--exclude "/.gitea/" \
--exclude "/.venv/" \
--exclude "/.venv-ci/" \
--exclude "/members.json" \
--exclude "__pycache__/" \
./ "$DEPLOY_PATH/"
# members.json is the stereo pair's learned membership and .venv is
# the runtime -- both are excluded above, so --delete leaves them be.
- 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"
- name: Restart
run: sudo 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:"
sudo systemctl status "$SERVICE" --no-pager --lines 30 || true
exit 1