feat(install): updates refresh systemd units; new installs run the newest release (#729)

Updates that move HEAD now install changed systemd units through a root-owned helper (/usr/local/sbin/ledmatrix-refresh-units, two literal sudo lines), with a backup restored on rollback; a refresh that fails part-way puts the old units back. Devices without the new sudo rule keep updating and are told to re-run the installer once. The one-shot installer now checks out the newest vX.Y.Z release (LEDMATRIX_CHANNEL=beta keeps main) and never moves an existing checkout backwards.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Chuck
2026-10-03 13:09:53 -04:00
committed by GitHub
co-authored by Claude Opus 5.5
parent 515248b34e
commit f841fa36b6
24 changed files with 1974 additions and 29 deletions
+11
View File
@@ -415,6 +415,7 @@ def _perform_core_update_locked(stash_local_changes=True):
# date" is a success too, and prompting for a restart then would
# train users to ignore the prompt.
code_changed = False
unit_result = None
# Requirement files whose install failed. The automatic updater refuses
# to restart onto code whose dependencies did not install.
dependency_failures = []
@@ -537,6 +538,14 @@ def _perform_core_update_locked(stash_local_changes=True):
)
except (OSError, RuntimeError) as purge_err:
logger.warning("Post-update plugin purge failed: %s", purge_err)
# The new code may come with new systemd unit settings (systemd/*).
# Install them now, so the restart that follows runs under them; the
# automatic update's rollback puts the old ones back.
if code_changed:
from web_interface import unit_refresh
unit_result = unit_refresh.refresh_after_update()
if unit_result['message']:
pull_message += " " + unit_result['message']
else:
logger.warning("git pull failed (returncode=%d): %s", result.returncode, result.stderr)
# Show git's own first line: "check logs" leaves the user with
@@ -556,6 +565,8 @@ def _perform_core_update_locked(stash_local_changes=True):
'restart_required': bool(result.returncode == 0 and code_changed),
'dependency_failures': dependency_failures,
'channel': channel.channel,
# web_interface/unit_refresh.py's result, or None when no code changed.
'unit_refresh': unit_result,
}