fix(auto-update): CI failures on Linux

- Keep the setup result when chown fails. CI runs as a non-root user, where
  chown to the web user raises; that discarded the result file, so the
  General tab would never learn whether setup worked. Regression test added.
- Register the two new /api/v3/system/auto-update routes in the URL map
  snapshot.
- Use utility classes app.css defines (space-y-1, hover:text-red-600).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Chuck
2026-09-14 17:49:16 -04:00
co-authored by Claude Opus 5
parent e9a9cb53f7
commit 388d1e7de3
5 changed files with 38 additions and 3 deletions
+5
View File
@@ -195,7 +195,12 @@ class UpdateHelperSetup:
json.dump(result, f, indent=2) json.dump(result, f, indent=2)
os.chmod(tmp, 0o644) os.chmod(tmp, 0o644)
if self._web_ids and hasattr(os, 'chown'): if self._web_ids and hasattr(os, 'chown'):
# Only root can give the file away; the result is readable
# (0644) either way, so a failed chown must not lose it.
try:
os.chown(tmp, *self._web_ids) os.chown(tmp, *self._web_ids)
except OSError:
pass
os.replace(tmp, self.result_file) os.replace(tmp, self.result_file)
except OSError as e: except OSError as e:
logger.warning("Could not record automatic update setup result: %s", e) logger.warning("Could not record automatic update setup result: %s", e)
+17
View File
@@ -841,6 +841,23 @@
"POST" "POST"
] ]
], ],
[
"/api/v3/system/auto-update",
"api_v3.get_auto_update_status",
[
"GET",
"HEAD",
"OPTIONS"
]
],
[
"/api/v3/system/auto-update/dismiss",
"api_v3.dismiss_auto_update_alert",
[
"OPTIONS",
"POST"
]
],
[ [
"/api/v3/system/check-update", "/api/v3/system/check-update",
"api_v3.check_for_update", "api_v3.check_for_update",
+13
View File
@@ -167,3 +167,16 @@ def test_not_a_systemd_host_is_left_alone(tmp_path):
systemctl = FakeSystemctl() systemctl = FakeSystemctl()
assert setup(root, tmp_path / 'no-etc', systemctl).ensure(ON) is None assert setup(root, tmp_path / 'no-etc', systemctl).ensure(ON) is None
assert systemctl.calls == [] assert systemctl.calls == []
def test_the_result_is_kept_when_it_cannot_be_given_to_the_web_user(tmp_path, monkeypatch):
"""Caught by CI, which runs as a non-root Linux user: chown needs root, and
a failed chown used to discard the result, so the General tab never
learned whether setup worked."""
def refuse(*args):
raise PermissionError('Operation not permitted')
monkeypatch.setattr(aus.os, 'chown', refuse, raising=False)
root, etc = project(tmp_path)
out = setup(root, etc, FakeSystemctl()).ensure(ON)
assert out['status'] == 'installed'
assert result(root) == out
@@ -70,7 +70,7 @@
<p class="mt-1 ml-6 text-xs text-blue-700">An update was just installed and its health check is running.</p> <p class="mt-1 ml-6 text-xs text-blue-700">An update was just installed and its health check is running.</p>
{% endif %} {% endif %}
{% if auto_update_status and (auto_update_status.last_run or auto_update_status.next_due) %} {% if auto_update_status and (auto_update_status.last_run or auto_update_status.next_due) %}
<div class="mt-1 ml-6 text-xs text-gray-500 space-y-0.5"> <div class="mt-1 ml-6 text-xs text-gray-500 space-y-1">
{% if auto_update_status.last_run %} {% if auto_update_status.last_run %}
<p>Last automatic update: {{ auto_update_status.last_run }} <p>Last automatic update: {{ auto_update_status.last_run }}
<span class="{{ 'text-red-600' if auto_update_status.status == 'error' else '' }}">— {{ auto_update_status.summary }}</span></p> <span class="{{ 'text-red-600' if auto_update_status.status == 'error' else '' }}">— {{ auto_update_status.summary }}</span></p>
@@ -8,7 +8,7 @@
<p class="text-sm text-red-700 mt-1" id="auto-update-banner-text"></p> <p class="text-sm text-red-700 mt-1" id="auto-update-banner-text"></p>
<p class="text-xs text-red-600 mt-1">Details are under Automatic Updates on the General tab.</p> <p class="text-xs text-red-600 mt-1">Details are under Automatic Updates on the General tab.</p>
</div> </div>
<button type="button" onclick="window.dismissAutoUpdateBanner()" class="ml-4 flex-shrink-0 text-red-500 hover:text-red-700" aria-label="Dismiss"> <button type="button" onclick="window.dismissAutoUpdateBanner()" class="ml-4 flex-shrink-0 text-red-500 hover:text-red-600" aria-label="Dismiss">
<i class="fas fa-times"></i> <i class="fas fa-times"></i>
</button> </button>
</div> </div>