From ce8596945e527bd6f8bee1e2098fd4c3ad4c19a6 Mon Sep 17 00:00:00 2001 From: Franzz Date: Tue, 15 Sep 2026 00:08:00 +0200 Subject: [PATCH] Fix service restarter test --- .gitea/workflows/deploy.yml | 26 +++++++++++++++++++++++--- README.md | 12 +++++++++++- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index 89c6649..e9bca61 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -34,6 +34,7 @@ jobs: - name: Check runner tools run: | + echo "running as $(id -un) on $(hostname)" command -v python3 command -v rsync command -v curl @@ -43,8 +44,25 @@ jobs: test -d "$DEPLOY_PATH" test -w "$DEPLOY_PATH" + # `sudo -n -l ` 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: sudo -n systemctl is-active "$SERVICE" || true + 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 @@ -80,8 +98,10 @@ jobs: 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 systemctl restart "$SERVICE" + run: sudo -n "$(command -v systemctl)" restart "$SERVICE" - name: Wait for the panel to answer run: | @@ -95,5 +115,5 @@ jobs: sleep 1 done echo "panel did not come back -- last of its log:" - sudo systemctl status "$SERVICE" --no-pager --lines 30 || true + systemctl status "$SERVICE" --no-pager --lines 30 || true exit 1 diff --git a/README.md b/README.md index 01aa1e6..8ca37c9 100644 --- a/README.md +++ b/README.md @@ -120,11 +120,21 @@ runner can write to is enough: 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" \ +echo "$(id -un) ALL=(ALL) NOPASSWD: $(command -v systemctl) restart heos-panel" \ | sudo tee /etc/sudoers.d/heos-panel sudo chmod 440 /etc/sudoers.d/heos-panel ``` +Run those **as the user the runner runs as**, not as yourself — the first +step of the workflow prints who that is. Two things in that sudoers line are +easy to get wrong, and the workflow checks both before it deploys anything, +printing the line back at you with the right values filled in: + +- the user has to be the runner's, and +- the path has to be the one `sudo` resolves from `PATH`. It compares that + string against the sudoers line without following symlinks, so on a system + where `/bin` links to `/usr/bin` the two spellings are not interchangeable. + 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: