mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-08-21 02:19:07 +00:00
The update button pulls new code and restarts nothing. There is no systemctl, restart, reload or reboot anywhere in the 172-line git_pull handler -- it stashes, pulls, installs changed requirements, re-removes plugins the user had uninstalled, and returns "Code updated successfully." Meanwhile both services go on running the code they loaded at boot. So the display keeps rendering the old build, the web interface keeps serving the old build, and the user is told the update worked. Nothing on screen suggests otherwise, and the next reboot is what actually applies it -- whenever that is. The affordance for this already exists: the restart-pending banner, raised after main-config saves, with a Restart Now button wired to the display service. A code update is a stronger reason to show it than a config save is. The response now reports restart_required, and applyUpdate raises the banner with wording for a code update rather than a config save. The banner's message became a parameter and is persisted next to the flag, since it outlives the page that raised it. restart_required is only true when the pull actually moved HEAD. "Already up to date" is a success too, and prompting after a no-op would train users to dismiss the prompt unread. This covers the display service, which is what the Restart Now button drives and what users notice. The web interface still picks up its own new code on its next restart; restarting it from inside a request it is serving is a larger change than this one. Reverting the flag fails the test that a pull which moved HEAD asks for a restart. 290 tests pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01STMbQE4YctTacQXfbYqKuW
85 lines
3.2 KiB
Python
85 lines
3.2 KiB
Python
"""A pull that changed nothing on the running system is not an applied update.
|
|
|
|
git_pull replaces files on disk and restarts nothing -- there is no systemctl
|
|
call anywhere in the handler. The display and web services keep running the
|
|
code they loaded at boot, so the user is told "Code updated successfully" and
|
|
sees no change until they happen to reboot. The response now says whether a
|
|
restart is owed, and the UI raises the existing restart-pending banner.
|
|
"""
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
from flask import Flask
|
|
|
|
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
|
|
|
from web_interface.blueprints import api_v3 as mod # noqa: E402
|
|
from web_interface.blueprints.api_v3 import api_v3 # noqa: E402
|
|
|
|
|
|
@pytest.fixture
|
|
def client():
|
|
app = Flask(__name__)
|
|
app.config['TESTING'] = True
|
|
app.register_blueprint(api_v3, url_prefix='/api/v3')
|
|
# The handler consults these after a successful pull; None is the
|
|
# "not wired up" case it already guards for.
|
|
api_v3.plugin_store_manager = None
|
|
api_v3.config_manager = None
|
|
return app.test_client()
|
|
|
|
|
|
def _git(heads, pull_rc=0, pull_out='Updating a1b2c3..d4e5f6\n'):
|
|
"""Fake git. `heads` are the successive answers to rev-parse HEAD."""
|
|
seq = list(heads)
|
|
|
|
def run(args, **kwargs):
|
|
def ok(stdout='', rc=0, b=False):
|
|
return subprocess.CompletedProcess(
|
|
args, rc, stdout=(stdout.encode() if b else stdout),
|
|
stderr=(b'' if b else ''))
|
|
if args[:2] == ['git', 'rev-parse'] and args[-1] == 'HEAD':
|
|
return ok(seq.pop(0) + '\n' if seq else 'deadbeef\n')
|
|
if 'symbolic-full-name' in args or '@{u}' in args:
|
|
return ok('origin/main\n')
|
|
if args[:2] == ['git', 'status']:
|
|
return ok('')
|
|
if args[:2] == ['git', 'diff']:
|
|
return ok('')
|
|
if args[:2] == ['git', 'pull']:
|
|
return ok(pull_out, pull_rc)
|
|
return ok('')
|
|
return run
|
|
|
|
|
|
def _pull(client):
|
|
return client.post('/api/v3/system/action',
|
|
json={'action': 'git_pull'}).get_json()
|
|
|
|
|
|
class TestRestartIsRequestedWhenCodeChanged:
|
|
def test_a_pull_that_moved_head_asks_for_a_restart(self, client):
|
|
with patch.object(mod.subprocess, 'run', _git(['aaa111', 'bbb222'])):
|
|
data = _pull(client)
|
|
assert data['status'] == 'success'
|
|
assert data['restart_required'] is True, (
|
|
"new code on disk, services still running the old code, and "
|
|
"nothing told the user to restart")
|
|
|
|
def test_already_up_to_date_does_not(self, client):
|
|
with patch.object(mod.subprocess, 'run',
|
|
_git(['aaa111', 'aaa111'], pull_out='Already up to date.\n')):
|
|
data = _pull(client)
|
|
assert data['status'] == 'success'
|
|
assert data['restart_required'] is False, (
|
|
"prompting after a no-op update trains users to ignore the prompt")
|
|
|
|
def test_a_failed_pull_does_not(self, client):
|
|
with patch.object(mod.subprocess, 'run', _git(['aaa111'], pull_rc=1)):
|
|
data = _pull(client)
|
|
assert data['status'] == 'error'
|
|
assert data['restart_required'] is False
|