diff --git a/controller/lib/scripts.py b/controller/lib/scripts.py
index cdddf05d..7785c9ca 100644
--- a/controller/lib/scripts.py
+++ b/controller/lib/scripts.py
@@ -21,6 +21,14 @@ logger = logging.getLogger(__name__)
# - type: "branch" (auto-populates with current git branch) or "text"
# - required: Whether the parameter must have a value
AVAILABLE_SCRIPTS = {
+ "help": {
+ "name": "help",
+ "display_name": "System Diagnostics",
+ "description": "Runs help.sh which checks WROLPi services, certificates, database, and dependencies."
+ " Read-only; changes nothing.",
+ "service_name": "wrolpi-help-script.service",
+ "warnings": [],
+ },
"repair": {
"name": "repair",
"display_name": "Repair WROLPi",
diff --git a/controller/templates/index.html b/controller/templates/index.html
index 4dc932dd..78196a71 100644
--- a/controller/templates/index.html
+++ b/controller/templates/index.html
@@ -2070,6 +2070,7 @@
const warningsList = document.getElementById('script-warnings-list');
warningsList.innerHTML = script.warnings.map(w => `
${w}`).join('');
+ document.getElementById('script-confirm-warnings').style.display = script.warnings.length ? '' : 'none';
// Generate parameter inputs dynamically
const paramsContainer = document.getElementById('script-params-container');
diff --git a/controller/test/test_scripts.py b/controller/test/test_scripts.py
index 8d083841..ecbb7fde 100644
--- a/controller/test/test_scripts.py
+++ b/controller/test/test_scripts.py
@@ -35,6 +35,14 @@ class TestAvailableScripts:
repair = AVAILABLE_SCRIPTS["repair"]
assert len(repair["warnings"]) > 0
+ def test_help_script_exists(self):
+ """Should have a help (diagnostics) script defined."""
+ assert "help" in AVAILABLE_SCRIPTS
+ help_script = AVAILABLE_SCRIPTS["help"]
+ assert help_script["service_name"] == "wrolpi-help-script.service"
+ # help.sh is read-only, so it needs no warnings.
+ assert help_script["warnings"] == []
+
class TestListAvailableScripts:
"""Tests for list_available_scripts function."""
@@ -112,7 +120,8 @@ class TestGetScriptStatus:
with mock.patch("controller.lib.scripts._get_service_timing", return_value=(None, None)):
result = get_script_status()
assert result["running"] is True
- assert result["script_name"] == "repair"
+ # The first script checked reports as running because every systemctl call is mocked.
+ assert result["script_name"] == next(iter(AVAILABLE_SCRIPTS))
def test_returns_not_running_when_service_inactive(self):
"""Should return not running when service is inactive."""
diff --git a/etc/raspberrypios/wrolpi-help-script.service b/etc/raspberrypios/wrolpi-help-script.service
new file mode 100644
index 00000000..74ddab9c
--- /dev/null
+++ b/etc/raspberrypios/wrolpi-help-script.service
@@ -0,0 +1,10 @@
+[Unit]
+Description=WROLPi Help Script (system diagnostics)
+After=network.target
+
+[Service]
+Type=oneshot
+ExecStart=/opt/wrolpi/help.sh
+User=root
+StandardOutput=journal
+StandardError=journal