From 88134ba979be2597cccda885451b6bf612d1b977 Mon Sep 17 00:00:00 2001 From: Kirill Kalishev Date: Tue, 18 Feb 2025 17:31:21 -0500 Subject: [PATCH 1/5] setup script can communicate an error message to the end user --- .../coder/gateway/CoderGatewayConstants.kt | 1 + .../gateway/CoderRemoteConnectionHandle.kt | 33 ++++++++++--- .../coder/gateway/util/SetupCommandTest.kt | 47 +++++++++++++++++++ 3 files changed, 74 insertions(+), 7 deletions(-) create mode 100644 src/test/kotlin/com/coder/gateway/util/SetupCommandTest.kt diff --git a/src/main/kotlin/com/coder/gateway/CoderGatewayConstants.kt b/src/main/kotlin/com/coder/gateway/CoderGatewayConstants.kt index 6344aca68..1defb91d8 100644 --- a/src/main/kotlin/com/coder/gateway/CoderGatewayConstants.kt +++ b/src/main/kotlin/com/coder/gateway/CoderGatewayConstants.kt @@ -3,4 +3,5 @@ package com.coder.gateway object CoderGatewayConstants { const val GATEWAY_CONNECTOR_ID = "Coder.Gateway.Connector" const val GATEWAY_RECENT_CONNECTIONS_ID = "Coder.Gateway.Recent.Connections" + const val GATEWAY_SETUP_COMMAND_ERROR = "CODER_SETUP_ERROR" } diff --git a/src/main/kotlin/com/coder/gateway/CoderRemoteConnectionHandle.kt b/src/main/kotlin/com/coder/gateway/CoderRemoteConnectionHandle.kt index 102b73fcc..b55f138d8 100644 --- a/src/main/kotlin/com/coder/gateway/CoderRemoteConnectionHandle.kt +++ b/src/main/kotlin/com/coder/gateway/CoderRemoteConnectionHandle.kt @@ -2,6 +2,7 @@ package com.coder.gateway +import com.coder.gateway.CoderGatewayConstants.GATEWAY_SETUP_COMMAND_ERROR import com.coder.gateway.cli.CoderCLIManager import com.coder.gateway.models.WorkspaceProjectIDE import com.coder.gateway.models.toIdeWithStatus @@ -412,18 +413,16 @@ class CoderRemoteConnectionHandle { ) { if (setupCommand.isNotBlank()) { indicator.text = "Running setup command..." - try { - exec(workspace, setupCommand) - } catch (ex: Exception) { - if (!ignoreSetupFailure) { - throw ex - } - } + processSetupCommand( + { exec(workspace, setupCommand) }, + ignoreSetupFailure + ) } else { logger.info("No setup command to run on ${workspace.hostname}") } } + /** * Execute a command in the IDE's bin directory. * This exists since the accessor does not provide a generic exec. @@ -523,5 +522,25 @@ class CoderRemoteConnectionHandle { companion object { val logger = Logger.getInstance(CoderRemoteConnectionHandle::class.java.simpleName) + fun processSetupCommand( + output: () -> String, + ignoreSetupFailure: Boolean + ) { + try { + val errorText = output + .invoke() + .lines() + .firstOrNull { it.contains(GATEWAY_SETUP_COMMAND_ERROR) } + ?.let { it.substring(it.indexOf(GATEWAY_SETUP_COMMAND_ERROR) + GATEWAY_SETUP_COMMAND_ERROR.length).trim() } + + if (!errorText.isNullOrBlank()) { + throw Exception(errorText) + } + } catch (ex: Exception) { + if (!ignoreSetupFailure) { + throw ex + } + } + } } } diff --git a/src/test/kotlin/com/coder/gateway/util/SetupCommandTest.kt b/src/test/kotlin/com/coder/gateway/util/SetupCommandTest.kt new file mode 100644 index 000000000..80cce97e7 --- /dev/null +++ b/src/test/kotlin/com/coder/gateway/util/SetupCommandTest.kt @@ -0,0 +1,47 @@ +package com.coder.gateway.util + +import com.coder.gateway.CoderRemoteConnectionHandle.Companion.processSetupCommand +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import kotlin.test.assertEquals + +internal class SetupCommandTest { + + @Test + fun executionErrors() { + assertEquals( + "Execution error", + assertThrows { + processSetupCommand({ throw Exception("Execution error") }, false) + }.message + ) + processSetupCommand({ throw Exception("Execution error") }, true) + } + + @Test + fun setupScriptError() { + assertEquals( + "Your IDE is expired, please update", + assertThrows { + processSetupCommand({ + """ + execution line 1 + execution line 2 + CODER_SETUP_ERRORYour IDE is expired, please update + execution line 3 + """ + }, false) + }.message + ) + + processSetupCommand({ + """ + execution line 1 + execution line 2 + CODER_SETUP_ERRORYour IDE is expired, please update + execution line 3 + """ + }, true) + + } +} \ No newline at end of file From 55870c48a1d5b87aab289f10e6cd194b8bd84496 Mon Sep 17 00:00:00 2001 From: Kirill Kalishev Date: Tue, 18 Feb 2025 19:15:01 -0500 Subject: [PATCH 2/5] review fixes --- .../coder/gateway/CoderRemoteConnectionHandle.kt | 13 ++++++------- .../com/coder/gateway/util/SetupCommandTest.kt | 14 +++++++------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/main/kotlin/com/coder/gateway/CoderRemoteConnectionHandle.kt b/src/main/kotlin/com/coder/gateway/CoderRemoteConnectionHandle.kt index b55f138d8..26b87336a 100644 --- a/src/main/kotlin/com/coder/gateway/CoderRemoteConnectionHandle.kt +++ b/src/main/kotlin/com/coder/gateway/CoderRemoteConnectionHandle.kt @@ -413,10 +413,9 @@ class CoderRemoteConnectionHandle { ) { if (setupCommand.isNotBlank()) { indicator.text = "Running setup command..." - processSetupCommand( - { exec(workspace, setupCommand) }, - ignoreSetupFailure - ) + processSetupCommand(ignoreSetupFailure) { + exec(workspace, setupCommand) + } } else { logger.info("No setup command to run on ${workspace.hostname}") } @@ -523,11 +522,11 @@ class CoderRemoteConnectionHandle { companion object { val logger = Logger.getInstance(CoderRemoteConnectionHandle::class.java.simpleName) fun processSetupCommand( - output: () -> String, - ignoreSetupFailure: Boolean + ignoreSetupFailure: Boolean, + execCommand: () -> String ) { try { - val errorText = output + val errorText = execCommand .invoke() .lines() .firstOrNull { it.contains(GATEWAY_SETUP_COMMAND_ERROR) } diff --git a/src/test/kotlin/com/coder/gateway/util/SetupCommandTest.kt b/src/test/kotlin/com/coder/gateway/util/SetupCommandTest.kt index 80cce97e7..62f4fa7b8 100644 --- a/src/test/kotlin/com/coder/gateway/util/SetupCommandTest.kt +++ b/src/test/kotlin/com/coder/gateway/util/SetupCommandTest.kt @@ -12,10 +12,10 @@ internal class SetupCommandTest { assertEquals( "Execution error", assertThrows { - processSetupCommand({ throw Exception("Execution error") }, false) + processSetupCommand(false) { throw Exception("Execution error") } }.message ) - processSetupCommand({ throw Exception("Execution error") }, true) + processSetupCommand(true) { throw Exception("Execution error") } } @Test @@ -23,25 +23,25 @@ internal class SetupCommandTest { assertEquals( "Your IDE is expired, please update", assertThrows { - processSetupCommand({ - """ + processSetupCommand(false) { + """ execution line 1 execution line 2 CODER_SETUP_ERRORYour IDE is expired, please update execution line 3 """ - }, false) + } }.message ) - processSetupCommand({ + processSetupCommand(true) { """ execution line 1 execution line 2 CODER_SETUP_ERRORYour IDE is expired, please update execution line 3 """ - }, true) + } } } \ No newline at end of file From eb5e2ac7b34939785c2e6900d6859de0da74aa16 Mon Sep 17 00:00:00 2001 From: Kirill Kalishev Date: Tue, 18 Feb 2025 19:37:06 -0500 Subject: [PATCH 3/5] custom exception class for setup command --- .../gateway/CoderRemoteConnectionHandle.kt | 36 +++++++++++++------ .../gateway/CoderSetupCommandException.kt | 7 ++++ .../messages/CoderGatewayBundle.properties | 1 + .../coder/gateway/util/SetupCommandTest.kt | 5 +-- 4 files changed, 36 insertions(+), 13 deletions(-) create mode 100644 src/main/kotlin/com/coder/gateway/CoderSetupCommandException.kt diff --git a/src/main/kotlin/com/coder/gateway/CoderRemoteConnectionHandle.kt b/src/main/kotlin/com/coder/gateway/CoderRemoteConnectionHandle.kt index 26b87336a..790a2cd3a 100644 --- a/src/main/kotlin/com/coder/gateway/CoderRemoteConnectionHandle.kt +++ b/src/main/kotlin/com/coder/gateway/CoderRemoteConnectionHandle.kt @@ -161,25 +161,38 @@ class CoderRemoteConnectionHandle { ) logger.info("Adding ${parameters.ideName} for ${parameters.hostname}:${parameters.projectPath} to recent connections") recentConnectionsService.addRecentConnection(parameters.toRecentWorkspaceConnection()) + } catch (e: CoderSetupCommandException) { + logger.error("Failed to run setup command", e) + showConnectionErrorMessage( + e.message ?: "Unknown error", + "gateway.connector.coder.setup-command.failed", + ) } catch (e: Exception) { if (isCancellation(e)) { logger.info("Connection canceled due to ${e.javaClass.simpleName}") } else { logger.error("Failed to connect (will not retry)", e) - // The dialog will close once we return so write the error - // out into a new dialog. - ApplicationManager.getApplication().invokeAndWait { - Messages.showMessageDialog( - e.message ?: e.javaClass.simpleName ?: "Aborted", - CoderGatewayBundle.message("gateway.connector.coder.connection.failed"), - Messages.getErrorIcon(), - ) - } + showConnectionErrorMessage( + e.message ?: e.javaClass.simpleName ?: "Aborted", + "gateway.connector.coder.connection.failed" + ) } } } } + // The dialog will close once we return so write the error + // out into a new dialog. + private fun showConnectionErrorMessage(message: String, titleKey: String) { + ApplicationManager.getApplication().invokeAndWait { + Messages.showMessageDialog( + message, + CoderGatewayBundle.message(titleKey), + Messages.getErrorIcon(), + ) + } + } + /** * Return a new (non-EAP) IDE if we should update. */ @@ -521,6 +534,7 @@ class CoderRemoteConnectionHandle { companion object { val logger = Logger.getInstance(CoderRemoteConnectionHandle::class.java.simpleName) + @Throws(CoderSetupCommandException::class) fun processSetupCommand( ignoreSetupFailure: Boolean, execCommand: () -> String @@ -533,11 +547,11 @@ class CoderRemoteConnectionHandle { ?.let { it.substring(it.indexOf(GATEWAY_SETUP_COMMAND_ERROR) + GATEWAY_SETUP_COMMAND_ERROR.length).trim() } if (!errorText.isNullOrBlank()) { - throw Exception(errorText) + throw CoderSetupCommandException(errorText) } } catch (ex: Exception) { if (!ignoreSetupFailure) { - throw ex + throw CoderSetupCommandException(ex.message ?: "Unknown error", ex) } } } diff --git a/src/main/kotlin/com/coder/gateway/CoderSetupCommandException.kt b/src/main/kotlin/com/coder/gateway/CoderSetupCommandException.kt new file mode 100644 index 000000000..e43d92695 --- /dev/null +++ b/src/main/kotlin/com/coder/gateway/CoderSetupCommandException.kt @@ -0,0 +1,7 @@ +package com.coder.gateway + +class CoderSetupCommandException : Exception { + + constructor(message: String) : super(message) + constructor(message: String, cause: Throwable) : super(message, cause) +} \ No newline at end of file diff --git a/src/main/resources/messages/CoderGatewayBundle.properties b/src/main/resources/messages/CoderGatewayBundle.properties index 4400eb893..b392c3983 100644 --- a/src/main/resources/messages/CoderGatewayBundle.properties +++ b/src/main/resources/messages/CoderGatewayBundle.properties @@ -49,6 +49,7 @@ gateway.connector.coder.connection.provider.title=Connecting to Coder workspace. gateway.connector.coder.connecting=Connecting... gateway.connector.coder.connecting.retry=Connecting (attempt {0})... gateway.connector.coder.connection.failed=Failed to connect +gateway.connector.coder.setup-command.failed=Failed to set up gateway.connector.coder.connecting.failed.retry=Failed to connect...retrying {0} gateway.connector.settings.data-directory.title=Data directory gateway.connector.settings.data-directory.comment=Directories are created \ diff --git a/src/test/kotlin/com/coder/gateway/util/SetupCommandTest.kt b/src/test/kotlin/com/coder/gateway/util/SetupCommandTest.kt index 62f4fa7b8..b237925b4 100644 --- a/src/test/kotlin/com/coder/gateway/util/SetupCommandTest.kt +++ b/src/test/kotlin/com/coder/gateway/util/SetupCommandTest.kt @@ -1,6 +1,7 @@ package com.coder.gateway.util import com.coder.gateway.CoderRemoteConnectionHandle.Companion.processSetupCommand +import com.coder.gateway.CoderSetupCommandException import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows import kotlin.test.assertEquals @@ -11,7 +12,7 @@ internal class SetupCommandTest { fun executionErrors() { assertEquals( "Execution error", - assertThrows { + assertThrows { processSetupCommand(false) { throw Exception("Execution error") } }.message ) @@ -22,7 +23,7 @@ internal class SetupCommandTest { fun setupScriptError() { assertEquals( "Your IDE is expired, please update", - assertThrows { + assertThrows { processSetupCommand(false) { """ execution line 1 From c0b6fea68e5919f1da31d4db538fb225dad416d5 Mon Sep 17 00:00:00 2001 From: Kirill Kalishev Date: Tue, 18 Feb 2025 19:45:17 -0500 Subject: [PATCH 4/5] better title --- src/main/resources/messages/CoderGatewayBundle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/messages/CoderGatewayBundle.properties b/src/main/resources/messages/CoderGatewayBundle.properties index b392c3983..f318012e0 100644 --- a/src/main/resources/messages/CoderGatewayBundle.properties +++ b/src/main/resources/messages/CoderGatewayBundle.properties @@ -49,7 +49,7 @@ gateway.connector.coder.connection.provider.title=Connecting to Coder workspace. gateway.connector.coder.connecting=Connecting... gateway.connector.coder.connecting.retry=Connecting (attempt {0})... gateway.connector.coder.connection.failed=Failed to connect -gateway.connector.coder.setup-command.failed=Failed to set up +gateway.connector.coder.setup-command.failed=Failed to set up backend IDE gateway.connector.coder.connecting.failed.retry=Failed to connect...retrying {0} gateway.connector.settings.data-directory.title=Data directory gateway.connector.settings.data-directory.comment=Directories are created \ From acc6a7f519fffbab9213ec02f0c1bf9203dda625 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Thu, 20 Feb 2025 16:23:31 -0600 Subject: [PATCH 5/5] changelog update --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a14be9e3e..72a54920c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ ## Unreleased +### Added + +- Added functionality to show setup script error message to the end user. + ### Fixed - Fix bug where wildcard configs would not be written under certain conditions. pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy