summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorxXJSONDeruloXx <danielhimebauch@gmail.com>2026-09-10 14:45:27 -0400
committerxXJSONDeruloXx <danielhimebauch@gmail.com>2026-09-10 14:45:27 -0400
commit1e97e741ae89270801526234e3caaf5c7aacdca7 (patch)
tree7846986332a774ba706291bf0afc6eb321395c36
parentd7115074060a3e9e682d511eae3c47a1c64042b8 (diff)
downloaddecky-lsfg-vk-1e97e741ae89270801526234e3caaf5c7aacdca7.tar.gz
decky-lsfg-vk-1e97e741ae89270801526234e3caaf5c7aacdca7.zip
fix: clean Flatpak state from uninstall API
-rw-r--r--py_modules/lsfg_vk/plugin.py38
-rw-r--r--src/api/lsfgApi.ts9
-rw-r--r--src/hooks/useLsfgHooks.ts2
-rw-r--r--tests/test_plugin_flatpak_lifecycle.py71
4 files changed, 115 insertions, 5 deletions
diff --git a/py_modules/lsfg_vk/plugin.py b/py_modules/lsfg_vk/plugin.py
index 282ed96..aedb99b 100644
--- a/py_modules/lsfg_vk/plugin.py
+++ b/py_modules/lsfg_vk/plugin.py
@@ -37,7 +37,20 @@ class Plugin:
return self.installation_service.check_installation()
async def uninstall_lsfg_vk(self):
- return self.installation_service.uninstall()
+ result = self.installation_service.uninstall()
+ flatpak = self._cleanup_plugin_flatpak_state()
+ result = dict(result)
+ result["flatpak_cleanup"] = flatpak
+
+ if not result.get("success") or not flatpak.get("success"):
+ result["success"] = False
+ errors = [
+ str(error)
+ for error in (result.get("error"), flatpak.get("error"))
+ if error
+ ]
+ result["error"] = "; ".join(errors) or "Uninstallation did not complete"
+ return result
async def get_game_configs(self):
return self.configuration_service.get_game_configs()
@@ -166,13 +179,30 @@ class Plugin:
async def _uninstall(self):
decky.logger.info("decky-lsfg-vk plugin being uninstalled")
self.installation_service.cleanup_on_uninstall()
+ result = self._cleanup_plugin_flatpak_state()
+ if not result.get("success"):
+ decky.logger.warning(result.get("error"))
+ decky.logger.info("decky-lsfg-vk plugin uninstall cleanup completed")
+
+ def _cleanup_plugin_flatpak_state(self) -> Dict[str, Any]:
+ """Run ownership-safe Flatpak cleanup and fail closed on unexpected errors."""
try:
result = self.flatpak_service.remove_plugin_owned_extensions()
- if not result.get("success"):
- decky.logger.warning(result.get("error"))
+ if not isinstance(result, dict):
+ raise RuntimeError("Flatpak cleanup returned an invalid result")
+ return result
except Exception as error:
decky.logger.error(f"Error during Flatpak cleanup: {error}")
- decky.logger.info("decky-lsfg-vk plugin uninstall cleanup completed")
+ return {
+ "success": False,
+ "message": "",
+ "error": f"Error during Flatpak cleanup: {error}",
+ "removed_branches": [],
+ "preserved_branches": [],
+ "removed_filesystem_grants": [],
+ "preserved_filesystem_grants": [],
+ "ownership_uncertain": True,
+ }
async def _migration(self):
decky.logger.info("Running decky-lsfg-vk plugin migrations")
diff --git a/src/api/lsfgApi.ts b/src/api/lsfgApi.ts
index cf09ed0..12e284e 100644
--- a/src/api/lsfgApi.ts
+++ b/src/api/lsfgApi.ts
@@ -7,8 +7,17 @@ interface ApiResult {
error?: string | null;
}
+export interface FlatpakCleanupResult extends ApiResult {
+ removed_branches?: string[];
+ preserved_branches?: string[];
+ removed_filesystem_grants?: string[];
+ preserved_filesystem_grants?: string[];
+ ownership_uncertain?: boolean;
+}
+
export interface InstallationResult extends ApiResult {
removed_files?: string[];
+ flatpak_cleanup?: FlatpakCleanupResult;
}
export interface InstallationStatus {
diff --git a/src/hooks/useLsfgHooks.ts b/src/hooks/useLsfgHooks.ts
index 16f8962..5a82b61 100644
--- a/src/hooks/useLsfgHooks.ts
+++ b/src/hooks/useLsfgHooks.ts
@@ -96,7 +96,7 @@ export function useInstallation(reloadConfig?: () => Promise<void>) {
return;
}
setIsInstalled(false);
- setInstallationStatus("lsfg-vk uninstalled successfully!");
+ setInstallationStatus("lsfg-vk and Flatpak support uninstalled successfully!");
await checkInstallation();
showUninstallSuccessToast();
} catch (error) {
diff --git a/tests/test_plugin_flatpak_lifecycle.py b/tests/test_plugin_flatpak_lifecycle.py
index 25f09b6..613a807 100644
--- a/tests/test_plugin_flatpak_lifecycle.py
+++ b/tests/test_plugin_flatpak_lifecycle.py
@@ -57,6 +57,77 @@ class PluginFlatpakLifecycleTests(unittest.TestCase):
plugin.installation_service.cleanup_on_uninstall.assert_called_once_with()
plugin.flatpak_service.remove_plugin_owned_extensions.assert_called_once_with()
+ def test_uninstall_button_also_cleans_flatpak_support(self):
+ plugin = Plugin.__new__(Plugin)
+ plugin.installation_service = Mock()
+ plugin.installation_service.uninstall.return_value = {
+ "success": True,
+ "message": "lsfg-vk uninstalled successfully",
+ "error": None,
+ "removed_files": ["/home/deck/.local/lib/liblsfg-vk.so"],
+ }
+ plugin.flatpak_service = Mock()
+ plugin.flatpak_service.remove_plugin_owned_extensions.return_value = {
+ "success": True,
+ "message": "Plugin-owned Flatpak state removed",
+ "error": None,
+ "removed_branches": ["23.08", "24.08", "25.08"],
+ "preserved_branches": [],
+ "removed_filesystem_grants": ["/home/deck/.config/lsfg-vk"],
+ "preserved_filesystem_grants": [],
+ "ownership_uncertain": False,
+ }
+
+ result = asyncio.run(plugin.uninstall_lsfg_vk())
+
+ plugin.installation_service.uninstall.assert_called_once_with()
+ plugin.flatpak_service.remove_plugin_owned_extensions.assert_called_once_with()
+ self.assertTrue(result["success"])
+ self.assertEqual(result["flatpak_cleanup"]["removed_branches"], ["23.08", "24.08", "25.08"])
+
+ def test_uninstall_button_reports_flatpak_cleanup_failure(self):
+ plugin = Plugin.__new__(Plugin)
+ plugin.installation_service = Mock()
+ plugin.installation_service.uninstall.return_value = {
+ "success": True,
+ "message": "lsfg-vk uninstalled successfully",
+ "error": None,
+ "removed_files": [],
+ }
+ plugin.flatpak_service = Mock()
+ plugin.flatpak_service.remove_plugin_owned_extensions.return_value = {
+ "success": False,
+ "message": "",
+ "error": "Flatpak ownership metadata was not trusted",
+ "ownership_uncertain": True,
+ }
+
+ result = asyncio.run(plugin.uninstall_lsfg_vk())
+
+ self.assertFalse(result["success"])
+ self.assertIn("Flatpak ownership metadata was not trusted", result["error"])
+ self.assertTrue(result["flatpak_cleanup"]["ownership_uncertain"])
+
+ def test_uninstall_button_fails_closed_if_flatpak_cleanup_raises(self):
+ plugin = Plugin.__new__(Plugin)
+ plugin.installation_service = Mock()
+ plugin.installation_service.uninstall.return_value = {
+ "success": True,
+ "message": "lsfg-vk uninstalled successfully",
+ "error": None,
+ "removed_files": [],
+ }
+ plugin.flatpak_service = Mock()
+ plugin.flatpak_service.remove_plugin_owned_extensions.side_effect = RuntimeError(
+ "ownership could not be established"
+ )
+
+ result = asyncio.run(plugin.uninstall_lsfg_vk())
+
+ self.assertFalse(result["success"])
+ self.assertIn("ownership could not be established", result["error"])
+ self.assertTrue(result["flatpak_cleanup"]["ownership_uncertain"])
+
if __name__ == "__main__":
unittest.main()