Swaping back to nodejs dependent checkout
Deploy HEOS panel / deploy (push) Failing after 36s

This commit is contained in:
2026-09-14 23:58:53 +02:00
parent a7a8ab6f4e
commit 9a5f2ac639
2 changed files with 74 additions and 80 deletions
+40 -48
View File
@@ -3,17 +3,13 @@
# 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 updates DEPLOY_PATH and restarts the service, and it must run as the
# user that owns that directory.
# 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 is a git checkout, cloned there ONCE by hand -- see
# "Deploying from Gitea" in the README. This workflow only fast-forwards
# it, so the remote and whatever credentials reach it are set up a single
# time and stay put.
#
# Every step is plain shell on purpose: actions/checkout is a JavaScript
# action, and a host-mode runner can only run those with node on its PATH,
# failing with "Cannot find: node in PATH" without one.
# 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
@@ -33,60 +29,56 @@ jobs:
PANEL_URL: http://127.0.0.1:5005/ # WEB_PORT in config.py
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Check runner tools
run: |
command -v git
command -v python3
command -v rsync
command -v curl
- name: Check the deploy path is ready
- name: Check deploy path
run: |
if [ ! -d "$DEPLOY_PATH" ] || [ ! -w "$DEPLOY_PATH" ]; then
echo "$DEPLOY_PATH is missing, or not writable by $(id -un)."
exit 1
fi
if [ ! -d "$DEPLOY_PATH/.git" ]; then
echo "$DEPLOY_PATH is not a checkout yet. Once, on this machine,"
echo "as $(id -un):"
echo
echo " git clone <this repo> $DEPLOY_PATH"
echo " cd $DEPLOY_PATH"
echo " python3 -m venv .venv"
echo " .venv/bin/pip install -r requirements.txt"
echo " sudo cp deploy/heos-panel.service /etc/systemd/system/"
echo " sudo systemctl enable --now $SERVICE"
echo
echo "Then edit config.py for this house and push again."
exit 1
fi
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
- name: Fast-forward the checkout
run: |
cd "$DEPLOY_PATH"
git fetch --prune origin
# --ff-only on purpose: if someone has edited a tracked file on the
# box without committing it, this stops rather than throwing their
# change away.
git merge --ff-only "origin/${GITHUB_REF_NAME:-main}"
git --no-pager log -1 --oneline
# 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: |
cd "$DEPLOY_PATH"
test -d .venv || python3 -m venv .venv
.venv/bin/pip install --quiet -r requirements.txt
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. The new code
# is on disk by this point, but the running process is still the old
# one: a failure here stops the job before the restart below.
# 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: |
cd "$DEPLOY_PATH"
.venv/bin/python -m unittest discover -s tests -t . --verbose
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"
- name: Restart
run: sudo systemctl restart "$SERVICE"
+34 -32
View File
@@ -97,52 +97,54 @@ Edit `User=` and the paths in it if you keep the panel somewhere else.
## Deploying from Gitea
`.gitea/workflows/deploy.yml` fast-forwards the checkout on the server,
installs anything new from `requirements.txt`, runs the tests, restarts the
service and waits for the panel to answer again.
`.gitea/workflows/deploy.yml` checks out the push, runs the tests, rsyncs
the tree into place, installs anything new from `requirements.txt`,
restarts the service and waits for the panel to answer again. The tests run
before the rsync, so a failure leaves the server exactly as it was.
### Once, by hand on the server
### The runner
The workflow only ever *updates* a checkout. It never creates one, so the
remote and whatever credentials reach it are set up a single time and stay
put. As the user the runner runs as:
Host mode, on the machine that serves the panel, registered with the label
`heos` (`runs-on:` must match, or the job queues forever), running as a user
that can write to the deploy path. It also needs **node 20 or newer** on its
PATH: `actions/checkout` is a JavaScript action, and a host-mode runner has
nothing else to run one with — without it the job fails immediately with
`Cannot find: node in PATH`.
### Once, on the server
The deploy path does not need to be a checkout — an empty directory the
runner can write to is enough:
```bash
git clone <this repo> /var/www/html/heos
cd /var/www/html/heos
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
sudo cp deploy/heos-panel.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now heos-panel
sudo mkdir -p /var/www/html/heos
sudo chown "$(id -un)": /var/www/html/heos
echo "$(id -un) ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart heos-panel" \
| sudo tee /etc/sudoers.d/heos-panel
sudo chmod 440 /etc/sudoers.d/heos-panel
```
Edit `User=` and the paths in the unit if the panel lives somewhere else or
runs as someone else. Until that clone exists the workflow stops on its
first real step and prints these commands back at you.
Then push. That first run deploys the files and builds the virtualenv, and
stops at the restart, because the service does not exist yet. Install it
from the copy it just put there:
### The runner
```bash
sudo cp /var/www/html/heos/deploy/heos-panel.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable heos-panel
```
Host mode, on the machine that serves the panel, registered with the label
`heos` (`runs-on:` must match, or the job queues forever), running as the
user that owns the checkout — it writes there, and it fetches with that
checkout's own git credentials.
Edit `User=` and the paths in the unit first if the panel lives somewhere
else or runs as someone else. Re-run the workflow and it goes green.
Every step is plain shell. `actions/checkout` is a JavaScript action, and a
host-mode runner can only run those with `node` on its PATH; without one it
fails with `Cannot find: node in PATH`.
### What survives a deploy
Nothing untracked is disturbed, so `.venv` and `members.json` the runtime
and the stereo pair's learned membership — survive a deploy untouched. The
fast-forward is `--ff-only`, so a tracked file edited on the box without
being committed stops the deploy rather than being silently overwritten.
`config.py` is tracked: your device names live in git, so change them there
and push, rather than editing the deployed copy.
The rsync excludes `.venv` and `members.json`, so the runtime and the stereo
pair's learned membership are left alone by `--delete`. Everything else in
the deploy path is made to match the repo, `config.py` included: your device
names live in git, so change them there and push rather than editing the
deployed copy.
## Behind a reverse proxy, at /heos