fix(apps): honour keep_data on uninstall and fix install log name

- uninstall_app dropped input.keep_data; _manage_app always ran the
  bonus script with a bare 'off', so the RaspiBlitz script fell back to
  an interactive whiptail prompt that hangs the non-interactive API.
  Thread keep_data through and pass the explicit
  --keep-data/--delete-data flag the scripts expect.
- install.{app_id}.log rendered as install.AppId.MEMPOOL.log because
  str-enum formatting includes the class name on Python 3.11+; use
  app_id.value here and in the CLN-incompatibility message.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
fusion44 2026-07-03 20:58:35 +02:00
parent d5a568da3e
commit 9912ff0bfa
No known key found for this signature in database
3 changed files with 72 additions and 7 deletions

View file

@ -253,10 +253,14 @@ class RaspiBlitzApps(AppsBase):
yield result
async def uninstall_app(self, input: AppUninstallInput) -> AppManageResult:
async for result in self._manage_app(input.app_id, InstallMode.OFF):
async for result in self._manage_app(
input.app_id, InstallMode.OFF, keep_data=input.keep_data
):
yield result
async def _manage_app(self, app_id: AppId, mode: InstallMode) -> AppManageResult:
async def _manage_app(
self, app_id: AppId, mode: InstallMode, keep_data: bool = True
) -> AppManageResult:
"""
Manages the installation or uninstallation process for a specified application.
@ -315,16 +319,21 @@ class RaspiBlitzApps(AppsBase):
state=AppManagementProcessState.FAILURE,
message=ErrorMessage(
error_code=ApiErrors.APP_INVALID_FOR_PLATFORM,
detail=f"{app_id} not available for Core Lightning nodes",
detail=f"{app_id.value} not available for Core Lightning nodes",
),
)
)
return
if installing:
params = "on"
else:
# the bonus scripts require an explicit keep/delete flag,
# otherwise they fall back to an interactive prompt
params = f"off {'--keep-data' if keep_data else '--delete-data'}"
try:
async for data in self.run_bonus_script(
app_id, "on" if installing else "off"
):
async for data in self.run_bonus_script(app_id, params):
if isinstance(data, str):
yield Ok(
AppManageTaskMessage(
@ -377,7 +386,7 @@ class RaspiBlitzApps(AppsBase):
return
try:
log_file_name = f"/var/cache/raspiblitz/temp/install.{app_id}.log"
log_file_name = f"/var/cache/raspiblitz/temp/install.{app_id.value}.log"
stdout_full = ""
stderr_full = ""
data = {}

View file

@ -20,6 +20,8 @@ _TEST_ENV_DEFAULTS = {
"BAPI_BITCOIND_RPC_PW": "test",
"BAPI_BITCOIND_ZMQ_BLOCK_RPC": "hashblock",
"BAPI_BITCOIND_ZMQ_BLOCK_PORT": "28332",
# must be an existing directory for the raspiblitz apps impl to import
"BAPI_RB_SHELL_SCRIPT_PATH": "/tmp",
}
for _key, _value in _TEST_ENV_DEFAULTS.items():

View file

@ -0,0 +1,54 @@
"""
Regression test: uninstalling an app must honour the keep_data choice.
_manage_app dropped input.keep_data and always ran the bonus script with a
bare "off", so the RaspiBlitz bonus script fell back to an interactive
whiptail prompt (which hangs the non-interactive API) instead of the
requested keep/delete behaviour.
"""
import pytest
from app.apps.impl import raspiblitz
from app.apps.models import AppId, AppUninstallInput
from app.external.result_type.src.result.result import Ok
@pytest.fixture
def node(monkeypatch):
n = raspiblitz.RaspiBlitzApps()
async def fake_valid(app_id, installing):
return Ok(None)
monkeypatch.setattr(raspiblitz, "_validate_app_id", lambda app_id: Ok(None))
monkeypatch.setattr(n, "_valid_installed_status", fake_valid)
return n
async def _run_uninstall(node, monkeypatch, keep_data: bool) -> str:
captured = {}
async def fake_bonus(app_id, params):
captured["params"] = params
return
yield # unreachable; makes this an async generator
monkeypatch.setattr(node, "run_bonus_script", fake_bonus)
async for _ in node.uninstall_app(
AppUninstallInput(app_id=AppId.MEMPOOL, keep_data=keep_data)
):
pass
return captured["params"]
async def test_uninstall_delete_data(node, monkeypatch):
params = await _run_uninstall(node, monkeypatch, keep_data=False)
assert params == "off --delete-data"
async def test_uninstall_keep_data(node, monkeypatch):
params = await _run_uninstall(node, monkeypatch, keep_data=True)
assert params == "off --keep-data"