From e37eca35784e2c34b3b090623a26b2784d2e5edb Mon Sep 17 00:00:00 2001
From: Timothee 'TTimo' Besset <ttimo@valvesoftware.com>
Date: Tue, 9 May 2023 15:37:48 -0500
Subject: [PATCH] validate external tools (such as rsync and ssh), show an
 error dialog if missing

---
 client/devkit_client/__init__.py  |  4 ++++
 client/devkit_client/gui2/gui2.py | 15 +++++++++++++++
 2 files changed, 19 insertions(+)

diff --git a/client/devkit_client/__init__.py b/client/devkit_client/__init__.py
index 3cd7e63..591de35 100644
--- a/client/devkit_client/__init__.py
+++ b/client/devkit_client/__init__.py
@@ -248,6 +248,10 @@ def locate_external_tools():
         return g_external_tools
     if sys.platform != 'win32':
         g_external_tools = (None, 'ssh', 'rsync', None)
+        for tool in g_external_tools:
+            if tool is not None:
+                if shutil.which(tool) is None:
+                    raise Exception(f'{tool} not found - please install.')
         return g_external_tools
 
     cygpath = _locate_external_tool('cygpath.exe')
diff --git a/client/devkit_client/gui2/gui2.py b/client/devkit_client/gui2/gui2.py
index 0e9aa58..e84255f 100644
--- a/client/devkit_client/gui2/gui2.py
+++ b/client/devkit_client/gui2/gui2.py
@@ -1076,6 +1076,7 @@ class DevkitsWindow(ToolWindow):
         self.add_by_ip_text = ''
         self.add_by_ip_port = '{}'.format(devkit_client.DEFAULT_DEVKIT_SERVICE_HTTP)
         self.steam_client_args = None
+        self.first_draw = True
         # Hook into status updates to refresh the command line arguments
         self.devkit_commands.signal_steamos_status.connect(self.on_steamos_status)
         # Indicates whether Valve internal services are available
@@ -1203,6 +1204,20 @@ class DevkitsWindow(ToolWindow):
             return
 
         # === draw =========================================================================
+        if self.first_draw:
+            try:
+                devkit_client.locate_external_tools()
+            except Exception as e:
+                failed_future = concurrent.futures.Future()
+                failed_future.set_exception(e)
+                self.modal_wait = ModalWait(
+                    self.viewport,
+                    self.toolbar,
+                    'ERROR',
+                    failed_future
+                )
+            self.first_draw = False
+
         (_, opened) = imgui.begin(self.BUTTON_NAME, True, imgui.WINDOW_NO_COLLAPSE)
         if not opened:
             self.visible = False
-- 
GitLab