From 1788632f203f6044c78aaf28e80b9ba5a3822e58 Mon Sep 17 00:00:00 2001 From: Faur Ioan-Aurel Date: Sat, 22 Mar 2025 00:50:31 +0200 Subject: [PATCH 01/21] impl: custom ssh config header block for toolbox plugin - useful to make differentiate between Gateway plugin and Toolbox plugin --- src/main/kotlin/com/coder/toolbox/cli/CoderCLIManager.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/com/coder/toolbox/cli/CoderCLIManager.kt b/src/main/kotlin/com/coder/toolbox/cli/CoderCLIManager.kt index ecebb44..10841ce 100644 --- a/src/main/kotlin/com/coder/toolbox/cli/CoderCLIManager.kt +++ b/src/main/kotlin/com/coder/toolbox/cli/CoderCLIManager.kt @@ -251,8 +251,8 @@ class CoderCLIManager( feats: Features, ): String? { val host = deploymentURL.safeHost() - val startBlock = "# --- START CODER JETBRAINS $host" - val endBlock = "# --- END CODER JETBRAINS $host" + val startBlock = "# --- START CODER TOOLBOX $host" + val endBlock = "# --- END CODER TOOLBOX $host" val isRemoving = workspaceNames.isEmpty() val baseArgs = listOfNotNull( From 2a86abe0457da132d057816c6f7ee3aea048f561 Mon Sep 17 00:00:00 2001 From: Faur Ioan-Aurel Date: Sat, 22 Mar 2025 00:53:18 +0200 Subject: [PATCH 02/21] impl: report workspace usage under toolbox --- src/main/kotlin/com/coder/toolbox/cli/CoderCLIManager.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/com/coder/toolbox/cli/CoderCLIManager.kt b/src/main/kotlin/com/coder/toolbox/cli/CoderCLIManager.kt index 10841ce..a676dab 100644 --- a/src/main/kotlin/com/coder/toolbox/cli/CoderCLIManager.kt +++ b/src/main/kotlin/com/coder/toolbox/cli/CoderCLIManager.kt @@ -273,7 +273,7 @@ class CoderCLIManager( val proxyArgs = baseArgs + listOfNotNull( if (settings.sshLogDirectory.isNotBlank()) "--log-dir" else null, if (settings.sshLogDirectory.isNotBlank()) escape(settings.sshLogDirectory) else null, - if (feats.reportWorkspaceUsage) "--usage-app=jetbrains" else null, + if (feats.reportWorkspaceUsage) "--usage-app=toolbox" else null, ) val backgroundProxyArgs = baseArgs + listOfNotNull(if (feats.reportWorkspaceUsage) "--usage-app=disable" else null) From 270fa890a5d442ecfd2b9096847af1e934459016 Mon Sep 17 00:00:00 2001 From: Faur Ioan-Aurel Date: Sat, 22 Mar 2025 01:08:53 +0200 Subject: [PATCH 03/21] impl: change hostname in ssh config to reflect toolbox plugin --- src/main/kotlin/com/coder/toolbox/cli/CoderCLIManager.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/com/coder/toolbox/cli/CoderCLIManager.kt b/src/main/kotlin/com/coder/toolbox/cli/CoderCLIManager.kt index a676dab..ec049da 100644 --- a/src/main/kotlin/com/coder/toolbox/cli/CoderCLIManager.kt +++ b/src/main/kotlin/com/coder/toolbox/cli/CoderCLIManager.kt @@ -484,7 +484,7 @@ class CoderCLIManager( fun getHostName( url: URL, workspaceName: String, - ): String = "coder-jetbrains--$workspaceName--${url.safeHost()}" + ): String = "coder-toolbox--$workspaceName--${url.safeHost()}" @JvmStatic fun getBackgroundHostName( From d3968ae708652c4641ab1b21ecbdc4c0ddf6635c Mon Sep 17 00:00:00 2001 From: Faur Ioan-Aurel Date: Sat, 22 Mar 2025 01:14:52 +0200 Subject: [PATCH 04/21] fix: update tests with to reflect the changes to the ssh parameters --- .../fixtures/inputs/existing-end-no-newline.conf | 4 ++-- .../resources/fixtures/inputs/existing-end.conf | 4 ++-- .../inputs/existing-middle-and-unrelated.conf | 8 ++++---- .../fixtures/inputs/existing-middle.conf | 4 ++-- .../resources/fixtures/inputs/existing-only.conf | 4 ++-- .../fixtures/inputs/existing-start.conf | 4 ++-- .../inputs/malformed-mismatched-start.conf | 4 ++-- .../fixtures/inputs/malformed-no-end.conf | 2 +- .../fixtures/inputs/malformed-no-start.conf | 2 +- .../inputs/malformed-start-after-end.conf | 4 ++-- .../fixtures/inputs/no-related-blocks.conf | 4 ++-- .../fixtures/outputs/append-blank-newlines.conf | 10 +++++----- .../resources/fixtures/outputs/append-blank.conf | 10 +++++----- .../fixtures/outputs/append-no-blocks.conf | 10 +++++----- .../fixtures/outputs/append-no-newline.conf | 10 +++++----- .../outputs/append-no-related-blocks.conf | 14 +++++++------- .../fixtures/outputs/disable-autostart.conf | 10 +++++----- .../resources/fixtures/outputs/extra-config.conf | 10 +++++----- .../fixtures/outputs/header-command-windows.conf | 10 +++++----- .../fixtures/outputs/header-command.conf | 10 +++++----- src/test/resources/fixtures/outputs/log-dir.conf | 10 +++++----- .../fixtures/outputs/multiple-workspaces.conf | 16 ++++++++-------- .../fixtures/outputs/no-disable-autostart.conf | 10 +++++----- .../fixtures/outputs/no-report-usage.conf | 8 ++++---- .../fixtures/outputs/replace-end-no-newline.conf | 10 +++++----- .../resources/fixtures/outputs/replace-end.conf | 10 +++++----- .../outputs/replace-middle-ignore-unrelated.conf | 14 +++++++------- .../fixtures/outputs/replace-middle.conf | 10 +++++----- .../resources/fixtures/outputs/replace-only.conf | 10 +++++----- .../fixtures/outputs/replace-start.conf | 10 +++++----- 30 files changed, 123 insertions(+), 123 deletions(-) diff --git a/src/test/resources/fixtures/inputs/existing-end-no-newline.conf b/src/test/resources/fixtures/inputs/existing-end-no-newline.conf index 28a545f..840ee3c 100644 --- a/src/test/resources/fixtures/inputs/existing-end-no-newline.conf +++ b/src/test/resources/fixtures/inputs/existing-end-no-newline.conf @@ -1,5 +1,5 @@ Host test Port 80 Host test2 - Port 443 # --- START CODER JETBRAINS test.coder.invalid -some jetbrains config # --- END CODER JETBRAINS test.coder.invalid + Port 443 # --- START CODER TOOLBOX test.coder.invalid +some jetbrains config # --- END CODER TOOLBOX test.coder.invalid diff --git a/src/test/resources/fixtures/inputs/existing-end.conf b/src/test/resources/fixtures/inputs/existing-end.conf index 9383789..703ed3b 100644 --- a/src/test/resources/fixtures/inputs/existing-end.conf +++ b/src/test/resources/fixtures/inputs/existing-end.conf @@ -2,6 +2,6 @@ Host test Port 80 Host test2 Port 443 -# --- START CODER JETBRAINS test.coder.invalid +# --- START CODER TOOLBOX test.coder.invalid some jetbrains config -# --- END CODER JETBRAINS test.coder.invalid +# --- END CODER TOOLBOX test.coder.invalid diff --git a/src/test/resources/fixtures/inputs/existing-middle-and-unrelated.conf b/src/test/resources/fixtures/inputs/existing-middle-and-unrelated.conf index 297d688..4abc90a 100644 --- a/src/test/resources/fixtures/inputs/existing-middle-and-unrelated.conf +++ b/src/test/resources/fixtures/inputs/existing-middle-and-unrelated.conf @@ -3,11 +3,11 @@ Host test # ------------START-CODER----------- some coder config # ------------END-CODER------------ -# --- START CODER JETBRAINS test.coder.invalid +# --- START CODER TOOLBOX test.coder.invalid some jetbrains config -# --- END CODER JETBRAINS test.coder.invalid +# --- END CODER TOOLBOX test.coder.invalid Host test2 Port 443 -# --- START CODER JETBRAINS test.coder.unrelated +# --- START CODER TOOLBOX test.coder.unrelated some jetbrains config -# --- END CODER JETBRAINS test.coder.unrelated +# --- END CODER TOOLBOX test.coder.unrelated diff --git a/src/test/resources/fixtures/inputs/existing-middle.conf b/src/test/resources/fixtures/inputs/existing-middle.conf index 90b0555..e8553a4 100644 --- a/src/test/resources/fixtures/inputs/existing-middle.conf +++ b/src/test/resources/fixtures/inputs/existing-middle.conf @@ -1,7 +1,7 @@ Host test Port 80 -# --- START CODER JETBRAINS test.coder.invalid +# --- START CODER TOOLBOX test.coder.invalid some jetbrains config -# --- END CODER JETBRAINS test.coder.invalid +# --- END CODER TOOLBOX test.coder.invalid Host test2 Port 443 diff --git a/src/test/resources/fixtures/inputs/existing-only.conf b/src/test/resources/fixtures/inputs/existing-only.conf index 0e960a2..463bbb3 100644 --- a/src/test/resources/fixtures/inputs/existing-only.conf +++ b/src/test/resources/fixtures/inputs/existing-only.conf @@ -1,3 +1,3 @@ -# --- START CODER JETBRAINS test.coder.invalid +# --- START CODER TOOLBOX test.coder.invalid some jetbrains config -# --- END CODER JETBRAINS test.coder.invalid +# --- END CODER TOOLBOX test.coder.invalid diff --git a/src/test/resources/fixtures/inputs/existing-start.conf b/src/test/resources/fixtures/inputs/existing-start.conf index 0cf1159..e490498 100644 --- a/src/test/resources/fixtures/inputs/existing-start.conf +++ b/src/test/resources/fixtures/inputs/existing-start.conf @@ -1,6 +1,6 @@ -# --- START CODER JETBRAINS test.coder.invalid +# --- START CODER TOOLBOX test.coder.invalid some jetbrains config -# --- END CODER JETBRAINS test.coder.invalid +# --- END CODER TOOLBOX test.coder.invalid Host test Port 80 Host test2 diff --git a/src/test/resources/fixtures/inputs/malformed-mismatched-start.conf b/src/test/resources/fixtures/inputs/malformed-mismatched-start.conf index 7631e64..3dc130c 100644 --- a/src/test/resources/fixtures/inputs/malformed-mismatched-start.conf +++ b/src/test/resources/fixtures/inputs/malformed-mismatched-start.conf @@ -1,3 +1,3 @@ -# --- START CODER JETBRAINS test.coder.something-else +# --- START CODER TOOLBOX test.coder.something-else some jetbrains config -# --- END CODER JETBRAINS test.coder.invalid +# --- END CODER TOOLBOX test.coder.invalid diff --git a/src/test/resources/fixtures/inputs/malformed-no-end.conf b/src/test/resources/fixtures/inputs/malformed-no-end.conf index dbcd97e..a65bb36 100644 --- a/src/test/resources/fixtures/inputs/malformed-no-end.conf +++ b/src/test/resources/fixtures/inputs/malformed-no-end.conf @@ -1,2 +1,2 @@ -# --- START CODER JETBRAINS test.coder.invalid +# --- START CODER TOOLBOX test.coder.invalid some jetbrains config diff --git a/src/test/resources/fixtures/inputs/malformed-no-start.conf b/src/test/resources/fixtures/inputs/malformed-no-start.conf index ba6c18f..690d627 100644 --- a/src/test/resources/fixtures/inputs/malformed-no-start.conf +++ b/src/test/resources/fixtures/inputs/malformed-no-start.conf @@ -1,2 +1,2 @@ some jetbrains config -# --- END CODER JETBRAINS test.coder.invalid +# --- END CODER TOOLBOX test.coder.invalid diff --git a/src/test/resources/fixtures/inputs/malformed-start-after-end.conf b/src/test/resources/fixtures/inputs/malformed-start-after-end.conf index e9f411c..28fb5d2 100644 --- a/src/test/resources/fixtures/inputs/malformed-start-after-end.conf +++ b/src/test/resources/fixtures/inputs/malformed-start-after-end.conf @@ -1,3 +1,3 @@ -# --- END CODER JETBRAINS test.coder.invalid +# --- END CODER TOOLBOX test.coder.invalid some jetbrains config -# --- START CODER JETBRAINS test.coder.invalid +# --- START CODER TOOLBOX test.coder.invalid diff --git a/src/test/resources/fixtures/inputs/no-related-blocks.conf b/src/test/resources/fixtures/inputs/no-related-blocks.conf index 34b2b59..59e6766 100644 --- a/src/test/resources/fixtures/inputs/no-related-blocks.conf +++ b/src/test/resources/fixtures/inputs/no-related-blocks.conf @@ -5,6 +5,6 @@ some coder config # ------------END-CODER------------ Host test2 Port 443 -# --- START CODER JETBRAINS test.coder.unrelated +# --- START CODER TOOLBOX test.coder.unrelated some jetbrains config -# --- END CODER JETBRAINS test.coder.unrelated +# --- END CODER TOOLBOX test.coder.unrelated diff --git a/src/test/resources/fixtures/outputs/append-blank-newlines.conf b/src/test/resources/fixtures/outputs/append-blank-newlines.conf index bc0fb6d..073c3a0 100644 --- a/src/test/resources/fixtures/outputs/append-blank-newlines.conf +++ b/src/test/resources/fixtures/outputs/append-blank-newlines.conf @@ -2,19 +2,19 @@ -# --- START CODER JETBRAINS test.coder.invalid -Host coder-jetbrains--foo-bar--test.coder.invalid - ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=jetbrains foo-bar +# --- START CODER TOOLBOX test.coder.invalid +Host coder-toolbox--foo-bar--test.coder.invalid + ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=toolbox foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-jetbrains--foo-bar--test.coder.invalid--bg +Host coder-toolbox--foo-bar--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=disable foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -# --- END CODER JETBRAINS test.coder.invalid +# --- END CODER TOOLBOX test.coder.invalid diff --git a/src/test/resources/fixtures/outputs/append-blank.conf b/src/test/resources/fixtures/outputs/append-blank.conf index fce1a66..5a2098e 100644 --- a/src/test/resources/fixtures/outputs/append-blank.conf +++ b/src/test/resources/fixtures/outputs/append-blank.conf @@ -1,16 +1,16 @@ -# --- START CODER JETBRAINS test.coder.invalid -Host coder-jetbrains--foo-bar--test.coder.invalid - ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=jetbrains foo-bar +# --- START CODER TOOLBOX test.coder.invalid +Host coder-toolbox--foo-bar--test.coder.invalid + ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=toolbox foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-jetbrains--foo-bar--test.coder.invalid--bg +Host coder-toolbox--foo-bar--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=disable foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -# --- END CODER JETBRAINS test.coder.invalid +# --- END CODER TOOLBOX test.coder.invalid diff --git a/src/test/resources/fixtures/outputs/append-no-blocks.conf b/src/test/resources/fixtures/outputs/append-no-blocks.conf index b62b8f3..3f6f64b 100644 --- a/src/test/resources/fixtures/outputs/append-no-blocks.conf +++ b/src/test/resources/fixtures/outputs/append-no-blocks.conf @@ -3,19 +3,19 @@ Host test Host test2 Port 443 -# --- START CODER JETBRAINS test.coder.invalid -Host coder-jetbrains--foo-bar--test.coder.invalid - ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=jetbrains foo-bar +# --- START CODER TOOLBOX test.coder.invalid +Host coder-toolbox--foo-bar--test.coder.invalid + ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=toolbox foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-jetbrains--foo-bar--test.coder.invalid--bg +Host coder-toolbox--foo-bar--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=disable foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -# --- END CODER JETBRAINS test.coder.invalid +# --- END CODER TOOLBOX test.coder.invalid diff --git a/src/test/resources/fixtures/outputs/append-no-newline.conf b/src/test/resources/fixtures/outputs/append-no-newline.conf index 0457f71..ce20ae6 100644 --- a/src/test/resources/fixtures/outputs/append-no-newline.conf +++ b/src/test/resources/fixtures/outputs/append-no-newline.conf @@ -2,19 +2,19 @@ Host test Port 80 Host test2 Port 443 -# --- START CODER JETBRAINS test.coder.invalid -Host coder-jetbrains--foo-bar--test.coder.invalid - ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=jetbrains foo-bar +# --- START CODER TOOLBOX test.coder.invalid +Host coder-toolbox--foo-bar--test.coder.invalid + ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=toolbox foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-jetbrains--foo-bar--test.coder.invalid--bg +Host coder-toolbox--foo-bar--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=disable foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -# --- END CODER JETBRAINS test.coder.invalid +# --- END CODER TOOLBOX test.coder.invalid diff --git a/src/test/resources/fixtures/outputs/append-no-related-blocks.conf b/src/test/resources/fixtures/outputs/append-no-related-blocks.conf index a7fdf4c..8c2a3f8 100644 --- a/src/test/resources/fixtures/outputs/append-no-related-blocks.conf +++ b/src/test/resources/fixtures/outputs/append-no-related-blocks.conf @@ -5,23 +5,23 @@ some coder config # ------------END-CODER------------ Host test2 Port 443 -# --- START CODER JETBRAINS test.coder.unrelated +# --- START CODER TOOLBOX test.coder.unrelated some jetbrains config -# --- END CODER JETBRAINS test.coder.unrelated +# --- END CODER TOOLBOX test.coder.unrelated -# --- START CODER JETBRAINS test.coder.invalid -Host coder-jetbrains--foo-bar--test.coder.invalid - ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=jetbrains foo-bar +# --- START CODER TOOLBOX test.coder.invalid +Host coder-toolbox--foo-bar--test.coder.invalid + ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=toolbox foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-jetbrains--foo-bar--test.coder.invalid--bg +Host coder-toolbox--foo-bar--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=disable foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -# --- END CODER JETBRAINS test.coder.invalid +# --- END CODER TOOLBOX test.coder.invalid diff --git a/src/test/resources/fixtures/outputs/disable-autostart.conf b/src/test/resources/fixtures/outputs/disable-autostart.conf index 575fdc4..aeb5906 100644 --- a/src/test/resources/fixtures/outputs/disable-autostart.conf +++ b/src/test/resources/fixtures/outputs/disable-autostart.conf @@ -1,16 +1,16 @@ -# --- START CODER JETBRAINS test.coder.invalid -Host coder-jetbrains--foo--test.coder.invalid - ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --disable-autostart --usage-app=jetbrains foo +# --- START CODER TOOLBOX test.coder.invalid +Host coder-toolbox--foo--test.coder.invalid + ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --disable-autostart --usage-app=toolbox foo ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-jetbrains--foo--test.coder.invalid--bg +Host coder-toolbox--foo--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --disable-autostart --usage-app=disable foo ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -# --- END CODER JETBRAINS test.coder.invalid +# --- END CODER TOOLBOX test.coder.invalid diff --git a/src/test/resources/fixtures/outputs/extra-config.conf b/src/test/resources/fixtures/outputs/extra-config.conf index cc5eb1d..d0bb726 100644 --- a/src/test/resources/fixtures/outputs/extra-config.conf +++ b/src/test/resources/fixtures/outputs/extra-config.conf @@ -1,6 +1,6 @@ -# --- START CODER JETBRAINS test.coder.invalid -Host coder-jetbrains--extra--test.coder.invalid - ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=jetbrains extra +# --- START CODER TOOLBOX test.coder.invalid +Host coder-toolbox--extra--test.coder.invalid + ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=toolbox extra ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null @@ -8,7 +8,7 @@ Host coder-jetbrains--extra--test.coder.invalid SetEnv CODER_SSH_SESSION_TYPE=JetBrains ServerAliveInterval 5 ServerAliveCountMax 3 -Host coder-jetbrains--extra--test.coder.invalid--bg +Host coder-toolbox--extra--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=disable extra ConnectTimeout 0 StrictHostKeyChecking no @@ -17,4 +17,4 @@ Host coder-jetbrains--extra--test.coder.invalid--bg SetEnv CODER_SSH_SESSION_TYPE=JetBrains ServerAliveInterval 5 ServerAliveCountMax 3 -# --- END CODER JETBRAINS test.coder.invalid +# --- END CODER TOOLBOX test.coder.invalid diff --git a/src/test/resources/fixtures/outputs/header-command-windows.conf b/src/test/resources/fixtures/outputs/header-command-windows.conf index f9c2714..b151de8 100644 --- a/src/test/resources/fixtures/outputs/header-command-windows.conf +++ b/src/test/resources/fixtures/outputs/header-command-windows.conf @@ -1,16 +1,16 @@ -# --- START CODER JETBRAINS test.coder.invalid -Host coder-jetbrains--header--test.coder.invalid - ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid --header-command "\"C:\Program Files\My Header Command\HeaderCommand.exe\" --url=\"%%CODER_URL%%\" --test=\"foo bar\"" ssh --stdio --usage-app=jetbrains header +# --- START CODER TOOLBOX test.coder.invalid +Host coder-toolbox--header--test.coder.invalid + ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid --header-command "\"C:\Program Files\My Header Command\HeaderCommand.exe\" --url=\"%%CODER_URL%%\" --test=\"foo bar\"" ssh --stdio --usage-app=toolbox header ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-jetbrains--header--test.coder.invalid--bg +Host coder-toolbox--header--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid --header-command "\"C:\Program Files\My Header Command\HeaderCommand.exe\" --url=\"%%CODER_URL%%\" --test=\"foo bar\"" ssh --stdio --usage-app=disable header ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -# --- END CODER JETBRAINS test.coder.invalid +# --- END CODER TOOLBOX test.coder.invalid diff --git a/src/test/resources/fixtures/outputs/header-command.conf b/src/test/resources/fixtures/outputs/header-command.conf index de24f71..2502baa 100644 --- a/src/test/resources/fixtures/outputs/header-command.conf +++ b/src/test/resources/fixtures/outputs/header-command.conf @@ -1,16 +1,16 @@ -# --- START CODER JETBRAINS test.coder.invalid -Host coder-jetbrains--header--test.coder.invalid - ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid --header-command 'my-header-command --url="$CODER_URL" --test="foo bar" --literal='\''$CODER_URL'\''' ssh --stdio --usage-app=jetbrains header +# --- START CODER TOOLBOX test.coder.invalid +Host coder-toolbox--header--test.coder.invalid + ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid --header-command 'my-header-command --url="$CODER_URL" --test="foo bar" --literal='\''$CODER_URL'\''' ssh --stdio --usage-app=toolbox header ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-jetbrains--header--test.coder.invalid--bg +Host coder-toolbox--header--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid --header-command 'my-header-command --url="$CODER_URL" --test="foo bar" --literal='\''$CODER_URL'\''' ssh --stdio --usage-app=disable header ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -# --- END CODER JETBRAINS test.coder.invalid +# --- END CODER TOOLBOX test.coder.invalid diff --git a/src/test/resources/fixtures/outputs/log-dir.conf b/src/test/resources/fixtures/outputs/log-dir.conf index 233e0f3..40f9c05 100644 --- a/src/test/resources/fixtures/outputs/log-dir.conf +++ b/src/test/resources/fixtures/outputs/log-dir.conf @@ -1,16 +1,16 @@ -# --- START CODER JETBRAINS test.coder.invalid -Host coder-jetbrains--foo--test.coder.invalid - ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --log-dir /tmp/coder-toolbox/test.coder.invalid/logs --usage-app=jetbrains foo +# --- START CODER TOOLBOX test.coder.invalid +Host coder-toolbox--foo--test.coder.invalid + ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --log-dir /tmp/coder-toolbox/test.coder.invalid/logs --usage-app=toolbox foo ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-jetbrains--foo--test.coder.invalid--bg +Host coder-toolbox--foo--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=disable foo ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -# --- END CODER JETBRAINS test.coder.invalid +# --- END CODER TOOLBOX test.coder.invalid diff --git a/src/test/resources/fixtures/outputs/multiple-workspaces.conf b/src/test/resources/fixtures/outputs/multiple-workspaces.conf index aeba6d6..789d37d 100644 --- a/src/test/resources/fixtures/outputs/multiple-workspaces.conf +++ b/src/test/resources/fixtures/outputs/multiple-workspaces.conf @@ -1,30 +1,30 @@ -# --- START CODER JETBRAINS test.coder.invalid -Host coder-jetbrains--foo--test.coder.invalid - ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=jetbrains foo +# --- START CODER TOOLBOX test.coder.invalid +Host coder-toolbox--foo--test.coder.invalid + ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=toolbox foo ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-jetbrains--foo--test.coder.invalid--bg +Host coder-toolbox--foo--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=disable foo ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-jetbrains--bar--test.coder.invalid - ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=jetbrains bar +Host coder-toolbox--bar--test.coder.invalid + ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=toolbox bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-jetbrains--bar--test.coder.invalid--bg +Host coder-toolbox--bar--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=disable bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -# --- END CODER JETBRAINS test.coder.invalid +# --- END CODER TOOLBOX test.coder.invalid diff --git a/src/test/resources/fixtures/outputs/no-disable-autostart.conf b/src/test/resources/fixtures/outputs/no-disable-autostart.conf index c9039f6..75a2a57 100644 --- a/src/test/resources/fixtures/outputs/no-disable-autostart.conf +++ b/src/test/resources/fixtures/outputs/no-disable-autostart.conf @@ -1,16 +1,16 @@ -# --- START CODER JETBRAINS test.coder.invalid -Host coder-jetbrains--foo--test.coder.invalid - ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=jetbrains foo +# --- START CODER TOOLBOX test.coder.invalid +Host coder-toolbox--foo--test.coder.invalid + ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=toolbox foo ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-jetbrains--foo--test.coder.invalid--bg +Host coder-toolbox--foo--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=disable foo ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -# --- END CODER JETBRAINS test.coder.invalid +# --- END CODER TOOLBOX test.coder.invalid diff --git a/src/test/resources/fixtures/outputs/no-report-usage.conf b/src/test/resources/fixtures/outputs/no-report-usage.conf index 0f89b24..016dea3 100644 --- a/src/test/resources/fixtures/outputs/no-report-usage.conf +++ b/src/test/resources/fixtures/outputs/no-report-usage.conf @@ -1,16 +1,16 @@ -# --- START CODER JETBRAINS test.coder.invalid -Host coder-jetbrains--foo--test.coder.invalid +# --- START CODER TOOLBOX test.coder.invalid +Host coder-toolbox--foo--test.coder.invalid ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio foo ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-jetbrains--foo--test.coder.invalid--bg +Host coder-toolbox--foo--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio foo ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -# --- END CODER JETBRAINS test.coder.invalid +# --- END CODER TOOLBOX test.coder.invalid diff --git a/src/test/resources/fixtures/outputs/replace-end-no-newline.conf b/src/test/resources/fixtures/outputs/replace-end-no-newline.conf index ffb69fc..26ea726 100644 --- a/src/test/resources/fixtures/outputs/replace-end-no-newline.conf +++ b/src/test/resources/fixtures/outputs/replace-end-no-newline.conf @@ -1,19 +1,19 @@ Host test Port 80 Host test2 - Port 443 # --- START CODER JETBRAINS test.coder.invalid -Host coder-jetbrains--foo-bar--test.coder.invalid - ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=jetbrains foo-bar + Port 443 # --- START CODER TOOLBOX test.coder.invalid +Host coder-toolbox--foo-bar--test.coder.invalid + ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=toolbox foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-jetbrains--foo-bar--test.coder.invalid--bg +Host coder-toolbox--foo-bar--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=disable foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -# --- END CODER JETBRAINS test.coder.invalid +# --- END CODER TOOLBOX test.coder.invalid diff --git a/src/test/resources/fixtures/outputs/replace-end.conf b/src/test/resources/fixtures/outputs/replace-end.conf index 0457f71..ce20ae6 100644 --- a/src/test/resources/fixtures/outputs/replace-end.conf +++ b/src/test/resources/fixtures/outputs/replace-end.conf @@ -2,19 +2,19 @@ Host test Port 80 Host test2 Port 443 -# --- START CODER JETBRAINS test.coder.invalid -Host coder-jetbrains--foo-bar--test.coder.invalid - ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=jetbrains foo-bar +# --- START CODER TOOLBOX test.coder.invalid +Host coder-toolbox--foo-bar--test.coder.invalid + ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=toolbox foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-jetbrains--foo-bar--test.coder.invalid--bg +Host coder-toolbox--foo-bar--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=disable foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -# --- END CODER JETBRAINS test.coder.invalid +# --- END CODER TOOLBOX test.coder.invalid diff --git a/src/test/resources/fixtures/outputs/replace-middle-ignore-unrelated.conf b/src/test/resources/fixtures/outputs/replace-middle-ignore-unrelated.conf index 10b8e58..efb1eac 100644 --- a/src/test/resources/fixtures/outputs/replace-middle-ignore-unrelated.conf +++ b/src/test/resources/fixtures/outputs/replace-middle-ignore-unrelated.conf @@ -3,24 +3,24 @@ Host test # ------------START-CODER----------- some coder config # ------------END-CODER------------ -# --- START CODER JETBRAINS test.coder.invalid -Host coder-jetbrains--foo-bar--test.coder.invalid - ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=jetbrains foo-bar +# --- START CODER TOOLBOX test.coder.invalid +Host coder-toolbox--foo-bar--test.coder.invalid + ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=toolbox foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-jetbrains--foo-bar--test.coder.invalid--bg +Host coder-toolbox--foo-bar--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=disable foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -# --- END CODER JETBRAINS test.coder.invalid +# --- END CODER TOOLBOX test.coder.invalid Host test2 Port 443 -# --- START CODER JETBRAINS test.coder.unrelated +# --- START CODER TOOLBOX test.coder.unrelated some jetbrains config -# --- END CODER JETBRAINS test.coder.unrelated +# --- END CODER TOOLBOX test.coder.unrelated diff --git a/src/test/resources/fixtures/outputs/replace-middle.conf b/src/test/resources/fixtures/outputs/replace-middle.conf index d06d640..28668ee 100644 --- a/src/test/resources/fixtures/outputs/replace-middle.conf +++ b/src/test/resources/fixtures/outputs/replace-middle.conf @@ -1,20 +1,20 @@ Host test Port 80 -# --- START CODER JETBRAINS test.coder.invalid -Host coder-jetbrains--foo-bar--test.coder.invalid - ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=jetbrains foo-bar +# --- START CODER TOOLBOX test.coder.invalid +Host coder-toolbox--foo-bar--test.coder.invalid + ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=toolbox foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-jetbrains--foo-bar--test.coder.invalid--bg +Host coder-toolbox--foo-bar--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=disable foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -# --- END CODER JETBRAINS test.coder.invalid +# --- END CODER TOOLBOX test.coder.invalid Host test2 Port 443 diff --git a/src/test/resources/fixtures/outputs/replace-only.conf b/src/test/resources/fixtures/outputs/replace-only.conf index fce1a66..5a2098e 100644 --- a/src/test/resources/fixtures/outputs/replace-only.conf +++ b/src/test/resources/fixtures/outputs/replace-only.conf @@ -1,16 +1,16 @@ -# --- START CODER JETBRAINS test.coder.invalid -Host coder-jetbrains--foo-bar--test.coder.invalid - ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=jetbrains foo-bar +# --- START CODER TOOLBOX test.coder.invalid +Host coder-toolbox--foo-bar--test.coder.invalid + ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=toolbox foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-jetbrains--foo-bar--test.coder.invalid--bg +Host coder-toolbox--foo-bar--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=disable foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -# --- END CODER JETBRAINS test.coder.invalid +# --- END CODER TOOLBOX test.coder.invalid diff --git a/src/test/resources/fixtures/outputs/replace-start.conf b/src/test/resources/fixtures/outputs/replace-start.conf index 61508f0..14fafe3 100644 --- a/src/test/resources/fixtures/outputs/replace-start.conf +++ b/src/test/resources/fixtures/outputs/replace-start.conf @@ -1,19 +1,19 @@ -# --- START CODER JETBRAINS test.coder.invalid -Host coder-jetbrains--foo-bar--test.coder.invalid - ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=jetbrains foo-bar +# --- START CODER TOOLBOX test.coder.invalid +Host coder-toolbox--foo-bar--test.coder.invalid + ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=toolbox foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-jetbrains--foo-bar--test.coder.invalid--bg +Host coder-toolbox--foo-bar--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=disable foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -# --- END CODER JETBRAINS test.coder.invalid +# --- END CODER TOOLBOX test.coder.invalid Host test Port 80 Host test2 From 6a3491d42cbfa3eb610f646c7f76c1a1a6aa4aa5 Mon Sep 17 00:00:00 2001 From: Faur Ioan-Aurel Date: Mon, 24 Mar 2025 22:40:56 +0200 Subject: [PATCH 05/21] fix: ssh header block as `JETBRAINS TOOLBOX` --- .../kotlin/com/coder/toolbox/cli/CoderCLIManager.kt | 6 +++--- .../fixtures/inputs/existing-end-no-newline.conf | 4 ++-- src/test/resources/fixtures/inputs/existing-end.conf | 4 ++-- .../inputs/existing-middle-and-unrelated.conf | 8 ++++---- .../resources/fixtures/inputs/existing-middle.conf | 4 ++-- .../resources/fixtures/inputs/existing-only.conf | 4 ++-- .../resources/fixtures/inputs/existing-start.conf | 4 ++-- .../fixtures/inputs/malformed-mismatched-start.conf | 4 ++-- .../resources/fixtures/inputs/malformed-no-end.conf | 2 +- .../fixtures/inputs/malformed-no-start.conf | 2 +- .../fixtures/inputs/malformed-start-after-end.conf | 4 ++-- .../resources/fixtures/inputs/no-related-blocks.conf | 4 ++-- .../fixtures/outputs/append-blank-newlines.conf | 8 ++++---- .../resources/fixtures/outputs/append-blank.conf | 8 ++++---- .../resources/fixtures/outputs/append-no-blocks.conf | 8 ++++---- .../fixtures/outputs/append-no-newline.conf | 8 ++++---- .../fixtures/outputs/append-no-related-blocks.conf | 12 ++++++------ .../fixtures/outputs/disable-autostart.conf | 8 ++++---- .../resources/fixtures/outputs/extra-config.conf | 8 ++++---- .../fixtures/outputs/header-command-windows.conf | 8 ++++---- .../resources/fixtures/outputs/header-command.conf | 8 ++++---- src/test/resources/fixtures/outputs/log-dir.conf | 8 ++++---- .../fixtures/outputs/multiple-workspaces.conf | 12 ++++++------ .../fixtures/outputs/no-disable-autostart.conf | 8 ++++---- .../resources/fixtures/outputs/no-report-usage.conf | 8 ++++---- .../fixtures/outputs/replace-end-no-newline.conf | 8 ++++---- src/test/resources/fixtures/outputs/replace-end.conf | 8 ++++---- .../outputs/replace-middle-ignore-unrelated.conf | 12 ++++++------ .../resources/fixtures/outputs/replace-middle.conf | 8 ++++---- .../resources/fixtures/outputs/replace-only.conf | 8 ++++---- .../resources/fixtures/outputs/replace-start.conf | 8 ++++---- 31 files changed, 107 insertions(+), 107 deletions(-) diff --git a/src/main/kotlin/com/coder/toolbox/cli/CoderCLIManager.kt b/src/main/kotlin/com/coder/toolbox/cli/CoderCLIManager.kt index ec049da..98cd3bd 100644 --- a/src/main/kotlin/com/coder/toolbox/cli/CoderCLIManager.kt +++ b/src/main/kotlin/com/coder/toolbox/cli/CoderCLIManager.kt @@ -251,8 +251,8 @@ class CoderCLIManager( feats: Features, ): String? { val host = deploymentURL.safeHost() - val startBlock = "# --- START CODER TOOLBOX $host" - val endBlock = "# --- END CODER TOOLBOX $host" + val startBlock = "# --- START CODER JETBRAINS TOOLBOX $host" + val endBlock = "# --- END CODER JETBRAINS TOOLBOX $host" val isRemoving = workspaceNames.isEmpty() val baseArgs = listOfNotNull( @@ -484,7 +484,7 @@ class CoderCLIManager( fun getHostName( url: URL, workspaceName: String, - ): String = "coder-toolbox--$workspaceName--${url.safeHost()}" + ): String = "coder-jetbrains-toolbox--$workspaceName--${url.safeHost()}" @JvmStatic fun getBackgroundHostName( diff --git a/src/test/resources/fixtures/inputs/existing-end-no-newline.conf b/src/test/resources/fixtures/inputs/existing-end-no-newline.conf index 840ee3c..44617b5 100644 --- a/src/test/resources/fixtures/inputs/existing-end-no-newline.conf +++ b/src/test/resources/fixtures/inputs/existing-end-no-newline.conf @@ -1,5 +1,5 @@ Host test Port 80 Host test2 - Port 443 # --- START CODER TOOLBOX test.coder.invalid -some jetbrains config # --- END CODER TOOLBOX test.coder.invalid + Port 443 # --- START CODER JETBRAINS TOOLBOX test.coder.invalid +some jetbrains config # --- END CODER JETBRAINS TOOLBOX test.coder.invalid diff --git a/src/test/resources/fixtures/inputs/existing-end.conf b/src/test/resources/fixtures/inputs/existing-end.conf index 703ed3b..b363962 100644 --- a/src/test/resources/fixtures/inputs/existing-end.conf +++ b/src/test/resources/fixtures/inputs/existing-end.conf @@ -2,6 +2,6 @@ Host test Port 80 Host test2 Port 443 -# --- START CODER TOOLBOX test.coder.invalid +# --- START CODER JETBRAINS TOOLBOX test.coder.invalid some jetbrains config -# --- END CODER TOOLBOX test.coder.invalid +# --- END CODER JETBRAINS TOOLBOX test.coder.invalid diff --git a/src/test/resources/fixtures/inputs/existing-middle-and-unrelated.conf b/src/test/resources/fixtures/inputs/existing-middle-and-unrelated.conf index 4abc90a..8e185d9 100644 --- a/src/test/resources/fixtures/inputs/existing-middle-and-unrelated.conf +++ b/src/test/resources/fixtures/inputs/existing-middle-and-unrelated.conf @@ -3,11 +3,11 @@ Host test # ------------START-CODER----------- some coder config # ------------END-CODER------------ -# --- START CODER TOOLBOX test.coder.invalid +# --- START CODER JETBRAINS TOOLBOX test.coder.invalid some jetbrains config -# --- END CODER TOOLBOX test.coder.invalid +# --- END CODER JETBRAINS TOOLBOX test.coder.invalid Host test2 Port 443 -# --- START CODER TOOLBOX test.coder.unrelated +# --- START CODER JETBRAINS TOOLBOX test.coder.unrelated some jetbrains config -# --- END CODER TOOLBOX test.coder.unrelated +# --- END CODER JETBRAINS TOOLBOX test.coder.unrelated diff --git a/src/test/resources/fixtures/inputs/existing-middle.conf b/src/test/resources/fixtures/inputs/existing-middle.conf index e8553a4..131b099 100644 --- a/src/test/resources/fixtures/inputs/existing-middle.conf +++ b/src/test/resources/fixtures/inputs/existing-middle.conf @@ -1,7 +1,7 @@ Host test Port 80 -# --- START CODER TOOLBOX test.coder.invalid +# --- START CODER JETBRAINS TOOLBOX test.coder.invalid some jetbrains config -# --- END CODER TOOLBOX test.coder.invalid +# --- END CODER JETBRAINS TOOLBOX test.coder.invalid Host test2 Port 443 diff --git a/src/test/resources/fixtures/inputs/existing-only.conf b/src/test/resources/fixtures/inputs/existing-only.conf index 463bbb3..192f569 100644 --- a/src/test/resources/fixtures/inputs/existing-only.conf +++ b/src/test/resources/fixtures/inputs/existing-only.conf @@ -1,3 +1,3 @@ -# --- START CODER TOOLBOX test.coder.invalid +# --- START CODER JETBRAINS TOOLBOX test.coder.invalid some jetbrains config -# --- END CODER TOOLBOX test.coder.invalid +# --- END CODER JETBRAINS TOOLBOX test.coder.invalid diff --git a/src/test/resources/fixtures/inputs/existing-start.conf b/src/test/resources/fixtures/inputs/existing-start.conf index e490498..ae8a9e7 100644 --- a/src/test/resources/fixtures/inputs/existing-start.conf +++ b/src/test/resources/fixtures/inputs/existing-start.conf @@ -1,6 +1,6 @@ -# --- START CODER TOOLBOX test.coder.invalid +# --- START CODER JETBRAINS TOOLBOX test.coder.invalid some jetbrains config -# --- END CODER TOOLBOX test.coder.invalid +# --- END CODER JETBRAINS TOOLBOX test.coder.invalid Host test Port 80 Host test2 diff --git a/src/test/resources/fixtures/inputs/malformed-mismatched-start.conf b/src/test/resources/fixtures/inputs/malformed-mismatched-start.conf index 3dc130c..45e535a 100644 --- a/src/test/resources/fixtures/inputs/malformed-mismatched-start.conf +++ b/src/test/resources/fixtures/inputs/malformed-mismatched-start.conf @@ -1,3 +1,3 @@ -# --- START CODER TOOLBOX test.coder.something-else +# --- START CODER JETBRAINS TOOLBOX test.coder.something-else some jetbrains config -# --- END CODER TOOLBOX test.coder.invalid +# --- END CODER JETBRAINS TOOLBOX test.coder.invalid diff --git a/src/test/resources/fixtures/inputs/malformed-no-end.conf b/src/test/resources/fixtures/inputs/malformed-no-end.conf index a65bb36..f6e2f7a 100644 --- a/src/test/resources/fixtures/inputs/malformed-no-end.conf +++ b/src/test/resources/fixtures/inputs/malformed-no-end.conf @@ -1,2 +1,2 @@ -# --- START CODER TOOLBOX test.coder.invalid +# --- START CODER JETBRAINS TOOLBOX test.coder.invalid some jetbrains config diff --git a/src/test/resources/fixtures/inputs/malformed-no-start.conf b/src/test/resources/fixtures/inputs/malformed-no-start.conf index 690d627..8fb7a76 100644 --- a/src/test/resources/fixtures/inputs/malformed-no-start.conf +++ b/src/test/resources/fixtures/inputs/malformed-no-start.conf @@ -1,2 +1,2 @@ some jetbrains config -# --- END CODER TOOLBOX test.coder.invalid +# --- END CODER JETBRAINS TOOLBOX test.coder.invalid diff --git a/src/test/resources/fixtures/inputs/malformed-start-after-end.conf b/src/test/resources/fixtures/inputs/malformed-start-after-end.conf index 28fb5d2..66cc352 100644 --- a/src/test/resources/fixtures/inputs/malformed-start-after-end.conf +++ b/src/test/resources/fixtures/inputs/malformed-start-after-end.conf @@ -1,3 +1,3 @@ -# --- END CODER TOOLBOX test.coder.invalid +# --- END CODER JETBRAINS TOOLBOX test.coder.invalid some jetbrains config -# --- START CODER TOOLBOX test.coder.invalid +# --- START CODER JETBRAINS TOOLBOX test.coder.invalid diff --git a/src/test/resources/fixtures/inputs/no-related-blocks.conf b/src/test/resources/fixtures/inputs/no-related-blocks.conf index 59e6766..c0d80a6 100644 --- a/src/test/resources/fixtures/inputs/no-related-blocks.conf +++ b/src/test/resources/fixtures/inputs/no-related-blocks.conf @@ -5,6 +5,6 @@ some coder config # ------------END-CODER------------ Host test2 Port 443 -# --- START CODER TOOLBOX test.coder.unrelated +# --- START CODER JETBRAINS TOOLBOX test.coder.unrelated some jetbrains config -# --- END CODER TOOLBOX test.coder.unrelated +# --- END CODER JETBRAINS TOOLBOX test.coder.unrelated diff --git a/src/test/resources/fixtures/outputs/append-blank-newlines.conf b/src/test/resources/fixtures/outputs/append-blank-newlines.conf index 073c3a0..0adbc15 100644 --- a/src/test/resources/fixtures/outputs/append-blank-newlines.conf +++ b/src/test/resources/fixtures/outputs/append-blank-newlines.conf @@ -2,19 +2,19 @@ -# --- START CODER TOOLBOX test.coder.invalid -Host coder-toolbox--foo-bar--test.coder.invalid +# --- START CODER JETBRAINS TOOLBOX test.coder.invalid +Host coder-jetbrains-toolbox--foo-bar--test.coder.invalid ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=toolbox foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-toolbox--foo-bar--test.coder.invalid--bg +Host coder-jetbrains-toolbox--foo-bar--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=disable foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -# --- END CODER TOOLBOX test.coder.invalid +# --- END CODER JETBRAINS TOOLBOX test.coder.invalid diff --git a/src/test/resources/fixtures/outputs/append-blank.conf b/src/test/resources/fixtures/outputs/append-blank.conf index 5a2098e..7cea0dc 100644 --- a/src/test/resources/fixtures/outputs/append-blank.conf +++ b/src/test/resources/fixtures/outputs/append-blank.conf @@ -1,16 +1,16 @@ -# --- START CODER TOOLBOX test.coder.invalid -Host coder-toolbox--foo-bar--test.coder.invalid +# --- START CODER JETBRAINS TOOLBOX test.coder.invalid +Host coder-jetbrains-toolbox--foo-bar--test.coder.invalid ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=toolbox foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-toolbox--foo-bar--test.coder.invalid--bg +Host coder-jetbrains-toolbox--foo-bar--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=disable foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -# --- END CODER TOOLBOX test.coder.invalid +# --- END CODER JETBRAINS TOOLBOX test.coder.invalid diff --git a/src/test/resources/fixtures/outputs/append-no-blocks.conf b/src/test/resources/fixtures/outputs/append-no-blocks.conf index 3f6f64b..3fd2205 100644 --- a/src/test/resources/fixtures/outputs/append-no-blocks.conf +++ b/src/test/resources/fixtures/outputs/append-no-blocks.conf @@ -3,19 +3,19 @@ Host test Host test2 Port 443 -# --- START CODER TOOLBOX test.coder.invalid -Host coder-toolbox--foo-bar--test.coder.invalid +# --- START CODER JETBRAINS TOOLBOX test.coder.invalid +Host coder-jetbrains-toolbox--foo-bar--test.coder.invalid ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=toolbox foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-toolbox--foo-bar--test.coder.invalid--bg +Host coder-jetbrains-toolbox--foo-bar--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=disable foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -# --- END CODER TOOLBOX test.coder.invalid +# --- END CODER JETBRAINS TOOLBOX test.coder.invalid diff --git a/src/test/resources/fixtures/outputs/append-no-newline.conf b/src/test/resources/fixtures/outputs/append-no-newline.conf index ce20ae6..47b12d7 100644 --- a/src/test/resources/fixtures/outputs/append-no-newline.conf +++ b/src/test/resources/fixtures/outputs/append-no-newline.conf @@ -2,19 +2,19 @@ Host test Port 80 Host test2 Port 443 -# --- START CODER TOOLBOX test.coder.invalid -Host coder-toolbox--foo-bar--test.coder.invalid +# --- START CODER JETBRAINS TOOLBOX test.coder.invalid +Host coder-jetbrains-toolbox--foo-bar--test.coder.invalid ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=toolbox foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-toolbox--foo-bar--test.coder.invalid--bg +Host coder-jetbrains-toolbox--foo-bar--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=disable foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -# --- END CODER TOOLBOX test.coder.invalid +# --- END CODER JETBRAINS TOOLBOX test.coder.invalid diff --git a/src/test/resources/fixtures/outputs/append-no-related-blocks.conf b/src/test/resources/fixtures/outputs/append-no-related-blocks.conf index 8c2a3f8..f387826 100644 --- a/src/test/resources/fixtures/outputs/append-no-related-blocks.conf +++ b/src/test/resources/fixtures/outputs/append-no-related-blocks.conf @@ -5,23 +5,23 @@ some coder config # ------------END-CODER------------ Host test2 Port 443 -# --- START CODER TOOLBOX test.coder.unrelated +# --- START CODER JETBRAINS TOOLBOX test.coder.unrelated some jetbrains config -# --- END CODER TOOLBOX test.coder.unrelated +# --- END CODER JETBRAINS TOOLBOX test.coder.unrelated -# --- START CODER TOOLBOX test.coder.invalid -Host coder-toolbox--foo-bar--test.coder.invalid +# --- START CODER JETBRAINS TOOLBOX test.coder.invalid +Host coder-jetbrains-toolbox--foo-bar--test.coder.invalid ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=toolbox foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-toolbox--foo-bar--test.coder.invalid--bg +Host coder-jetbrains-toolbox--foo-bar--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=disable foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -# --- END CODER TOOLBOX test.coder.invalid +# --- END CODER JETBRAINS TOOLBOX test.coder.invalid diff --git a/src/test/resources/fixtures/outputs/disable-autostart.conf b/src/test/resources/fixtures/outputs/disable-autostart.conf index aeb5906..2c2b730 100644 --- a/src/test/resources/fixtures/outputs/disable-autostart.conf +++ b/src/test/resources/fixtures/outputs/disable-autostart.conf @@ -1,16 +1,16 @@ -# --- START CODER TOOLBOX test.coder.invalid -Host coder-toolbox--foo--test.coder.invalid +# --- START CODER JETBRAINS TOOLBOX test.coder.invalid +Host coder-jetbrains-toolbox--foo--test.coder.invalid ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --disable-autostart --usage-app=toolbox foo ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-toolbox--foo--test.coder.invalid--bg +Host coder-jetbrains-toolbox--foo--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --disable-autostart --usage-app=disable foo ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -# --- END CODER TOOLBOX test.coder.invalid +# --- END CODER JETBRAINS TOOLBOX test.coder.invalid diff --git a/src/test/resources/fixtures/outputs/extra-config.conf b/src/test/resources/fixtures/outputs/extra-config.conf index d0bb726..ee0c9b5 100644 --- a/src/test/resources/fixtures/outputs/extra-config.conf +++ b/src/test/resources/fixtures/outputs/extra-config.conf @@ -1,5 +1,5 @@ -# --- START CODER TOOLBOX test.coder.invalid -Host coder-toolbox--extra--test.coder.invalid +# --- START CODER JETBRAINS TOOLBOX test.coder.invalid +Host coder-jetbrains-toolbox--extra--test.coder.invalid ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=toolbox extra ConnectTimeout 0 StrictHostKeyChecking no @@ -8,7 +8,7 @@ Host coder-toolbox--extra--test.coder.invalid SetEnv CODER_SSH_SESSION_TYPE=JetBrains ServerAliveInterval 5 ServerAliveCountMax 3 -Host coder-toolbox--extra--test.coder.invalid--bg +Host coder-jetbrains-toolbox--extra--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=disable extra ConnectTimeout 0 StrictHostKeyChecking no @@ -17,4 +17,4 @@ Host coder-toolbox--extra--test.coder.invalid--bg SetEnv CODER_SSH_SESSION_TYPE=JetBrains ServerAliveInterval 5 ServerAliveCountMax 3 -# --- END CODER TOOLBOX test.coder.invalid +# --- END CODER JETBRAINS TOOLBOX test.coder.invalid diff --git a/src/test/resources/fixtures/outputs/header-command-windows.conf b/src/test/resources/fixtures/outputs/header-command-windows.conf index b151de8..496b577 100644 --- a/src/test/resources/fixtures/outputs/header-command-windows.conf +++ b/src/test/resources/fixtures/outputs/header-command-windows.conf @@ -1,16 +1,16 @@ -# --- START CODER TOOLBOX test.coder.invalid -Host coder-toolbox--header--test.coder.invalid +# --- START CODER JETBRAINS TOOLBOX test.coder.invalid +Host coder-jetbrains-toolbox--header--test.coder.invalid ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid --header-command "\"C:\Program Files\My Header Command\HeaderCommand.exe\" --url=\"%%CODER_URL%%\" --test=\"foo bar\"" ssh --stdio --usage-app=toolbox header ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-toolbox--header--test.coder.invalid--bg +Host coder-jetbrains-toolbox--header--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid --header-command "\"C:\Program Files\My Header Command\HeaderCommand.exe\" --url=\"%%CODER_URL%%\" --test=\"foo bar\"" ssh --stdio --usage-app=disable header ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -# --- END CODER TOOLBOX test.coder.invalid +# --- END CODER JETBRAINS TOOLBOX test.coder.invalid diff --git a/src/test/resources/fixtures/outputs/header-command.conf b/src/test/resources/fixtures/outputs/header-command.conf index 2502baa..fc7e902 100644 --- a/src/test/resources/fixtures/outputs/header-command.conf +++ b/src/test/resources/fixtures/outputs/header-command.conf @@ -1,16 +1,16 @@ -# --- START CODER TOOLBOX test.coder.invalid -Host coder-toolbox--header--test.coder.invalid +# --- START CODER JETBRAINS TOOLBOX test.coder.invalid +Host coder-jetbrains-toolbox--header--test.coder.invalid ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid --header-command 'my-header-command --url="$CODER_URL" --test="foo bar" --literal='\''$CODER_URL'\''' ssh --stdio --usage-app=toolbox header ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-toolbox--header--test.coder.invalid--bg +Host coder-jetbrains-toolbox--header--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid --header-command 'my-header-command --url="$CODER_URL" --test="foo bar" --literal='\''$CODER_URL'\''' ssh --stdio --usage-app=disable header ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -# --- END CODER TOOLBOX test.coder.invalid +# --- END CODER JETBRAINS TOOLBOX test.coder.invalid diff --git a/src/test/resources/fixtures/outputs/log-dir.conf b/src/test/resources/fixtures/outputs/log-dir.conf index 40f9c05..8da54ef 100644 --- a/src/test/resources/fixtures/outputs/log-dir.conf +++ b/src/test/resources/fixtures/outputs/log-dir.conf @@ -1,16 +1,16 @@ -# --- START CODER TOOLBOX test.coder.invalid -Host coder-toolbox--foo--test.coder.invalid +# --- START CODER JETBRAINS TOOLBOX test.coder.invalid +Host coder-jetbrains-toolbox--foo--test.coder.invalid ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --log-dir /tmp/coder-toolbox/test.coder.invalid/logs --usage-app=toolbox foo ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-toolbox--foo--test.coder.invalid--bg +Host coder-jetbrains-toolbox--foo--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=disable foo ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -# --- END CODER TOOLBOX test.coder.invalid +# --- END CODER JETBRAINS TOOLBOX test.coder.invalid diff --git a/src/test/resources/fixtures/outputs/multiple-workspaces.conf b/src/test/resources/fixtures/outputs/multiple-workspaces.conf index 789d37d..9cd883b 100644 --- a/src/test/resources/fixtures/outputs/multiple-workspaces.conf +++ b/src/test/resources/fixtures/outputs/multiple-workspaces.conf @@ -1,30 +1,30 @@ -# --- START CODER TOOLBOX test.coder.invalid -Host coder-toolbox--foo--test.coder.invalid +# --- START CODER JETBRAINS TOOLBOX test.coder.invalid +Host coder-jetbrains-toolbox--foo--test.coder.invalid ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=toolbox foo ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-toolbox--foo--test.coder.invalid--bg +Host coder-jetbrains-toolbox--foo--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=disable foo ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-toolbox--bar--test.coder.invalid +Host coder-jetbrains-toolbox--bar--test.coder.invalid ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=toolbox bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-toolbox--bar--test.coder.invalid--bg +Host coder-jetbrains-toolbox--bar--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=disable bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -# --- END CODER TOOLBOX test.coder.invalid +# --- END CODER JETBRAINS TOOLBOX test.coder.invalid diff --git a/src/test/resources/fixtures/outputs/no-disable-autostart.conf b/src/test/resources/fixtures/outputs/no-disable-autostart.conf index 75a2a57..4bcc966 100644 --- a/src/test/resources/fixtures/outputs/no-disable-autostart.conf +++ b/src/test/resources/fixtures/outputs/no-disable-autostart.conf @@ -1,16 +1,16 @@ -# --- START CODER TOOLBOX test.coder.invalid -Host coder-toolbox--foo--test.coder.invalid +# --- START CODER JETBRAINS TOOLBOX test.coder.invalid +Host coder-jetbrains-toolbox--foo--test.coder.invalid ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=toolbox foo ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-toolbox--foo--test.coder.invalid--bg +Host coder-jetbrains-toolbox--foo--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=disable foo ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -# --- END CODER TOOLBOX test.coder.invalid +# --- END CODER JETBRAINS TOOLBOX test.coder.invalid diff --git a/src/test/resources/fixtures/outputs/no-report-usage.conf b/src/test/resources/fixtures/outputs/no-report-usage.conf index 016dea3..2c03d91 100644 --- a/src/test/resources/fixtures/outputs/no-report-usage.conf +++ b/src/test/resources/fixtures/outputs/no-report-usage.conf @@ -1,16 +1,16 @@ -# --- START CODER TOOLBOX test.coder.invalid -Host coder-toolbox--foo--test.coder.invalid +# --- START CODER JETBRAINS TOOLBOX test.coder.invalid +Host coder-jetbrains-toolbox--foo--test.coder.invalid ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio foo ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-toolbox--foo--test.coder.invalid--bg +Host coder-jetbrains-toolbox--foo--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio foo ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -# --- END CODER TOOLBOX test.coder.invalid +# --- END CODER JETBRAINS TOOLBOX test.coder.invalid diff --git a/src/test/resources/fixtures/outputs/replace-end-no-newline.conf b/src/test/resources/fixtures/outputs/replace-end-no-newline.conf index 26ea726..132a79f 100644 --- a/src/test/resources/fixtures/outputs/replace-end-no-newline.conf +++ b/src/test/resources/fixtures/outputs/replace-end-no-newline.conf @@ -1,19 +1,19 @@ Host test Port 80 Host test2 - Port 443 # --- START CODER TOOLBOX test.coder.invalid -Host coder-toolbox--foo-bar--test.coder.invalid + Port 443 # --- START CODER JETBRAINS TOOLBOX test.coder.invalid +Host coder-jetbrains-toolbox--foo-bar--test.coder.invalid ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=toolbox foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-toolbox--foo-bar--test.coder.invalid--bg +Host coder-jetbrains-toolbox--foo-bar--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=disable foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -# --- END CODER TOOLBOX test.coder.invalid +# --- END CODER JETBRAINS TOOLBOX test.coder.invalid diff --git a/src/test/resources/fixtures/outputs/replace-end.conf b/src/test/resources/fixtures/outputs/replace-end.conf index ce20ae6..47b12d7 100644 --- a/src/test/resources/fixtures/outputs/replace-end.conf +++ b/src/test/resources/fixtures/outputs/replace-end.conf @@ -2,19 +2,19 @@ Host test Port 80 Host test2 Port 443 -# --- START CODER TOOLBOX test.coder.invalid -Host coder-toolbox--foo-bar--test.coder.invalid +# --- START CODER JETBRAINS TOOLBOX test.coder.invalid +Host coder-jetbrains-toolbox--foo-bar--test.coder.invalid ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=toolbox foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-toolbox--foo-bar--test.coder.invalid--bg +Host coder-jetbrains-toolbox--foo-bar--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=disable foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -# --- END CODER TOOLBOX test.coder.invalid +# --- END CODER JETBRAINS TOOLBOX test.coder.invalid diff --git a/src/test/resources/fixtures/outputs/replace-middle-ignore-unrelated.conf b/src/test/resources/fixtures/outputs/replace-middle-ignore-unrelated.conf index efb1eac..b5838c3 100644 --- a/src/test/resources/fixtures/outputs/replace-middle-ignore-unrelated.conf +++ b/src/test/resources/fixtures/outputs/replace-middle-ignore-unrelated.conf @@ -3,24 +3,24 @@ Host test # ------------START-CODER----------- some coder config # ------------END-CODER------------ -# --- START CODER TOOLBOX test.coder.invalid -Host coder-toolbox--foo-bar--test.coder.invalid +# --- START CODER JETBRAINS TOOLBOX test.coder.invalid +Host coder-jetbrains-toolbox--foo-bar--test.coder.invalid ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=toolbox foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-toolbox--foo-bar--test.coder.invalid--bg +Host coder-jetbrains-toolbox--foo-bar--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=disable foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -# --- END CODER TOOLBOX test.coder.invalid +# --- END CODER JETBRAINS TOOLBOX test.coder.invalid Host test2 Port 443 -# --- START CODER TOOLBOX test.coder.unrelated +# --- START CODER JETBRAINS TOOLBOX test.coder.unrelated some jetbrains config -# --- END CODER TOOLBOX test.coder.unrelated +# --- END CODER JETBRAINS TOOLBOX test.coder.unrelated diff --git a/src/test/resources/fixtures/outputs/replace-middle.conf b/src/test/resources/fixtures/outputs/replace-middle.conf index 28668ee..2b5ba5a 100644 --- a/src/test/resources/fixtures/outputs/replace-middle.conf +++ b/src/test/resources/fixtures/outputs/replace-middle.conf @@ -1,20 +1,20 @@ Host test Port 80 -# --- START CODER TOOLBOX test.coder.invalid -Host coder-toolbox--foo-bar--test.coder.invalid +# --- START CODER JETBRAINS TOOLBOX test.coder.invalid +Host coder-jetbrains-toolbox--foo-bar--test.coder.invalid ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=toolbox foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-toolbox--foo-bar--test.coder.invalid--bg +Host coder-jetbrains-toolbox--foo-bar--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=disable foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -# --- END CODER TOOLBOX test.coder.invalid +# --- END CODER JETBRAINS TOOLBOX test.coder.invalid Host test2 Port 443 diff --git a/src/test/resources/fixtures/outputs/replace-only.conf b/src/test/resources/fixtures/outputs/replace-only.conf index 5a2098e..7cea0dc 100644 --- a/src/test/resources/fixtures/outputs/replace-only.conf +++ b/src/test/resources/fixtures/outputs/replace-only.conf @@ -1,16 +1,16 @@ -# --- START CODER TOOLBOX test.coder.invalid -Host coder-toolbox--foo-bar--test.coder.invalid +# --- START CODER JETBRAINS TOOLBOX test.coder.invalid +Host coder-jetbrains-toolbox--foo-bar--test.coder.invalid ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=toolbox foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-toolbox--foo-bar--test.coder.invalid--bg +Host coder-jetbrains-toolbox--foo-bar--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=disable foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -# --- END CODER TOOLBOX test.coder.invalid +# --- END CODER JETBRAINS TOOLBOX test.coder.invalid diff --git a/src/test/resources/fixtures/outputs/replace-start.conf b/src/test/resources/fixtures/outputs/replace-start.conf index 14fafe3..52505f5 100644 --- a/src/test/resources/fixtures/outputs/replace-start.conf +++ b/src/test/resources/fixtures/outputs/replace-start.conf @@ -1,19 +1,19 @@ -# --- START CODER TOOLBOX test.coder.invalid -Host coder-toolbox--foo-bar--test.coder.invalid +# --- START CODER JETBRAINS TOOLBOX test.coder.invalid +Host coder-jetbrains-toolbox--foo-bar--test.coder.invalid ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=toolbox foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-toolbox--foo-bar--test.coder.invalid--bg +Host coder-jetbrains-toolbox--foo-bar--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=disable foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -# --- END CODER TOOLBOX test.coder.invalid +# --- END CODER JETBRAINS TOOLBOX test.coder.invalid Host test Port 80 Host test2 From 3a8738c8dc71eefdf35cc8bc02814f64ba2238eb Mon Sep 17 00:00:00 2001 From: Faur Ioan-Aurel Date: Tue, 25 Mar 2025 00:35:12 +0200 Subject: [PATCH 06/21] refactor: expose typed settings and secrets store - instead of the raw interfaces - this prepares the plugin for the next stage where some of the settings are read by the URI handling component. --- .../com/coder/toolbox/CoderRemoteProvider.kt | 28 ++++----- .../com/coder/toolbox/CoderToolboxContext.kt | 8 +-- .../coder/toolbox/CoderToolboxExtension.kt | 6 +- .../coder/toolbox/views/CoderSettingsPage.kt | 58 ++++++++++--------- .../coder/toolbox/cli/CoderCLIManagerTest.kt | 8 +-- .../coder/toolbox/sdk/CoderRestClientTest.kt | 8 +-- 6 files changed, 59 insertions(+), 57 deletions(-) diff --git a/src/main/kotlin/com/coder/toolbox/CoderRemoteProvider.kt b/src/main/kotlin/com/coder/toolbox/CoderRemoteProvider.kt index 21586e3..0fdd169 100644 --- a/src/main/kotlin/com/coder/toolbox/CoderRemoteProvider.kt +++ b/src/main/kotlin/com/coder/toolbox/CoderRemoteProvider.kt @@ -3,8 +3,6 @@ package com.coder.toolbox import com.coder.toolbox.cli.CoderCLIManager import com.coder.toolbox.sdk.CoderRestClient import com.coder.toolbox.sdk.v2.models.WorkspaceStatus -import com.coder.toolbox.services.CoderSecretsService -import com.coder.toolbox.services.CoderSettingsService import com.coder.toolbox.settings.CoderSettings import com.coder.toolbox.settings.Source import com.coder.toolbox.util.CoderProtocolHandler @@ -47,10 +45,8 @@ class CoderRemoteProvider( private var lastEnvironments: Set? = null // Create our services from the Toolbox ones. - private val settingsService = CoderSettingsService(context.settingsStore) - private val settings: CoderSettings = CoderSettings(settingsService, context.logger) - private val secrets: CoderSecretsService = CoderSecretsService(context.secretsStore) - private val settingsPage: CoderSettingsPage = CoderSettingsPage(context, settingsService) + private val settings: CoderSettings = CoderSettings(context.settings, context.logger) + private val settingsPage: CoderSettingsPage = CoderSettingsPage(context) private val dialogUi = DialogUi(context, settings) // The REST client, if we are signed in @@ -151,7 +147,7 @@ class CoderRemoteProvider( private fun logout() { // Keep the URL and token to make it easy to log back in, but set // rememberMe to false so we do not try to automatically log in. - secrets.rememberMe = "false" + context.secrets.rememberMe = "false" close() } @@ -272,8 +268,8 @@ class CoderRemoteProvider( // When coming back to the application, authenticate immediately. val autologin = shouldDoAutoLogin() var autologinEx: Exception? = null - secrets.lastToken.let { lastToken -> - secrets.lastDeploymentURL.let { lastDeploymentURL -> + context.secrets.lastToken.let { lastToken -> + context.secrets.lastDeploymentURL.let { lastDeploymentURL -> if (autologin && lastDeploymentURL.isNotBlank() && (lastToken.isNotBlank() || !settings.requireTokenAuth)) { try { return createConnectPage(URL(lastDeploymentURL), lastToken) @@ -309,7 +305,7 @@ class CoderRemoteProvider( return null } - private fun shouldDoAutoLogin(): Boolean = firstRun && secrets.rememberMe == "true" + private fun shouldDoAutoLogin(): Boolean = firstRun && context.secrets.rememberMe == "true" /** * Create a connect page that starts polling and resets the UI on success. @@ -323,10 +319,10 @@ class CoderRemoteProvider( ::goToEnvironmentsPage, ) { client, cli -> // Store the URL and token for use next time. - secrets.lastDeploymentURL = client.url.toString() - secrets.lastToken = client.token ?: "" + context.secrets.lastDeploymentURL = client.url.toString() + context.secrets.lastToken = client.token ?: "" // Currently we always remember, but this could be made an option. - secrets.rememberMe = "true" + context.secrets.rememberMe = "true" this.client = client pollError = null pollJob?.cancel() @@ -343,8 +339,8 @@ class CoderRemoteProvider( * 2. Token on disk for this deployment. * 3. Global token for Coder, if it matches the deployment. */ - private fun getToken(deploymentURL: URL): Pair? = secrets.lastToken.let { - if (it.isNotBlank() && secrets.lastDeploymentURL == deploymentURL.toString()) { + private fun getToken(deploymentURL: URL): Pair? = context.secrets.lastToken.let { + if (it.isNotBlank() && context.secrets.lastDeploymentURL == deploymentURL.toString()) { it to Source.LAST_USED } else { settings.token(deploymentURL) @@ -361,7 +357,7 @@ class CoderRemoteProvider( * 3. CODER_URL. * 4. URL in global cli config. */ - private fun getDeploymentURL(): Pair? = secrets.lastDeploymentURL.let { + private fun getDeploymentURL(): Pair? = context.secrets.lastDeploymentURL.let { if (it.isNotBlank()) { it to Source.LAST_USED } else { diff --git a/src/main/kotlin/com/coder/toolbox/CoderToolboxContext.kt b/src/main/kotlin/com/coder/toolbox/CoderToolboxContext.kt index 2819595..404b4f9 100644 --- a/src/main/kotlin/com/coder/toolbox/CoderToolboxContext.kt +++ b/src/main/kotlin/com/coder/toolbox/CoderToolboxContext.kt @@ -1,7 +1,7 @@ package com.coder.toolbox -import com.jetbrains.toolbox.api.core.PluginSecretStore -import com.jetbrains.toolbox.api.core.PluginSettingsStore +import com.coder.toolbox.services.CoderSecretsService +import com.coder.toolbox.services.CoderSettingsService import com.jetbrains.toolbox.api.core.diagnostics.Logger import com.jetbrains.toolbox.api.localization.LocalizableStringFactory import com.jetbrains.toolbox.api.remoteDev.connection.ClientHelper @@ -18,6 +18,6 @@ data class CoderToolboxContext( val cs: CoroutineScope, val logger: Logger, val i18n: LocalizableStringFactory, - val settingsStore: PluginSettingsStore, - val secretsStore: PluginSecretStore + val settings: CoderSettingsService, + val secrets: CoderSecretsService ) diff --git a/src/main/kotlin/com/coder/toolbox/CoderToolboxExtension.kt b/src/main/kotlin/com/coder/toolbox/CoderToolboxExtension.kt index 5ef5454..12c08d9 100644 --- a/src/main/kotlin/com/coder/toolbox/CoderToolboxExtension.kt +++ b/src/main/kotlin/com/coder/toolbox/CoderToolboxExtension.kt @@ -1,5 +1,7 @@ package com.coder.toolbox +import com.coder.toolbox.services.CoderSecretsService +import com.coder.toolbox.services.CoderSettingsService import com.jetbrains.toolbox.api.core.PluginSecretStore import com.jetbrains.toolbox.api.core.PluginSettingsStore import com.jetbrains.toolbox.api.core.ServiceLocator @@ -29,8 +31,8 @@ class CoderToolboxExtension : RemoteDevExtension { serviceLocator.getService(CoroutineScope::class.java), serviceLocator.getService(Logger::class.java), serviceLocator.getService(LocalizableStringFactory::class.java), - serviceLocator.getService(PluginSettingsStore::class.java), - serviceLocator.getService(PluginSecretStore::class.java), + CoderSettingsService(serviceLocator.getService(PluginSettingsStore::class.java)), + CoderSecretsService(serviceLocator.getService(PluginSecretStore::class.java)), ), OkHttpClient(), ) diff --git a/src/main/kotlin/com/coder/toolbox/views/CoderSettingsPage.kt b/src/main/kotlin/com/coder/toolbox/views/CoderSettingsPage.kt index be8dafa..b4ce938 100644 --- a/src/main/kotlin/com/coder/toolbox/views/CoderSettingsPage.kt +++ b/src/main/kotlin/com/coder/toolbox/views/CoderSettingsPage.kt @@ -1,7 +1,6 @@ package com.coder.toolbox.views import com.coder.toolbox.CoderToolboxContext -import com.coder.toolbox.services.CoderSettingsService import com.jetbrains.toolbox.api.ui.actions.RunnableActionDescription import com.jetbrains.toolbox.api.ui.components.CheckboxField import com.jetbrains.toolbox.api.ui.components.TextField @@ -17,28 +16,33 @@ import kotlinx.coroutines.flow.StateFlow * TODO@JB: There is no scroll, and our settings do not fit. As a consequence, * I have not been able to test this page. */ -class CoderSettingsPage( - context: CoderToolboxContext, - private val settings: CoderSettingsService, -) : CoderPage(context, context.i18n.ptrl("Coder Settings"), false) { +class CoderSettingsPage(context: CoderToolboxContext) : CoderPage(context, context.i18n.ptrl("Coder Settings"), false) { // TODO: Copy over the descriptions, holding until I can test this page. private val binarySourceField = - TextField(context.i18n.ptrl("Binary source"), settings.binarySource, TextType.General) + TextField(context.i18n.ptrl("Binary source"), context.settings.binarySource, TextType.General) private val binaryDirectoryField = - TextField(context.i18n.ptrl("Binary directory"), settings.binaryDirectory, TextType.General) + TextField(context.i18n.ptrl("Binary directory"), context.settings.binaryDirectory, TextType.General) private val dataDirectoryField = - TextField(context.i18n.ptrl("Data directory"), settings.dataDirectory, TextType.General) - private val enableDownloadsField = CheckboxField(settings.enableDownloads, context.i18n.ptrl("Enable downloads")) + TextField(context.i18n.ptrl("Data directory"), context.settings.dataDirectory, TextType.General) + private val enableDownloadsField = + CheckboxField(context.settings.enableDownloads, context.i18n.ptrl("Enable downloads")) private val enableBinaryDirectoryFallbackField = - CheckboxField(settings.enableBinaryDirectoryFallback, context.i18n.ptrl("Enable binary directory fallback")) + CheckboxField( + context.settings.enableBinaryDirectoryFallback, + context.i18n.ptrl("Enable binary directory fallback") + ) private val headerCommandField = - TextField(context.i18n.ptrl("Header command"), settings.headerCommand, TextType.General) - private val tlsCertPathField = TextField(context.i18n.ptrl("TLS cert path"), settings.tlsCertPath, TextType.General) - private val tlsKeyPathField = TextField(context.i18n.ptrl("TLS key path"), settings.tlsKeyPath, TextType.General) - private val tlsCAPathField = TextField(context.i18n.ptrl("TLS CA path"), settings.tlsCAPath, TextType.General) + TextField(context.i18n.ptrl("Header command"), context.settings.headerCommand, TextType.General) + private val tlsCertPathField = + TextField(context.i18n.ptrl("TLS cert path"), context.settings.tlsCertPath, TextType.General) + private val tlsKeyPathField = + TextField(context.i18n.ptrl("TLS key path"), context.settings.tlsKeyPath, TextType.General) + private val tlsCAPathField = + TextField(context.i18n.ptrl("TLS CA path"), context.settings.tlsCAPath, TextType.General) private val tlsAlternateHostnameField = - TextField(context.i18n.ptrl("TLS alternate hostname"), settings.tlsAlternateHostname, TextType.General) - private val disableAutostartField = CheckboxField(settings.disableAutostart, context.i18n.ptrl("Disable autostart")) + TextField(context.i18n.ptrl("TLS alternate hostname"), context.settings.tlsAlternateHostname, TextType.General) + private val disableAutostartField = + CheckboxField(context.settings.disableAutostart, context.i18n.ptrl("Disable autostart")) override val fields: StateFlow> = MutableStateFlow( listOf( @@ -59,17 +63,17 @@ class CoderSettingsPage( override val actionButtons: StateFlow> = MutableStateFlow( listOf( Action(context.i18n.ptrl("Save"), closesPage = true) { - settings.binarySource = binarySourceField.textState.value - settings.binaryDirectory = binaryDirectoryField.textState.value - settings.dataDirectory = dataDirectoryField.textState.value - settings.enableDownloads = enableDownloadsField.checkedState.value - settings.enableBinaryDirectoryFallback = enableBinaryDirectoryFallbackField.checkedState.value - settings.headerCommand = headerCommandField.textState.value - settings.tlsCertPath = tlsCertPathField.textState.value - settings.tlsKeyPath = tlsKeyPathField.textState.value - settings.tlsCAPath = tlsCAPathField.textState.value - settings.tlsAlternateHostname = tlsAlternateHostnameField.textState.value - settings.disableAutostart = disableAutostartField.checkedState.value + context.settings.binarySource = binarySourceField.textState.value + context.settings.binaryDirectory = binaryDirectoryField.textState.value + context.settings.dataDirectory = dataDirectoryField.textState.value + context.settings.enableDownloads = enableDownloadsField.checkedState.value + context.settings.enableBinaryDirectoryFallback = enableBinaryDirectoryFallbackField.checkedState.value + context.settings.headerCommand = headerCommandField.textState.value + context.settings.tlsCertPath = tlsCertPathField.textState.value + context.settings.tlsKeyPath = tlsKeyPathField.textState.value + context.settings.tlsCAPath = tlsCAPathField.textState.value + context.settings.tlsAlternateHostname = tlsAlternateHostnameField.textState.value + context.settings.disableAutostart = disableAutostartField.checkedState.value }, ) ) diff --git a/src/test/kotlin/com/coder/toolbox/cli/CoderCLIManagerTest.kt b/src/test/kotlin/com/coder/toolbox/cli/CoderCLIManagerTest.kt index 6b7933e..e73190b 100644 --- a/src/test/kotlin/com/coder/toolbox/cli/CoderCLIManagerTest.kt +++ b/src/test/kotlin/com/coder/toolbox/cli/CoderCLIManagerTest.kt @@ -4,6 +4,8 @@ import com.coder.toolbox.CoderToolboxContext import com.coder.toolbox.cli.ex.MissingVersionException import com.coder.toolbox.cli.ex.ResponseException import com.coder.toolbox.cli.ex.SSHConfigFormatException +import com.coder.toolbox.services.CoderSecretsService +import com.coder.toolbox.services.CoderSettingsService import com.coder.toolbox.settings.CODER_SSH_CONFIG_OPTIONS import com.coder.toolbox.settings.CoderSettings import com.coder.toolbox.settings.CoderSettingsState @@ -15,8 +17,6 @@ import com.coder.toolbox.util.escape import com.coder.toolbox.util.getOS import com.coder.toolbox.util.sha1 import com.coder.toolbox.util.toURL -import com.jetbrains.toolbox.api.core.PluginSecretStore -import com.jetbrains.toolbox.api.core.PluginSettingsStore import com.jetbrains.toolbox.api.core.diagnostics.Logger import com.jetbrains.toolbox.api.localization.LocalizableStringFactory import com.jetbrains.toolbox.api.remoteDev.connection.ClientHelper @@ -53,8 +53,8 @@ internal class CoderCLIManagerTest { mockk(), mockk(relaxed = true), mockk(), - mockk(), - mockk() + mockk(), + mockk() ) /** diff --git a/src/test/kotlin/com/coder/toolbox/sdk/CoderRestClientTest.kt b/src/test/kotlin/com/coder/toolbox/sdk/CoderRestClientTest.kt index c4c73fa..a316af6 100644 --- a/src/test/kotlin/com/coder/toolbox/sdk/CoderRestClientTest.kt +++ b/src/test/kotlin/com/coder/toolbox/sdk/CoderRestClientTest.kt @@ -13,11 +13,11 @@ import com.coder.toolbox.sdk.v2.models.WorkspaceBuild import com.coder.toolbox.sdk.v2.models.WorkspaceResource import com.coder.toolbox.sdk.v2.models.WorkspaceTransition import com.coder.toolbox.sdk.v2.models.WorkspacesResponse +import com.coder.toolbox.services.CoderSecretsService +import com.coder.toolbox.services.CoderSettingsService import com.coder.toolbox.settings.CoderSettings import com.coder.toolbox.settings.CoderSettingsState import com.coder.toolbox.util.sslContextFromPEMs -import com.jetbrains.toolbox.api.core.PluginSecretStore -import com.jetbrains.toolbox.api.core.PluginSettingsStore import com.jetbrains.toolbox.api.core.diagnostics.Logger import com.jetbrains.toolbox.api.localization.LocalizableStringFactory import com.jetbrains.toolbox.api.remoteDev.connection.ClientHelper @@ -100,8 +100,8 @@ class CoderRestClientTest { mockk(), mockk(relaxed = true), mockk(), - mockk(), - mockk() + mockk(), + mockk() ) data class TestWorkspace(var workspace: Workspace, var resources: List? = emptyList()) From 1da3f4b6672eae9f795aa68933a78a29755d4f0a Mon Sep 17 00:00:00 2001 From: Faur Ioan-Aurel Date: Tue, 25 Mar 2025 00:38:30 +0200 Subject: [PATCH 07/21] impl: don't start the workspace automatically if disable-autostart is enabled - uri handling always tried to start the workspace if it was stopped. - this commit honors the `disable autostart` setting which should stop the starting of the workspace automatically when connecting via SSH. - instead user is warned via a pop-up dialog that manual starting is needed --- .../com/coder/toolbox/util/CoderProtocolHandler.kt | 13 +++++++++++++ src/main/resources/localization/defaultMessages.po | 3 +++ 2 files changed, 16 insertions(+) diff --git a/src/main/kotlin/com/coder/toolbox/util/CoderProtocolHandler.kt b/src/main/kotlin/com/coder/toolbox/util/CoderProtocolHandler.kt index 77969e8..a884cc6 100644 --- a/src/main/kotlin/com/coder/toolbox/util/CoderProtocolHandler.kt +++ b/src/main/kotlin/com/coder/toolbox/util/CoderProtocolHandler.kt @@ -95,6 +95,19 @@ open class CoderProtocolHandler( WorkspaceStatus.STOPPING, WorkspaceStatus.STOPPED, WorkspaceStatus.CANCELING, WorkspaceStatus.CANCELED -> { + if (context.settings.disableAutostart) { + context.ui.showWindow() + context.envPageManager.showPluginEnvironmentsPage(true) + + context.logger.warn("$workspaceName from $deploymentURL is not started and autostart is disabled.") + context.ui.showInfoPopup( + context.i18n.pnotr("$workspaceName is not running"), + context.i18n.ptrl("Can't handle URI because workspace is not running and autostart is disabled. Please start the workspace manually and execute the URI again."), + context.i18n.ptrl("OK") + ) + return + } + restClient.startWorkspace(workspace) if (restClient.waitForReady(workspace) != true) { context.logger.error("$workspaceName from $deploymentURL could not be started on time") diff --git a/src/main/resources/localization/defaultMessages.po b/src/main/resources/localization/defaultMessages.po index aa96e05..9572682 100644 --- a/src/main/resources/localization/defaultMessages.po +++ b/src/main/resources/localization/defaultMessages.po @@ -146,4 +146,7 @@ msgid "Get a token" msgstr "" msgid "Connect" +msgstr "" + +msgid "Can't handle URI because workspace is not running and autostart is disabled. Please start the workspace manually and execute the URI again." msgstr "" \ No newline at end of file From a104721cd686613da5398bd4350dc6820d5813e5 Mon Sep 17 00:00:00 2001 From: Faur Ioan-Aurel Date: Tue, 25 Mar 2025 00:56:16 +0200 Subject: [PATCH 08/21] fix: force window to show when error dialogs pops-up - this patch makes the window visible in uri handling flows when an error dialog pops-up. --- .../toolbox/util/CoderProtocolHandler.kt | 47 +++++++++++-------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/src/main/kotlin/com/coder/toolbox/util/CoderProtocolHandler.kt b/src/main/kotlin/com/coder/toolbox/util/CoderProtocolHandler.kt index a884cc6..d6d2917 100644 --- a/src/main/kotlin/com/coder/toolbox/util/CoderProtocolHandler.kt +++ b/src/main/kotlin/com/coder/toolbox/util/CoderProtocolHandler.kt @@ -10,6 +10,7 @@ import com.coder.toolbox.sdk.v2.models.Workspace import com.coder.toolbox.sdk.v2.models.WorkspaceAgent import com.coder.toolbox.sdk.v2.models.WorkspaceStatus import com.coder.toolbox.settings.CoderSettings +import com.jetbrains.toolbox.api.localization.LocalizableString import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.delay import kotlinx.coroutines.flow.StateFlow @@ -48,7 +49,7 @@ open class CoderProtocolHandler( val deploymentURL = params.url() ?: askUrl() if (deploymentURL.isNullOrBlank()) { context.logger.error("Query parameter \"$URL\" is missing from URI $uri") - context.ui.showErrorInfoPopup(MissingArgumentException("Can't handle URI because query parameter \"$URL\" is missing")) + context.showErrorPopup(MissingArgumentException("Can't handle URI because query parameter \"$URL\" is missing")) return } @@ -57,15 +58,7 @@ open class CoderProtocolHandler( authenticate(deploymentURL, queryToken) } catch (ex: Exception) { context.logger.error(ex, "Query parameter \"$TOKEN\" is missing from URI $uri") - context.ui.showErrorInfoPopup( - IllegalStateException( - humanizeConnectionError( - deploymentURL.toURL(), - true, - ex - ) - ) - ) + context.showErrorPopup(IllegalStateException(humanizeConnectionError(deploymentURL.toURL(), true, ex))) return } @@ -73,7 +66,7 @@ open class CoderProtocolHandler( val workspaceName = params.workspace() if (workspaceName.isNullOrBlank()) { context.logger.error("Query parameter \"$WORKSPACE\" is missing from URI $uri") - context.ui.showErrorInfoPopup(MissingArgumentException("Can't handle URI because query parameter \"$WORKSPACE\" is missing")) + context.showErrorPopup(MissingArgumentException("Can't handle URI because query parameter \"$WORKSPACE\" is missing")) return } @@ -81,7 +74,7 @@ open class CoderProtocolHandler( val workspace = workspaces.firstOrNull { it.name == workspaceName } if (workspace == null) { context.logger.error("There is no workspace with name $workspaceName on $deploymentURL") - context.ui.showErrorInfoPopup(MissingArgumentException("Can't handle URI because workspace with name $workspaceName does not exist")) + context.showErrorPopup(MissingArgumentException("Can't handle URI because workspace with name $workspaceName does not exist")) return } @@ -89,18 +82,15 @@ open class CoderProtocolHandler( WorkspaceStatus.PENDING, WorkspaceStatus.STARTING -> if (restClient.waitForReady(workspace) != true) { context.logger.error("$workspaceName from $deploymentURL could not be ready on time") - context.ui.showErrorInfoPopup(MissingArgumentException("Can't handle URI because workspace $workspaceName could not be ready on time")) + context.showErrorPopup(MissingArgumentException("Can't handle URI because workspace $workspaceName could not be ready on time")) return } WorkspaceStatus.STOPPING, WorkspaceStatus.STOPPED, WorkspaceStatus.CANCELING, WorkspaceStatus.CANCELED -> { if (context.settings.disableAutostart) { - context.ui.showWindow() - context.envPageManager.showPluginEnvironmentsPage(true) - context.logger.warn("$workspaceName from $deploymentURL is not started and autostart is disabled.") - context.ui.showInfoPopup( + context.showInfoPopup( context.i18n.pnotr("$workspaceName is not running"), context.i18n.ptrl("Can't handle URI because workspace is not running and autostart is disabled. Please start the workspace manually and execute the URI again."), context.i18n.ptrl("OK") @@ -111,14 +101,14 @@ open class CoderProtocolHandler( restClient.startWorkspace(workspace) if (restClient.waitForReady(workspace) != true) { context.logger.error("$workspaceName from $deploymentURL could not be started on time") - context.ui.showErrorInfoPopup(MissingArgumentException("Can't handle URI because workspace $workspaceName could not be started on time")) + context.showErrorPopup(MissingArgumentException("Can't handle URI because workspace $workspaceName could not be started on time")) return } } WorkspaceStatus.FAILED, WorkspaceStatus.DELETING, WorkspaceStatus.DELETED -> { context.logger.error("Unable to connect to $workspaceName from $deploymentURL") - context.ui.showErrorInfoPopup(MissingArgumentException("Can't handle URI because because we're unable to connect to workspace $workspaceName")) + context.showErrorPopup(MissingArgumentException("Can't handle URI because because we're unable to connect to workspace $workspaceName")) return } @@ -325,6 +315,25 @@ internal fun getMatchingAgent( return agent } +private suspend fun CoderToolboxContext.showErrorPopup(error: Throwable) { + popupPluginMainPage() + this.ui.showErrorInfoPopup(error) +} + +private suspend fun CoderToolboxContext.showInfoPopup( + title: LocalizableString, + message: LocalizableString, + okLabel: LocalizableString +) { + popupPluginMainPage() + this.ui.showInfoPopup(title, message, okLabel) +} + +private fun CoderToolboxContext.popupPluginMainPage() { + this.ui.showWindow() + this.envPageManager.showPluginEnvironmentsPage(true) +} + /** * Suspends the coroutine until first true value is received. */ From d885c605b38b4b13112d4b0f8cd4fdb459f26046 Mon Sep 17 00:00:00 2001 From: Faur Ioan-Aurel Date: Tue, 25 Mar 2025 01:02:57 +0200 Subject: [PATCH 09/21] fix: resiliency when REST call to start the workspace fail - catches the exception, logs the errors and pops-up an error dialog during URI handling - previously the call failed silently (for example when the workspace is stopped and out of date) --- .../com/coder/toolbox/util/CoderProtocolHandler.kt | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/com/coder/toolbox/util/CoderProtocolHandler.kt b/src/main/kotlin/com/coder/toolbox/util/CoderProtocolHandler.kt index d6d2917..1361990 100644 --- a/src/main/kotlin/com/coder/toolbox/util/CoderProtocolHandler.kt +++ b/src/main/kotlin/com/coder/toolbox/util/CoderProtocolHandler.kt @@ -98,7 +98,16 @@ open class CoderProtocolHandler( return } - restClient.startWorkspace(workspace) + try { + restClient.startWorkspace(workspace) + } catch (e: Exception) { + context.logger.error( + e, + "$workspaceName from $deploymentURL could not be started while handling URI" + ) + context.showErrorPopup(MissingArgumentException("Can't handle URI because an error was encountered while trying to start workspace $workspaceName")) + return + } if (restClient.waitForReady(workspace) != true) { context.logger.error("$workspaceName from $deploymentURL could not be started on time") context.showErrorPopup(MissingArgumentException("Can't handle URI because workspace $workspaceName could not be started on time")) From 0bdd3ffa1ac8d0337282f88727493676a72a9037 Mon Sep 17 00:00:00 2001 From: Faur Ioan-Aurel Date: Tue, 25 Mar 2025 01:08:43 +0200 Subject: [PATCH 10/21] fix: resiliency when workspace agent is not ready --- .../coder/toolbox/util/CoderProtocolHandler.kt | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/src/main/kotlin/com/coder/toolbox/util/CoderProtocolHandler.kt b/src/main/kotlin/com/coder/toolbox/util/CoderProtocolHandler.kt index 1361990..24168e2 100644 --- a/src/main/kotlin/com/coder/toolbox/util/CoderProtocolHandler.kt +++ b/src/main/kotlin/com/coder/toolbox/util/CoderProtocolHandler.kt @@ -128,19 +128,10 @@ open class CoderProtocolHandler( val agent = getMatchingAgent(params, workspace) val status = WorkspaceAndAgentStatus.from(workspace, agent) - if (status.pending()) { - // TODO: Wait for the agent to be ready. - throw IllegalArgumentException( - "The agent \"${agent.name}\" has a status of \"${ - status.toString().lowercase() - }\"; please wait then try again", - ) - } else if (!status.ready()) { - throw IllegalArgumentException( - "The agent \"${agent.name}\" has a status of \"${ - status.toString().lowercase() - }\"; unable to connect" - ) + if (!status.ready()) { + context.logger.error("Agent ${agent.name} for workspace $workspaceName from $deploymentURL is not started") + context.showErrorPopup(MissingArgumentException("Can't handle URI because agent ${agent.name} for workspace $workspaceName from $deploymentURL is not started")) + return } val cli = From 2929b0ea6d074ceda490ee9e207ae749a7cd4d4b Mon Sep 17 00:00:00 2001 From: Faur Ioan-Aurel Date: Tue, 25 Mar 2025 01:18:14 +0200 Subject: [PATCH 11/21] refactor: reuse block of code - to pop-up Toolbox window when it's not showing. --- .../com/coder/toolbox/util/CoderProtocolHandler.kt | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/main/kotlin/com/coder/toolbox/util/CoderProtocolHandler.kt b/src/main/kotlin/com/coder/toolbox/util/CoderProtocolHandler.kt index 24168e2..872ba16 100644 --- a/src/main/kotlin/com/coder/toolbox/util/CoderProtocolHandler.kt +++ b/src/main/kotlin/com/coder/toolbox/util/CoderProtocolHandler.kt @@ -157,8 +157,7 @@ open class CoderProtocolHandler( reInitialize(restClient, cli) val environmentId = "${workspace.name}.${agent.name}" - context.ui.showWindow() - context.envPageManager.showPluginEnvironmentsPage(true) + context.popupPluginMainPage() context.envPageManager.showEnvironmentPage(environmentId, false) val productCode = params.ideProductCode() val buildNumber = params.ideBuildNumber() @@ -193,8 +192,7 @@ open class CoderProtocolHandler( } private suspend fun askUrl(): String? { - context.ui.showWindow() - context.envPageManager.showPluginEnvironmentsPage(false) + context.popupPluginMainPage() return dialogUi.ask( context.i18n.ptrl("Deployment URL"), context.i18n.ptrl("Enter the full URL of your Coder deployment") @@ -216,8 +214,7 @@ open class CoderProtocolHandler( if (!tryToken.isNullOrBlank()) { tryToken } else { - context.ui.showWindow() - context.envPageManager.showPluginEnvironmentsPage(false) + context.popupPluginMainPage() // Otherwise ask for a new token, showing the previous token. dialogUi.askToken(deploymentURL.toURL()) } From 17089442110c0f5e20cddb476e23ff62fc74a686 Mon Sep 17 00:00:00 2001 From: Faur Ioan-Aurel Date: Thu, 27 Mar 2025 00:02:06 +0200 Subject: [PATCH 12/21] impl: rework the settings models - too much confusion around CoderSettings, CoderSettingsState, CoderSettingsService - lots of properties and some models were introduced only to inject data during tests - test related properties were leaking in the business code and there was no clear definition between the readable and writable interface - with this commit we have CoderSettingsStore exposing read+write, CoderSettings exposing reads + PluginSettingsStore the underlying persistable store - the tests control data via an instance of PluginSettingsStore just like the business code - two settings are now also exposed and configurable in the UI (previously only from tests): - ssh log dir - ssh extra arguments to the proxy command - resolves #40 - some of the options that did not make sense to be configurable and were only used in the tests were removed: - setupCommand - ignoreSetupFailures - these two are Gateway specific --- .../com/coder/toolbox/CoderRemoteProvider.kt | 25 +- .../com/coder/toolbox/CoderToolboxContext.kt | 8 +- .../coder/toolbox/CoderToolboxExtension.kt | 10 +- .../com/coder/toolbox/cli/CoderCLIManager.kt | 28 +- .../com/coder/toolbox/sdk/CoderRestClient.kt | 4 +- .../toolbox/services/CoderSettingsService.kt | 60 --- .../coder/toolbox/settings/CoderSettings.kt | 348 +++++----------- .../coder/toolbox/settings/SettingSource.kt | 29 ++ .../CoderSecretsStore.kt} | 4 +- .../coder/toolbox/store/CoderSettingsStore.kt | 208 ++++++++++ .../com/coder/toolbox/store/StoreKeys.kt | 38 ++ .../toolbox/util/CoderProtocolHandler.kt | 19 +- .../kotlin/com/coder/toolbox/util/Dialogs.kt | 6 +- src/main/kotlin/com/coder/toolbox/util/TLS.kt | 24 +- .../coder/toolbox/views/CoderSettingsPage.kt | 59 +-- .../com/coder/toolbox/views/ConnectPage.kt | 6 +- .../com/coder/toolbox/views/SignInPage.kt | 4 +- .../com/coder/toolbox/views/TokenPage.kt | 4 +- .../coder/toolbox/cli/CoderCLIManagerTest.kt | 360 ++++++++-------- .../coder/toolbox/sdk/CoderRestClientTest.kt | 66 +-- .../toolbox/settings/CoderSettingsTest.kt | 385 +++++++++--------- .../toolbox/util/PluginSettingsStoreUtil.kt | 15 + 22 files changed, 919 insertions(+), 791 deletions(-) delete mode 100644 src/main/kotlin/com/coder/toolbox/services/CoderSettingsService.kt create mode 100644 src/main/kotlin/com/coder/toolbox/settings/SettingSource.kt rename src/main/kotlin/com/coder/toolbox/{services/CoderSecretsService.kt => store/CoderSecretsStore.kt} (87%) create mode 100644 src/main/kotlin/com/coder/toolbox/store/CoderSettingsStore.kt create mode 100644 src/main/kotlin/com/coder/toolbox/store/StoreKeys.kt create mode 100644 src/test/kotlin/com/coder/toolbox/util/PluginSettingsStoreUtil.kt diff --git a/src/main/kotlin/com/coder/toolbox/CoderRemoteProvider.kt b/src/main/kotlin/com/coder/toolbox/CoderRemoteProvider.kt index 0fdd169..fd0ad57 100644 --- a/src/main/kotlin/com/coder/toolbox/CoderRemoteProvider.kt +++ b/src/main/kotlin/com/coder/toolbox/CoderRemoteProvider.kt @@ -3,8 +3,7 @@ package com.coder.toolbox import com.coder.toolbox.cli.CoderCLIManager import com.coder.toolbox.sdk.CoderRestClient import com.coder.toolbox.sdk.v2.models.WorkspaceStatus -import com.coder.toolbox.settings.CoderSettings -import com.coder.toolbox.settings.Source +import com.coder.toolbox.settings.SettingSource import com.coder.toolbox.util.CoderProtocolHandler import com.coder.toolbox.util.DialogUi import com.coder.toolbox.views.Action @@ -44,10 +43,11 @@ class CoderRemoteProvider( private var pollJob: Job? = null private var lastEnvironments: Set? = null + private val cSettings = context.settingsStore.readOnly() + // Create our services from the Toolbox ones. - private val settings: CoderSettings = CoderSettings(context.settings, context.logger) private val settingsPage: CoderSettingsPage = CoderSettingsPage(context) - private val dialogUi = DialogUi(context, settings) + private val dialogUi = DialogUi(context) // The REST client, if we are signed in private var client: CoderRestClient? = null @@ -61,7 +61,7 @@ class CoderRemoteProvider( private var firstRun = true private val isInitialized: MutableStateFlow = MutableStateFlow(false) private var coderHeaderPage = NewEnvironmentPage(context, context.i18n.pnotr(getDeploymentURL()?.first ?: "")) - private val linkHandler = CoderProtocolHandler(context, settings, httpClient, dialogUi, isInitialized) + private val linkHandler = CoderProtocolHandler(context, httpClient, dialogUi, isInitialized) override val environments: MutableStateFlow>> = MutableStateFlow( LoadableState.Value(emptyList()) ) @@ -270,7 +270,7 @@ class CoderRemoteProvider( var autologinEx: Exception? = null context.secrets.lastToken.let { lastToken -> context.secrets.lastDeploymentURL.let { lastDeploymentURL -> - if (autologin && lastDeploymentURL.isNotBlank() && (lastToken.isNotBlank() || !settings.requireTokenAuth)) { + if (autologin && lastDeploymentURL.isNotBlank() && (lastToken.isNotBlank() || !cSettings.requireTokenAuth)) { try { return createConnectPage(URL(lastDeploymentURL), lastToken) } catch (ex: Exception) { @@ -314,7 +314,6 @@ class CoderRemoteProvider( context, deploymentURL, token, - settings, httpClient, ::goToEnvironmentsPage, ) { client, cli -> @@ -339,11 +338,11 @@ class CoderRemoteProvider( * 2. Token on disk for this deployment. * 3. Global token for Coder, if it matches the deployment. */ - private fun getToken(deploymentURL: URL): Pair? = context.secrets.lastToken.let { + private fun getToken(deploymentURL: URL): Pair? = context.secrets.lastToken.let { if (it.isNotBlank() && context.secrets.lastDeploymentURL == deploymentURL.toString()) { - it to Source.LAST_USED + it to SettingSource.LAST_USED } else { - settings.token(deploymentURL) + cSettings.token(deploymentURL) } } @@ -357,11 +356,11 @@ class CoderRemoteProvider( * 3. CODER_URL. * 4. URL in global cli config. */ - private fun getDeploymentURL(): Pair? = context.secrets.lastDeploymentURL.let { + private fun getDeploymentURL(): Pair? = context.secrets.lastDeploymentURL.let { if (it.isNotBlank()) { - it to Source.LAST_USED + it to SettingSource.LAST_USED } else { - settings.defaultURL() + context.settingsStore.defaultURL() } } } diff --git a/src/main/kotlin/com/coder/toolbox/CoderToolboxContext.kt b/src/main/kotlin/com/coder/toolbox/CoderToolboxContext.kt index 404b4f9..7e70d15 100644 --- a/src/main/kotlin/com/coder/toolbox/CoderToolboxContext.kt +++ b/src/main/kotlin/com/coder/toolbox/CoderToolboxContext.kt @@ -1,7 +1,7 @@ package com.coder.toolbox -import com.coder.toolbox.services.CoderSecretsService -import com.coder.toolbox.services.CoderSettingsService +import com.coder.toolbox.store.CoderSecretsStore +import com.coder.toolbox.store.CoderSettingsStore import com.jetbrains.toolbox.api.core.diagnostics.Logger import com.jetbrains.toolbox.api.localization.LocalizableStringFactory import com.jetbrains.toolbox.api.remoteDev.connection.ClientHelper @@ -18,6 +18,6 @@ data class CoderToolboxContext( val cs: CoroutineScope, val logger: Logger, val i18n: LocalizableStringFactory, - val settings: CoderSettingsService, - val secrets: CoderSecretsService + val settingsStore: CoderSettingsStore, + val secrets: CoderSecretsStore ) diff --git a/src/main/kotlin/com/coder/toolbox/CoderToolboxExtension.kt b/src/main/kotlin/com/coder/toolbox/CoderToolboxExtension.kt index 12c08d9..755d934 100644 --- a/src/main/kotlin/com/coder/toolbox/CoderToolboxExtension.kt +++ b/src/main/kotlin/com/coder/toolbox/CoderToolboxExtension.kt @@ -1,7 +1,8 @@ package com.coder.toolbox -import com.coder.toolbox.services.CoderSecretsService -import com.coder.toolbox.services.CoderSettingsService +import com.coder.toolbox.settings.Environment +import com.coder.toolbox.store.CoderSecretsStore +import com.coder.toolbox.store.CoderSettingsStore import com.jetbrains.toolbox.api.core.PluginSecretStore import com.jetbrains.toolbox.api.core.PluginSettingsStore import com.jetbrains.toolbox.api.core.ServiceLocator @@ -22,6 +23,7 @@ import okhttp3.OkHttpClient class CoderToolboxExtension : RemoteDevExtension { // All services must be passed in here and threaded as necessary. override fun createRemoteProviderPluginInstance(serviceLocator: ServiceLocator): RemoteProvider { + val logger = serviceLocator.getService(Logger::class.java) return CoderRemoteProvider( CoderToolboxContext( serviceLocator.getService(ToolboxUi::class.java), @@ -31,8 +33,8 @@ class CoderToolboxExtension : RemoteDevExtension { serviceLocator.getService(CoroutineScope::class.java), serviceLocator.getService(Logger::class.java), serviceLocator.getService(LocalizableStringFactory::class.java), - CoderSettingsService(serviceLocator.getService(PluginSettingsStore::class.java)), - CoderSecretsService(serviceLocator.getService(PluginSecretStore::class.java)), + CoderSettingsStore(serviceLocator.getService(PluginSettingsStore::class.java), Environment(), logger), + CoderSecretsStore(serviceLocator.getService(PluginSecretStore::class.java)), ), OkHttpClient(), ) diff --git a/src/main/kotlin/com/coder/toolbox/cli/CoderCLIManager.kt b/src/main/kotlin/com/coder/toolbox/cli/CoderCLIManager.kt index 98cd3bd..e6e776a 100644 --- a/src/main/kotlin/com/coder/toolbox/cli/CoderCLIManager.kt +++ b/src/main/kotlin/com/coder/toolbox/cli/CoderCLIManager.kt @@ -5,7 +5,6 @@ import com.coder.toolbox.cli.ex.MissingVersionException import com.coder.toolbox.cli.ex.ResponseException import com.coder.toolbox.cli.ex.SSHConfigFormatException import com.coder.toolbox.settings.CoderSettings -import com.coder.toolbox.settings.CoderSettingsState import com.coder.toolbox.util.CoderHostnameVerifier import com.coder.toolbox.util.InvalidVersionException import com.coder.toolbox.util.OS @@ -59,8 +58,8 @@ fun ensureCLI( context: CoderToolboxContext, deploymentURL: URL, buildVersion: String, - settings: CoderSettings, ): CoderCLIManager { + val settings = context.settingsStore.readOnly() val cli = CoderCLIManager(deploymentURL, context.logger, settings) // Short-circuit if we already have the expected version. This @@ -123,7 +122,7 @@ class CoderCLIManager( private val deploymentURL: URL, private val logger: Logger, // Plugin configuration. - private val settings: CoderSettings = CoderSettings(CoderSettingsState(), logger), + private val settings: CoderSettings, // If the binary directory is not writable, this can be used to force the // manager to download to the data directory instead. forceDownloadToData: Boolean = false, @@ -138,7 +137,7 @@ class CoderCLIManager( fun download(): Boolean { val eTag = getBinaryETag() val conn = remoteBinaryURL.openConnection() as HttpURLConnection - if (settings.headerCommand.isNotBlank()) { + if (!settings.headerCommand.isNullOrBlank()) { val headersFromHeaderCommand = getHeaders(deploymentURL, settings.headerCommand) for ((key, value) in headersFromHeaderCommand) { conn.setRequestProperty(key, value) @@ -232,7 +231,7 @@ class CoderCLIManager( * Return the contents of the SSH config or null if it does not exist. */ private fun readSSHConfig(): String? = try { - settings.sshConfigPath.toFile().readText() + Path.of(settings.sshConfigPath).toFile().readText() } catch (e: FileNotFoundException) { null } @@ -264,21 +263,21 @@ class CoderCLIManager( // always use the correct URL. "--url", escape(deploymentURL.toString()), - if (settings.headerCommand.isNotBlank()) "--header-command" else null, - if (settings.headerCommand.isNotBlank()) escapeSubcommand(settings.headerCommand) else null, + if (!settings.headerCommand.isNullOrBlank()) "--header-command" else null, + if (!settings.headerCommand.isNullOrBlank()) escapeSubcommand(settings.headerCommand) else null, "ssh", "--stdio", if (settings.disableAutostart && feats.disableAutostart) "--disable-autostart" else null, ) val proxyArgs = baseArgs + listOfNotNull( - if (settings.sshLogDirectory.isNotBlank()) "--log-dir" else null, - if (settings.sshLogDirectory.isNotBlank()) escape(settings.sshLogDirectory) else null, + if (!settings.sshLogDirectory.isNullOrBlank()) "--log-dir" else null, + if (!settings.sshLogDirectory.isNullOrBlank()) escape(settings.sshLogDirectory) else null, if (feats.reportWorkspaceUsage) "--usage-app=toolbox" else null, ) val backgroundProxyArgs = baseArgs + listOfNotNull(if (feats.reportWorkspaceUsage) "--usage-app=disable" else null) val extraConfig = - if (settings.sshConfigOptions.isNotBlank()) { + if (!settings.sshConfigOptions.isNullOrBlank()) { "\n" + settings.sshConfigOptions.prependIndent(" ") } else { "" @@ -379,10 +378,13 @@ class CoderCLIManager( */ private fun writeSSHConfig(contents: String?) { if (contents != null) { - settings.sshConfigPath.parent.toFile().mkdirs() - settings.sshConfigPath.toFile().writeText(contents) + if (!settings.sshConfigPath.isNullOrBlank()) { + val sshConfPath = Path.of(settings.sshConfigPath) + sshConfPath.parent.toFile().mkdirs() + sshConfPath.toFile().writeText(contents) + } // The Coder cli will *not* create the log directory. - if (settings.sshLogDirectory.isNotBlank()) { + if (!settings.sshLogDirectory.isNullOrBlank()) { Path.of(settings.sshLogDirectory).toFile().mkdirs() } } diff --git a/src/main/kotlin/com/coder/toolbox/sdk/CoderRestClient.kt b/src/main/kotlin/com/coder/toolbox/sdk/CoderRestClient.kt index f3ccd58..1122b54 100644 --- a/src/main/kotlin/com/coder/toolbox/sdk/CoderRestClient.kt +++ b/src/main/kotlin/com/coder/toolbox/sdk/CoderRestClient.kt @@ -15,8 +15,6 @@ import com.coder.toolbox.sdk.v2.models.Workspace import com.coder.toolbox.sdk.v2.models.WorkspaceBuild import com.coder.toolbox.sdk.v2.models.WorkspaceResource import com.coder.toolbox.sdk.v2.models.WorkspaceTransition -import com.coder.toolbox.settings.CoderSettings -import com.coder.toolbox.settings.CoderSettingsState import com.coder.toolbox.util.CoderHostnameVerifier import com.coder.toolbox.util.coderSocketFactory import com.coder.toolbox.util.coderTrustManagers @@ -53,11 +51,11 @@ open class CoderRestClient( context: CoderToolboxContext, val url: URL, val token: String?, - private val settings: CoderSettings = CoderSettings(CoderSettingsState(), context.logger), private val proxyValues: ProxyValues? = null, private val pluginVersion: String = "development", existingHttpClient: OkHttpClient? = null, ) { + private val settings = context.settingsStore.readOnly() private val httpClient: OkHttpClient private val retroRestClient: CoderV2RestFacade diff --git a/src/main/kotlin/com/coder/toolbox/services/CoderSettingsService.kt b/src/main/kotlin/com/coder/toolbox/services/CoderSettingsService.kt deleted file mode 100644 index 41b6ebf..0000000 --- a/src/main/kotlin/com/coder/toolbox/services/CoderSettingsService.kt +++ /dev/null @@ -1,60 +0,0 @@ -package com.coder.toolbox.services - -import com.coder.toolbox.settings.CoderSettingsState -import com.jetbrains.toolbox.api.core.PluginSettingsStore - -/** - * Provides Coder settings backed by the settings state service. - * - * This also provides some helpers such as resolving the provided settings with - * environment variables and the defaults. - * - * For that reason, and to avoid presenting mutable values to most of the code - * while letting the settings page still read and mutate the underlying state, - * prefer using CoderSettingsService over CoderSettingsStateService. - */ -class CoderSettingsService(private val store: PluginSettingsStore) : CoderSettingsState() { - private fun get(key: String): String? = store[key] - - private fun set(key: String, value: String) { - if (value.isBlank()) { - store.remove(key) - } else { - store[key] = value - } - } - - override var binarySource: String - get() = get("binarySource") ?: super.binarySource - set(value) = set("binarySource", value) - override var binaryDirectory: String - get() = get("binaryDirectory") ?: super.binaryDirectory - set(value) = set("binaryDirectory", value) - override var dataDirectory: String - get() = get("dataDirectory") ?: super.dataDirectory - set(value) = set("dataDirectory", value) - override var enableDownloads: Boolean - get() = get("enableDownloads")?.toBooleanStrictOrNull() ?: super.enableDownloads - set(value) = set("enableDownloads", value.toString()) - override var enableBinaryDirectoryFallback: Boolean - get() = get("enableBinaryDirectoryFallback")?.toBooleanStrictOrNull() ?: super.enableBinaryDirectoryFallback - set(value) = set("enableBinaryDirectoryFallback", value.toString()) - override var headerCommand: String - get() = store["headerCommand"] ?: super.headerCommand - set(value) = set("headerCommand", value) - override var tlsCertPath: String - get() = store["tlsCertPath"] ?: super.tlsCertPath - set(value) = set("tlsCertPath", value) - override var tlsKeyPath: String - get() = store["tlsKeyPath"] ?: super.tlsKeyPath - set(value) = set("tlsKeyPath", value) - override var tlsCAPath: String - get() = store["tlsCAPath"] ?: super.tlsCAPath - set(value) = set("tlsCAPath", value) - override var tlsAlternateHostname: String - get() = store["tlsAlternateHostname"] ?: super.tlsAlternateHostname - set(value) = set("tlsAlternateHostname", value) - override var disableAutostart: Boolean - get() = store["disableAutostart"]?.toBooleanStrictOrNull() ?: super.disableAutostart - set(value) = set("disableAutostart", value.toString()) -} diff --git a/src/main/kotlin/com/coder/toolbox/settings/CoderSettings.kt b/src/main/kotlin/com/coder/toolbox/settings/CoderSettings.kt index 0f95798..3cc7ee6 100644 --- a/src/main/kotlin/com/coder/toolbox/settings/CoderSettings.kt +++ b/src/main/kotlin/com/coder/toolbox/settings/CoderSettings.kt @@ -1,204 +1,111 @@ package com.coder.toolbox.settings -import com.coder.toolbox.util.Arch -import com.coder.toolbox.util.OS import com.coder.toolbox.util.expand -import com.coder.toolbox.util.getArch -import com.coder.toolbox.util.getOS import com.coder.toolbox.util.safeHost import com.coder.toolbox.util.toURL import com.coder.toolbox.util.withPath -import com.jetbrains.toolbox.api.core.diagnostics.Logger import java.net.URL import java.nio.file.Files import java.nio.file.Path -import java.nio.file.Paths -const val CODER_SSH_CONFIG_OPTIONS = "CODER_SSH_CONFIG_OPTIONS" -const val CODER_URL = "CODER_URL" +data class CoderSettings( + val defaultURL: String?, -/** - * Describes where a setting came from. - */ -enum class Source { - CONFIG, // Pulled from the global Coder CLI config. - DEPLOYMENT_CONFIG, // Pulled from the config for a deployment. - ENVIRONMENT, // Pulled from environment variables. - LAST_USED, // Last used token. - QUERY, // From the Gateway link as a query parameter. - SETTINGS, // Pulled from settings. - USER, // Input by the user. - ; + /** + * Used to download the Coder CLI which is necessary to proxy SSH + * connections. The If-None-Match header will be set to the SHA1 of the CLI + * and can be used for caching. Absolute URLs will be used as-is; otherwise + * this value will be resolved against the deployment domain. Defaults to + * the plugin's data directory. + */ + val binarySource: String?, /** - * Return a description of the source. + * Directories are created here that store the CLI for each domain to which + * the plugin connects. Defaults to the data directory. */ - fun description(name: String): String = when (this) { - CONFIG -> "This $name was pulled from your global CLI config." - DEPLOYMENT_CONFIG -> "This $name was pulled from your deployment's CLI config." - LAST_USED -> "This was the last used $name." - QUERY -> "This $name was pulled from the Gateway link." - USER -> "This was the last used $name." - ENVIRONMENT -> "This $name was pulled from an environment variable." - SETTINGS -> "This $name was pulled from your settings." - } -} + val binaryDirectory: String?, -open class CoderSettingsState( - // Used to download the Coder CLI which is necessary to proxy SSH - // connections. The If-None-Match header will be set to the SHA1 of the CLI - // and can be used for caching. Absolute URLs will be used as-is; otherwise - // this value will be resolved against the deployment domain. Defaults to - // the plugin's data directory. - open var binarySource: String = "", - // Directories are created here that store the CLI for each domain to which - // the plugin connects. Defaults to the data directory. - open var binaryDirectory: String = "", - // Where to save plugin data like the Coder binary (if not configured with - // binaryDirectory) and the deployment URL and session token. - open var dataDirectory: String = "", - // Whether to allow the plugin to download the CLI if the current one is out - // of date or does not exist. - open var enableDownloads: Boolean = true, - // Whether to allow the plugin to fall back to the data directory when the - // CLI directory is not writable. - open var enableBinaryDirectoryFallback: Boolean = false, - // An external command that outputs additional HTTP headers added to all - // requests. The command must output each header as `key=value` on its own - // line. The following environment variables will be available to the - // process: CODER_URL. - open var headerCommand: String = "", - // Optionally set this to the path of a certificate to use for TLS - // connections. The certificate should be in X.509 PEM format. - open var tlsCertPath: String = "", - // Optionally set this to the path of the private key that corresponds to - // the above cert path to use for TLS connections. The key should be in - // X.509 PEM format. - open var tlsKeyPath: String = "", - // Optionally set this to the path of a file containing certificates for an - // alternate certificate authority used to verify TLS certs returned by the - // Coder service. The file should be in X.509 PEM format. - open var tlsCAPath: String = "", - // Optionally set this to an alternate hostname used for verifying TLS - // connections. This is useful when the hostname used to connect to the - // Coder service does not match the hostname in the TLS certificate. - open var tlsAlternateHostname: String = "", - // Whether to add --disable-autostart to the proxy command. This works - // around issues on macOS where it periodically wakes and Gateway - // reconnects, keeping the workspace constantly up. - open var disableAutostart: Boolean = getOS() == OS.MAC, - // Extra SSH config options. - open var sshConfigOptions: String = "", - // An external command to run in the directory of the IDE before connecting - // to it. - open var setupCommand: String = "", - // Whether to ignore setup command failures. - open var ignoreSetupFailure: Boolean = false, - // Default URL to show in the connection window. - open var defaultURL: String = "", - // Value for --log-dir. - open var sshLogDirectory: String = "", -) + val defaultCliBinaryNameByOsAndArch: String, -/** - * Consolidated TLS settings. - */ -data class CoderTLSSettings(private val state: CoderSettingsState) { - val certPath: String - get() = state.tlsCertPath - val keyPath: String - get() = state.tlsKeyPath - val caPath: String - get() = state.tlsCAPath - val altHostname: String - get() = state.tlsAlternateHostname -} + /** + * Configurable CLI binary name with extension, dependent on OS and arch + */ + val binaryName: String, -/** - * In non-test code use CoderSettingsService instead. - */ -open class CoderSettings( - // Raw mutable setting state. - private val state: CoderSettingsState, - private val logger: Logger, - // The location of the SSH config. Defaults to ~/.ssh/config. - val sshConfigPath: Path = Path.of(System.getProperty("user.home")).resolve(".ssh/config"), - // Overrides the default environment (for tests). - private val env: Environment = Environment(), - // Overrides the default binary name (for tests). - private val binaryName: String? = null, -) { - val tls = CoderTLSSettings(state) + /** + * Where to save plugin data like the Coder binary (if not configured with + * binaryDirectory) and the deployment URL and session token. + */ + val dataDirectory: String?, /** - * Whether downloading the CLI is allowed. + * Coder plugin's global data directory. */ - val enableDownloads: Boolean - get() = state.enableDownloads + val globalDataDirectory: String, /** - * Whether falling back to the data directory is allowed if the binary - * directory is not writable. + * Coder plugin's global config dir */ - val enableBinaryDirectoryFallback: Boolean - get() = state.enableBinaryDirectoryFallback + val globalConfigDir: String, /** - * A command to run to set headers for API calls. + * Whether to allow the plugin to download the CLI if the current one is out + * of date or does not exist. */ - val headerCommand: String - get() = state.headerCommand + val enableDownloads: Boolean, /** - * Whether to disable automatically starting a workspace when connecting. + * Whether to allow the plugin to fall back to the data directory when the + * CLI directory is not writable. */ - val disableAutostart: Boolean - get() = state.disableAutostart + val enableBinaryDirectoryFallback: Boolean, /** - * Extra SSH config to append to each host block. + * An external command that outputs additional HTTP headers added to all + * requests. The command must output each header as `key=value` on its own + * line. The following environment variables will be available to the + * process: CODER_URL. */ - val sshConfigOptions: String - get() = state.sshConfigOptions.ifBlank { env.get(CODER_SSH_CONFIG_OPTIONS) } + val headerCommand: String?, /** - * A command to run extra IDE setup. + * Optional TLS settings */ - val setupCommand: String - get() = state.setupCommand + val tls: CTLSSettings, /** - * Whether to ignore a failed setup command. + * Whether login should be done with a token */ - val ignoreSetupFailure: Boolean - get() = state.ignoreSetupFailure + val requireTokenAuth: Boolean = tls.certPath.isNullOrBlank() || tls.keyPath.isNullOrBlank(), /** - * The default URL to show in the connection window. + * Whether to add --disable-autostart to the proxy command. This works + * around issues on macOS where it periodically wakes and Gateway + * reconnects, keeping the workspace constantly up. */ - fun defaultURL(): Pair? { - val defaultURL = state.defaultURL - val envURL = env.get(CODER_URL) - if (defaultURL.isNotBlank()) { - return defaultURL to Source.SETTINGS - } else if (envURL.isNotBlank()) { - return envURL to Source.ENVIRONMENT - } else { - val (configUrl, _) = readConfig(coderConfigDir) - if (!configUrl.isNullOrBlank()) { - return configUrl to Source.CONFIG - } - } - return null - } + val disableAutostart: Boolean, - val sshLogDirectory: String - get() = state.sshLogDirectory + /** + * The location of the SSH config. Defaults to ~/.ssh/config. + */ + val sshConfigPath: String, + + /** + * Value for --log-dir. + */ + val sshLogDirectory: String?, + + /** + * Extra SSH config options + */ + val sshConfigOptions: String?, +) { /** * Given a deployment URL, try to find a token for it if required. */ - fun token(deploymentURL: URL): Pair? { + fun token(deploymentURL: URL): Pair? { // No need to bother if we do not need token auth anyway. if (!requireTokenAuth) { return null @@ -208,13 +115,13 @@ open class CoderSettings( // connected to in the past. val (_, deploymentToken) = readConfig(dataDir(deploymentURL).resolve("config")) if (!deploymentToken.isNullOrBlank()) { - return deploymentToken to Source.DEPLOYMENT_CONFIG + return deploymentToken to SettingSource.DEPLOYMENT_CONFIG } // Try the global config directory, in case they previously set up the // CLI with this URL. - val (configUrl, configToken) = readConfig(coderConfigDir) + val (configUrl, configToken) = readConfig(Path.of(globalConfigDir)) if (configUrl == deploymentURL.toString() && !configToken.isNullOrBlank()) { - return configToken to Source.CONFIG + return configToken to SettingSource.CONFIG } return null } @@ -223,10 +130,10 @@ open class CoderSettings( * Where the specified deployment should put its data. */ fun dataDir(url: URL): Path { - state.dataDirectory.let { + dataDirectory.let { val dir = - if (it.isBlank()) { - dataDir + if (it.isNullOrBlank()) { + Path.of(globalDataDirectory) } else { Path.of(expand(it)) } @@ -238,15 +145,13 @@ open class CoderSettings( * From where the specified deployment should download the binary. */ fun binSource(url: URL): URL { - state.binarySource.let { - val binaryName = getCoderCLIForOS(getOS(), getArch()) - return if (it.isBlank()) { - url.withPath("/bin/$binaryName") + binarySource.let { + return if (it.isNullOrBlank()) { + url.withPath("/bin/$defaultCliBinaryNameByOsAndArch") } else { - logger.info("Using binary source override $it") try { it.toURL() - } catch (e: Exception) { + } catch (_: Exception) { url.withPath(it) // Assume a relative path. } } @@ -260,15 +165,14 @@ open class CoderSettings( url: URL, forceDownloadToData: Boolean = false, ): Path { - state.binaryDirectory.let { - val name = binaryName ?: getCoderCLIForOS(getOS(), getArch()) + binaryDirectory.let { val dir = - if (forceDownloadToData || it.isBlank()) { + if (forceDownloadToData || it.isNullOrBlank()) { dataDir(url) } else { withHost(Path.of(expand(it)), url) } - return dir.resolve(name).toAbsolutePath() + return dir.resolve(binaryName).toAbsolutePath() } } @@ -276,7 +180,7 @@ open class CoderSettings( * Return the URL and token from the config, if they exist. */ fun readConfig(dir: Path): Pair { - logger.info("Reading config from $dir") +// logger.info("Reading config from $dir") return try { Files.readString(dir.resolve("url")) } catch (e: Exception) { @@ -303,88 +207,36 @@ open class CoderSettings( return path.resolve(host) } +} + +/** + * Consolidated TLS settings. + */ +data class CTLSSettings( /** - * Return the global config directory used by the Coder CLI. + * Optionally set this to the path of a certificate to use for TLS + * connections. The certificate should be in X.509 PEM format. */ - val coderConfigDir: Path - get() { - var dir = env.get("CODER_CONFIG_DIR") - if (dir.isNotBlank()) { - return Path.of(dir) - } - // The Coder CLI uses https://github.com/kirsle/configdir so this should - // match how it behaves. - return when (getOS()) { - OS.WINDOWS -> Paths.get(env.get("APPDATA"), "coderv2") - OS.MAC -> Paths.get(env.get("HOME"), "Library/Application Support/coderv2") - else -> { - dir = env.get("XDG_CONFIG_HOME") - if (dir.isNotBlank()) { - return Paths.get(dir, "coderv2") - } - return Paths.get(env.get("HOME"), ".config/coderv2") - } - } - } + val certPath: String?, /** - * Return the Coder plugin's global data directory. + * Optionally set this to the path of the private key that corresponds to + * the above cert path to use for TLS connections. The key should be in + * X.509 PEM format. */ - val dataDir: Path - get() { - return when (getOS()) { - OS.WINDOWS -> Paths.get(env.get("LOCALAPPDATA"), "coder-toolbox") - OS.MAC -> Paths.get(env.get("HOME"), "Library/Application Support/coder-toolbox") - else -> { - val dir = env.get("XDG_DATA_HOME") - if (dir.isNotBlank()) { - return Paths.get(dir, "coder-toolbox") - } - return Paths.get(env.get("HOME"), ".local/share/coder-toolbox") - } - } - } - - val requireTokenAuth: Boolean - get() { - return tls.certPath.isBlank() || tls.keyPath.isBlank() - } + val keyPath: String?, /** - * Return the name of the binary (with extension) for the provided OS and - * architecture. + * Optionally set this to the path of a file containing certificates for an + * alternate certificate authority used to verify TLS certs returned by the + * Coder service. The file should be in X.509 PEM format. */ - private fun getCoderCLIForOS( - os: OS?, - arch: Arch?, - ): String { - logger.info("Resolving binary for $os $arch") - if (os == null) { - logger.error("Could not resolve client OS and architecture, defaulting to WINDOWS AMD64") - return "coder-windows-amd64.exe" - } - return when (os) { - OS.WINDOWS -> - when (arch) { - Arch.AMD64 -> "coder-windows-amd64.exe" - Arch.ARM64 -> "coder-windows-arm64.exe" - else -> "coder-windows-amd64.exe" - } - - OS.LINUX -> - when (arch) { - Arch.AMD64 -> "coder-linux-amd64" - Arch.ARM64 -> "coder-linux-arm64" - Arch.ARMV7 -> "coder-linux-armv7" - else -> "coder-linux-amd64" - } + val caPath: String?, - OS.MAC -> - when (arch) { - Arch.AMD64 -> "coder-darwin-amd64" - Arch.ARM64 -> "coder-darwin-arm64" - else -> "coder-darwin-amd64" - } - } - } -} + /** + * Optionally set this to an alternate hostname used for verifying TLS + * connections. This is useful when the hostname used to connect to the + * Coder service does not match the hostname in the TLS certificate. + */ + val altHostname: String?, +) \ No newline at end of file diff --git a/src/main/kotlin/com/coder/toolbox/settings/SettingSource.kt b/src/main/kotlin/com/coder/toolbox/settings/SettingSource.kt new file mode 100644 index 0000000..433b9cc --- /dev/null +++ b/src/main/kotlin/com/coder/toolbox/settings/SettingSource.kt @@ -0,0 +1,29 @@ +package com.coder.toolbox.settings + +/** + * Describes where a setting came from. + */ +enum class SettingSource { + CONFIG, // Pulled from the global Coder CLI config. + DEPLOYMENT_CONFIG, // Pulled from the config for a deployment. + ENVIRONMENT, // Pulled from environment variables. + LAST_USED, // Last used token. + QUERY, // From the Gateway link as a query parameter. + SETTINGS, // Pulled from settings. + USER, // Input by the user. + ; + + /** + * Return a description of the source. + */ + fun description(name: String): String = when (this) { + CONFIG -> "This $name was pulled from your global CLI config." + DEPLOYMENT_CONFIG -> "This $name was pulled from your deployment's CLI config." + LAST_USED -> "This was the last used $name." + QUERY -> "This $name was pulled from the Gateway link." + USER -> "This was the last used $name." + ENVIRONMENT -> "This $name was pulled from an environment variable." + SETTINGS -> "This $name was pulled from your settings." + } +} + diff --git a/src/main/kotlin/com/coder/toolbox/services/CoderSecretsService.kt b/src/main/kotlin/com/coder/toolbox/store/CoderSecretsStore.kt similarity index 87% rename from src/main/kotlin/com/coder/toolbox/services/CoderSecretsService.kt rename to src/main/kotlin/com/coder/toolbox/store/CoderSecretsStore.kt index 10c1069..e5dde43 100644 --- a/src/main/kotlin/com/coder/toolbox/services/CoderSecretsService.kt +++ b/src/main/kotlin/com/coder/toolbox/store/CoderSecretsStore.kt @@ -1,4 +1,4 @@ -package com.coder.toolbox.services +package com.coder.toolbox.store import com.jetbrains.toolbox.api.core.PluginSecretStore @@ -6,7 +6,7 @@ import com.jetbrains.toolbox.api.core.PluginSecretStore /** * Provides Coder secrets backed by the secrets store service. */ -class CoderSecretsService(private val store: PluginSecretStore) { +class CoderSecretsStore(private val store: PluginSecretStore) { private fun get(key: String): String = store[key] ?: "" private fun set(key: String, value: String) { diff --git a/src/main/kotlin/com/coder/toolbox/store/CoderSettingsStore.kt b/src/main/kotlin/com/coder/toolbox/store/CoderSettingsStore.kt new file mode 100644 index 0000000..9321056 --- /dev/null +++ b/src/main/kotlin/com/coder/toolbox/store/CoderSettingsStore.kt @@ -0,0 +1,208 @@ +package com.coder.toolbox.store + +import com.coder.toolbox.settings.CTLSSettings +import com.coder.toolbox.settings.CoderSettings +import com.coder.toolbox.settings.Environment +import com.coder.toolbox.settings.SettingSource +import com.coder.toolbox.util.Arch +import com.coder.toolbox.util.OS +import com.coder.toolbox.util.getArch +import com.coder.toolbox.util.getOS +import com.jetbrains.toolbox.api.core.PluginSettingsStore +import com.jetbrains.toolbox.api.core.diagnostics.Logger +import java.nio.file.Path +import java.nio.file.Paths + +class CoderSettingsStore( + private val store: PluginSettingsStore, + private val env: Environment = Environment(), + private val logger: Logger +) { + private var backingSettings = CoderSettings( + defaultURL = store[DEFAULT_URL], + binarySource = store[BINARY_SOURCE], + binaryDirectory = store[BINARY_DIRECTORY], + defaultCliBinaryNameByOsAndArch = getCoderCLIForOS(getOS(), getArch()), + binaryName = store[BINARY_NAME] ?: getCoderCLIForOS(getOS(), getArch()), + dataDirectory = store[DATA_DIRECTORY], + globalDataDirectory = getDefaultGlobalDataDir().normalize().toString(), + globalConfigDir = getDefaultGlobalConfigDir().normalize().toString(), + enableDownloads = store[ENABLE_DOWNLOADS]?.toBooleanStrictOrNull() ?: true, + enableBinaryDirectoryFallback = store[ENABLE_BINARY_DIR_FALLBACK]?.toBooleanStrictOrNull() ?: false, + headerCommand = store[HEADER_COMMAND], + tls = CTLSSettings( + certPath = store[TLS_CERT_PATH], + keyPath = store[TLS_KEY_PATH], + caPath = store[TLS_CA_PATH], + altHostname = store[TLS_ALTERNATE_HOSTNAME] + ), + disableAutostart = store[DISABLE_AUTOSTART]?.toBooleanStrictOrNull() ?: (getOS() == OS.MAC), + + sshConfigPath = store[SSH_CONFIG_PATH].takeUnless { it.isNullOrEmpty() } + ?: Path.of(System.getProperty("user.home")).resolve(".ssh/config").normalize().toString(), + sshLogDirectory = store[SSH_LOG_DIR], + sshConfigOptions = store[SSH_CONFIG_OPTIONS].takeUnless { it.isNullOrEmpty() } ?: env.get( + CODER_SSH_CONFIG_OPTIONS + ) + ) + + /** + * The default URL to show in the connection window. + */ + fun defaultURL(): Pair? { + val envURL = env.get(CODER_URL) + if (!backingSettings.defaultURL.isNullOrEmpty()) { + return backingSettings.defaultURL!! to SettingSource.SETTINGS + } else if (envURL.isNotBlank()) { + return envURL to SettingSource.ENVIRONMENT + } else { + val (configUrl, _) = backingSettings.readConfig(Path.of(backingSettings.globalConfigDir)) + if (!configUrl.isNullOrBlank()) { + return configUrl to SettingSource.CONFIG + } + } + return null + } + + /** + * Read-only access to the settings + */ + fun readOnly(): CoderSettings = backingSettings + + fun updateBinarySource(source: String) { + backingSettings = backingSettings.copy(binarySource = source) + store[BINARY_SOURCE] = source + } + + fun updateBinaryDirectory(dir: String) { + backingSettings = backingSettings.copy(binaryDirectory = dir) + store[BINARY_DIRECTORY] = dir + } + + fun updateDataDirectory(dir: String) { + backingSettings = backingSettings.copy(dataDirectory = dir) + store[DATA_DIRECTORY] = dir + } + + fun updateEnableDownloads(shouldEnableDownloads: Boolean) { + backingSettings = backingSettings.copy(enableDownloads = shouldEnableDownloads) + store[ENABLE_DOWNLOADS] = shouldEnableDownloads.toString() + } + + fun updateBinaryDirectoryFallback(shouldEnableBinDirFallback: Boolean) { + backingSettings = backingSettings.copy(enableBinaryDirectoryFallback = shouldEnableBinDirFallback) + store[ENABLE_BINARY_DIR_FALLBACK] = shouldEnableBinDirFallback.toString() + } + + fun updateHeaderCommand(cmd: String) { + backingSettings = backingSettings.copy(headerCommand = cmd) + store[HEADER_COMMAND] = cmd + } + + fun updateCertPath(path: String) { + backingSettings = backingSettings.copy(tls = backingSettings.tls.copy(certPath = path)) + store[TLS_CERT_PATH] = path + } + + fun updateKeyPath(path: String) { + backingSettings = backingSettings.copy(tls = backingSettings.tls.copy(keyPath = path)) + store[TLS_KEY_PATH] = path + } + + fun updateCAPath(path: String) { + backingSettings = backingSettings.copy(tls = backingSettings.tls.copy(caPath = path)) + store[TLS_CA_PATH] = path + } + + fun updateAltHostname(hostname: String) { + backingSettings = backingSettings.copy(tls = backingSettings.tls.copy(altHostname = hostname)) + store[TLS_ALTERNATE_HOSTNAME] = hostname + } + + fun updateDisableAutostart(shouldDisableAutostart: Boolean) { + backingSettings = backingSettings.copy(disableAutostart = shouldDisableAutostart) + store[DISABLE_AUTOSTART] = shouldDisableAutostart.toString() + } + + fun updateSshLogDir(path: String) { + backingSettings = backingSettings.copy(sshLogDirectory = path) + store[SSH_LOG_DIR] = path + } + + fun updateSshConfigOptions(options: String) { + backingSettings = backingSettings.copy(sshConfigOptions = options) + store[SSH_CONFIG_OPTIONS] = options + } + + private fun getDefaultGlobalDataDir(): Path { + return when (getOS()) { + OS.WINDOWS -> Paths.get(env.get("LOCALAPPDATA"), "coder-toolbox") + OS.MAC -> Paths.get(env.get("HOME"), "Library/Application Support/coder-toolbox") + else -> { + val dir = env.get("XDG_DATA_HOME") + if (dir.isNotBlank()) { + return Paths.get(dir, "coder-toolbox") + } + return Paths.get(env.get("HOME"), ".local/share/coder-toolbox") + } + } + } + + private fun getDefaultGlobalConfigDir(): Path { + var dir = env.get("CODER_CONFIG_DIR") + if (dir.isNotBlank()) { + return Path.of(dir) + } + // The Coder CLI uses https://github.com/kirsle/configdir so this should + // match how it behaves. + return when (getOS()) { + OS.WINDOWS -> Paths.get(env.get("APPDATA"), "coderv2") + OS.MAC -> Paths.get(env.get("HOME"), "Library/Application Support/coderv2") + else -> { + dir = env.get("XDG_CONFIG_HOME") + if (dir.isNotBlank()) { + return Paths.get(dir, "coderv2") + } + return Paths.get(env.get("HOME"), ".config/coderv2") + } + } + } + + /** + * Return the name of the binary (with extension) for the provided OS and + * architecture. + */ + private fun getCoderCLIForOS( + os: OS?, + arch: Arch?, + ): String { + logger.info("Resolving binary for $os $arch") + if (os == null) { + logger.error("Could not resolve client OS and architecture, defaulting to WINDOWS AMD64") + return "coder-windows-amd64.exe" + } + return when (os) { + OS.WINDOWS -> + when (arch) { + Arch.AMD64 -> "coder-windows-amd64.exe" + Arch.ARM64 -> "coder-windows-arm64.exe" + else -> "coder-windows-amd64.exe" + } + + OS.LINUX -> + when (arch) { + Arch.AMD64 -> "coder-linux-amd64" + Arch.ARM64 -> "coder-linux-arm64" + Arch.ARMV7 -> "coder-linux-armv7" + else -> "coder-linux-amd64" + } + + OS.MAC -> + when (arch) { + Arch.AMD64 -> "coder-darwin-amd64" + Arch.ARM64 -> "coder-darwin-arm64" + else -> "coder-darwin-amd64" + } + } + } +} diff --git a/src/main/kotlin/com/coder/toolbox/store/StoreKeys.kt b/src/main/kotlin/com/coder/toolbox/store/StoreKeys.kt new file mode 100644 index 0000000..2d929ec --- /dev/null +++ b/src/main/kotlin/com/coder/toolbox/store/StoreKeys.kt @@ -0,0 +1,38 @@ +package com.coder.toolbox.store + +internal const val CODER_SSH_CONFIG_OPTIONS = "CODER_SSH_CONFIG_OPTIONS" + +internal const val CODER_URL = "CODER_URL" + +internal const val DEFAULT_URL = "defaultURL" + +internal const val BINARY_SOURCE = "binarySource" + +internal const val BINARY_DIRECTORY = "binaryDirectory" + +internal const val BINARY_NAME = "binaryName" + +internal const val DATA_DIRECTORY = "dataDirectory" + +internal const val ENABLE_DOWNLOADS = "enableDownloads" + +internal const val ENABLE_BINARY_DIR_FALLBACK = "enableBinaryDirectoryFallback" + +internal const val HEADER_COMMAND = "headerCommand" + +internal const val TLS_CERT_PATH = "tlsCertPath" + +internal const val TLS_KEY_PATH = "tlsKeyPath" + +internal const val TLS_CA_PATH = "tlsCAPath" + +internal const val TLS_ALTERNATE_HOSTNAME = "tlsAlternateHostname" + +internal const val DISABLE_AUTOSTART = "disableAutostart" + +internal const val SSH_CONFIG_PATH = "sshConfigPath" + +internal const val SSH_LOG_DIR = "sshLogDir" + +internal const val SSH_CONFIG_OPTIONS = "sshConfigOptions" + diff --git a/src/main/kotlin/com/coder/toolbox/util/CoderProtocolHandler.kt b/src/main/kotlin/com/coder/toolbox/util/CoderProtocolHandler.kt index 872ba16..fe2e307 100644 --- a/src/main/kotlin/com/coder/toolbox/util/CoderProtocolHandler.kt +++ b/src/main/kotlin/com/coder/toolbox/util/CoderProtocolHandler.kt @@ -9,7 +9,6 @@ import com.coder.toolbox.sdk.CoderRestClient import com.coder.toolbox.sdk.v2.models.Workspace import com.coder.toolbox.sdk.v2.models.WorkspaceAgent import com.coder.toolbox.sdk.v2.models.WorkspaceStatus -import com.coder.toolbox.settings.CoderSettings import com.jetbrains.toolbox.api.localization.LocalizableString import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.delay @@ -27,11 +26,12 @@ import kotlin.time.toJavaDuration open class CoderProtocolHandler( private val context: CoderToolboxContext, - private val settings: CoderSettings, private val httpClient: OkHttpClient?, private val dialogUi: DialogUi, private val isInitialized: StateFlow, ) { + private val settings = context.settingsStore.readOnly() + /** * Given a set of URL parameters, prepare the CLI then return a workspace to * connect. @@ -88,7 +88,7 @@ open class CoderProtocolHandler( WorkspaceStatus.STOPPING, WorkspaceStatus.STOPPED, WorkspaceStatus.CANCELING, WorkspaceStatus.CANCELED -> { - if (context.settings.disableAutostart) { + if (settings.disableAutostart) { context.logger.warn("$workspaceName from $deploymentURL is not started and autostart is disabled.") context.showInfoPopup( context.i18n.pnotr("$workspaceName is not running"), @@ -134,13 +134,11 @@ open class CoderProtocolHandler( return } - val cli = - ensureCLI( - context, - deploymentURL.toURL(), - restClient.buildInfo().version, - settings - ) + val cli = ensureCLI( + context, + deploymentURL.toURL(), + restClient.buildInfo().version + ) // We only need to log in if we are using token-based auth. if (restClient.token != null) { @@ -231,7 +229,6 @@ open class CoderProtocolHandler( context, deploymentURL.toURL(), token, - settings, proxyValues = null, // TODO - not sure the above comment applies as we are creating our own http client PluginManager.pluginInfo.version, httpClient diff --git a/src/main/kotlin/com/coder/toolbox/util/Dialogs.kt b/src/main/kotlin/com/coder/toolbox/util/Dialogs.kt index a1a4e3a..44a3dfb 100644 --- a/src/main/kotlin/com/coder/toolbox/util/Dialogs.kt +++ b/src/main/kotlin/com/coder/toolbox/util/Dialogs.kt @@ -2,7 +2,6 @@ package com.coder.toolbox.util import com.coder.toolbox.CoderToolboxContext import com.coder.toolbox.browser.BrowserUtil -import com.coder.toolbox.settings.CoderSettings import com.jetbrains.toolbox.api.localization.LocalizableString import com.jetbrains.toolbox.api.ui.components.TextType import java.net.URL @@ -12,10 +11,7 @@ import java.net.URL * * This is meant to mimic ToolboxUi. */ -class DialogUi( - private val context: CoderToolboxContext, - private val settings: CoderSettings, -) { +class DialogUi(private val context: CoderToolboxContext) { suspend fun confirm(title: LocalizableString, description: LocalizableString): Boolean { return context.ui.showOkCancelPopup(title, description, context.i18n.ptrl("Yes"), context.i18n.ptrl("No")) diff --git a/src/main/kotlin/com/coder/toolbox/util/TLS.kt b/src/main/kotlin/com/coder/toolbox/util/TLS.kt index c69aaff..17952df 100644 --- a/src/main/kotlin/com/coder/toolbox/util/TLS.kt +++ b/src/main/kotlin/com/coder/toolbox/util/TLS.kt @@ -1,6 +1,6 @@ package com.coder.toolbox.util -import com.coder.toolbox.settings.CoderTLSSettings +import com.coder.toolbox.settings.CTLSSettings import okhttp3.internal.tls.OkHostnameVerifier import java.io.File import java.io.FileInputStream @@ -28,12 +28,12 @@ import javax.net.ssl.TrustManagerFactory import javax.net.ssl.X509TrustManager fun sslContextFromPEMs( - certPath: String, - keyPath: String, - caPath: String, + certPath: String?, + keyPath: String?, + caPath: String?, ): SSLContext { var km: Array? = null - if (certPath.isNotBlank() && keyPath.isNotBlank()) { + if (!certPath.isNullOrBlank() && !keyPath.isNullOrBlank()) { val certificateFactory = CertificateFactory.getInstance("X.509") val certInputStream = FileInputStream(expand(certPath)) val certChain = certificateFactory.generateCertificates(certInputStream) @@ -81,18 +81,18 @@ fun sslContextFromPEMs( return sslContext } -fun coderSocketFactory(settings: CoderTLSSettings): SSLSocketFactory { +fun coderSocketFactory(settings: CTLSSettings): SSLSocketFactory { val sslContext = sslContextFromPEMs(settings.certPath, settings.keyPath, settings.caPath) - if (settings.altHostname.isBlank()) { + if (settings.altHostname.isNullOrBlank()) { return sslContext.socketFactory } return AlternateNameSSLSocketFactory(sslContext.socketFactory, settings.altHostname) } -fun coderTrustManagers(tlsCAPath: String): Array { +fun coderTrustManagers(tlsCAPath: String?): Array { val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()) - if (tlsCAPath.isBlank()) { + if (tlsCAPath.isNullOrBlank()) { // return default trust managers trustManagerFactory.init(null as KeyStore?) return trustManagerFactory.trustManagers @@ -111,7 +111,7 @@ fun coderTrustManagers(tlsCAPath: String): Array { return trustManagerFactory.trustManagers.map { MergedSystemTrustManger(it as X509TrustManager) }.toTypedArray() } -class AlternateNameSSLSocketFactory(private val delegate: SSLSocketFactory, private val alternateName: String) : +class AlternateNameSSLSocketFactory(private val delegate: SSLSocketFactory, private val alternateName: String?) : SSLSocketFactory() { override fun getDefaultCipherSuites(): Array = delegate.defaultCipherSuites @@ -181,12 +181,12 @@ class AlternateNameSSLSocketFactory(private val delegate: SSLSocketFactory, priv } } -class CoderHostnameVerifier(private val alternateName: String) : HostnameVerifier { +class CoderHostnameVerifier(private val alternateName: String?) : HostnameVerifier { override fun verify( host: String, session: SSLSession, ): Boolean { - if (alternateName.isEmpty()) { + if (alternateName.isNullOrBlank()) { return OkHostnameVerifier.verify(host, session) } val certs = session.peerCertificates ?: return false diff --git a/src/main/kotlin/com/coder/toolbox/views/CoderSettingsPage.kt b/src/main/kotlin/com/coder/toolbox/views/CoderSettingsPage.kt index b4ce938..c97537d 100644 --- a/src/main/kotlin/com/coder/toolbox/views/CoderSettingsPage.kt +++ b/src/main/kotlin/com/coder/toolbox/views/CoderSettingsPage.kt @@ -17,32 +17,39 @@ import kotlinx.coroutines.flow.StateFlow * I have not been able to test this page. */ class CoderSettingsPage(context: CoderToolboxContext) : CoderPage(context, context.i18n.ptrl("Coder Settings"), false) { + private val settings = context.settingsStore.readOnly() + // TODO: Copy over the descriptions, holding until I can test this page. private val binarySourceField = - TextField(context.i18n.ptrl("Binary source"), context.settings.binarySource, TextType.General) + TextField(context.i18n.ptrl("Binary source"), settings.binarySource ?: "", TextType.General) private val binaryDirectoryField = - TextField(context.i18n.ptrl("Binary directory"), context.settings.binaryDirectory, TextType.General) + TextField(context.i18n.ptrl("Binary directory"), settings.binaryDirectory ?: "", TextType.General) private val dataDirectoryField = - TextField(context.i18n.ptrl("Data directory"), context.settings.dataDirectory, TextType.General) + TextField(context.i18n.ptrl("Data directory"), settings.dataDirectory ?: "", TextType.General) private val enableDownloadsField = - CheckboxField(context.settings.enableDownloads, context.i18n.ptrl("Enable downloads")) + CheckboxField(settings.enableDownloads, context.i18n.ptrl("Enable downloads")) private val enableBinaryDirectoryFallbackField = CheckboxField( - context.settings.enableBinaryDirectoryFallback, + settings.enableBinaryDirectoryFallback, context.i18n.ptrl("Enable binary directory fallback") ) private val headerCommandField = - TextField(context.i18n.ptrl("Header command"), context.settings.headerCommand, TextType.General) + TextField(context.i18n.ptrl("Header command"), settings.headerCommand ?: "", TextType.General) private val tlsCertPathField = - TextField(context.i18n.ptrl("TLS cert path"), context.settings.tlsCertPath, TextType.General) + TextField(context.i18n.ptrl("TLS cert path"), settings.tls.certPath ?: "", TextType.General) private val tlsKeyPathField = - TextField(context.i18n.ptrl("TLS key path"), context.settings.tlsKeyPath, TextType.General) + TextField(context.i18n.ptrl("TLS key path"), settings.tls.keyPath ?: "", TextType.General) private val tlsCAPathField = - TextField(context.i18n.ptrl("TLS CA path"), context.settings.tlsCAPath, TextType.General) + TextField(context.i18n.ptrl("TLS CA path"), settings.tls.caPath ?: "", TextType.General) private val tlsAlternateHostnameField = - TextField(context.i18n.ptrl("TLS alternate hostname"), context.settings.tlsAlternateHostname, TextType.General) + TextField(context.i18n.ptrl("TLS alternate hostname"), settings.tls.altHostname ?: "", TextType.General) private val disableAutostartField = - CheckboxField(context.settings.disableAutostart, context.i18n.ptrl("Disable autostart")) + CheckboxField(settings.disableAutostart, context.i18n.ptrl("Disable autostart")) + private val sshExtraArgs = + TextField(context.i18n.ptrl("Extra SSH options"), settings.sshConfigOptions ?: "", TextType.General) + private val sshLogDirField = + TextField(context.i18n.ptrl("SSH proxy log directory"), settings.sshLogDirectory ?: "", TextType.General) + override val fields: StateFlow> = MutableStateFlow( listOf( @@ -56,25 +63,29 @@ class CoderSettingsPage(context: CoderToolboxContext) : CoderPage(context, conte tlsKeyPathField, tlsCAPathField, tlsAlternateHostnameField, - disableAutostartField + disableAutostartField, + sshLogDirField, + sshExtraArgs, ) ) override val actionButtons: StateFlow> = MutableStateFlow( listOf( Action(context.i18n.ptrl("Save"), closesPage = true) { - context.settings.binarySource = binarySourceField.textState.value - context.settings.binaryDirectory = binaryDirectoryField.textState.value - context.settings.dataDirectory = dataDirectoryField.textState.value - context.settings.enableDownloads = enableDownloadsField.checkedState.value - context.settings.enableBinaryDirectoryFallback = enableBinaryDirectoryFallbackField.checkedState.value - context.settings.headerCommand = headerCommandField.textState.value - context.settings.tlsCertPath = tlsCertPathField.textState.value - context.settings.tlsKeyPath = tlsKeyPathField.textState.value - context.settings.tlsCAPath = tlsCAPathField.textState.value - context.settings.tlsAlternateHostname = tlsAlternateHostnameField.textState.value - context.settings.disableAutostart = disableAutostartField.checkedState.value - }, + context.settingsStore.updateBinarySource(binarySourceField.textState.value) + context.settingsStore.updateBinaryDirectory(binaryDirectoryField.textState.value) + context.settingsStore.updateDataDirectory(dataDirectoryField.textState.value) + context.settingsStore.updateEnableDownloads(enableDownloadsField.checkedState.value) + context.settingsStore.updateBinaryDirectoryFallback(enableBinaryDirectoryFallbackField.checkedState.value) + context.settingsStore.updateHeaderCommand(headerCommandField.textState.value) + context.settingsStore.updateCertPath(tlsCertPathField.textState.value) + context.settingsStore.updateKeyPath(tlsKeyPathField.textState.value) + context.settingsStore.updateCAPath(tlsCAPathField.textState.value) + context.settingsStore.updateAltHostname(tlsAlternateHostnameField.textState.value) + context.settingsStore.updateDisableAutostart(disableAutostartField.checkedState.value) + context.settingsStore.updateSshLogDir(sshLogDirField.textState.value) + context.settingsStore.updateSshConfigOptions(sshExtraArgs.textState.value) + } ) ) } diff --git a/src/main/kotlin/com/coder/toolbox/views/ConnectPage.kt b/src/main/kotlin/com/coder/toolbox/views/ConnectPage.kt index 25a3359..261cc53 100644 --- a/src/main/kotlin/com/coder/toolbox/views/ConnectPage.kt +++ b/src/main/kotlin/com/coder/toolbox/views/ConnectPage.kt @@ -5,7 +5,6 @@ import com.coder.toolbox.cli.CoderCLIManager import com.coder.toolbox.cli.ensureCLI import com.coder.toolbox.plugin.PluginManager import com.coder.toolbox.sdk.CoderRestClient -import com.coder.toolbox.settings.CoderSettings import com.coder.toolbox.util.humanizeConnectionError import com.jetbrains.toolbox.api.localization.LocalizableString import com.jetbrains.toolbox.api.ui.actions.RunnableActionDescription @@ -25,7 +24,6 @@ class ConnectPage( private val context: CoderToolboxContext, private val url: URL, private val token: String?, - private val settings: CoderSettings, private val httpClient: OkHttpClient, private val onCancel: () -> Unit, private val onConnect: ( @@ -33,6 +31,7 @@ class ConnectPage( cli: CoderCLIManager, ) -> Unit, ) : CoderPage(context, context.i18n.ptrl("Connecting to Coder")) { + private val settings = context.settingsStore.readOnly() private var signInJob: Job? = null private var statusField = LabelField(context.i18n.pnotr("Connecting to ${url.host}...")) @@ -94,14 +93,13 @@ class ConnectPage( context, url, token, - settings, proxyValues = null, PluginManager.pluginInfo.version, httpClient ) client.authenticate() updateStatus(context.i18n.ptrl("Checking Coder binary..."), error = null) - val cli = ensureCLI(context, client.url, client.buildVersion, settings) + val cli = ensureCLI(context, client.url, client.buildVersion) // We only need to log in if we are using token-based auth. if (client.token != null) { updateStatus(context.i18n.ptrl("Configuring CLI..."), error = null) diff --git a/src/main/kotlin/com/coder/toolbox/views/SignInPage.kt b/src/main/kotlin/com/coder/toolbox/views/SignInPage.kt index f6455ba..914b41b 100644 --- a/src/main/kotlin/com/coder/toolbox/views/SignInPage.kt +++ b/src/main/kotlin/com/coder/toolbox/views/SignInPage.kt @@ -1,7 +1,7 @@ package com.coder.toolbox.views import com.coder.toolbox.CoderToolboxContext -import com.coder.toolbox.settings.Source +import com.coder.toolbox.settings.SettingSource import com.jetbrains.toolbox.api.ui.actions.RunnableActionDescription import com.jetbrains.toolbox.api.ui.components.LabelField import com.jetbrains.toolbox.api.ui.components.TextField @@ -19,7 +19,7 @@ import java.net.URL */ class SignInPage( private val context: CoderToolboxContext, - private val deploymentURL: Pair?, + private val deploymentURL: Pair?, private val onSignIn: (deploymentURL: URL) -> Unit, ) : CoderPage(context, context.i18n.ptrl("Sign In to Coder")) { private val urlField = TextField(context.i18n.ptrl("Deployment URL"), deploymentURL?.first ?: "", TextType.General) diff --git a/src/main/kotlin/com/coder/toolbox/views/TokenPage.kt b/src/main/kotlin/com/coder/toolbox/views/TokenPage.kt index 6b4cf6c..abd68fb 100644 --- a/src/main/kotlin/com/coder/toolbox/views/TokenPage.kt +++ b/src/main/kotlin/com/coder/toolbox/views/TokenPage.kt @@ -1,7 +1,7 @@ package com.coder.toolbox.views import com.coder.toolbox.CoderToolboxContext -import com.coder.toolbox.settings.Source +import com.coder.toolbox.settings.SettingSource import com.coder.toolbox.util.withPath import com.jetbrains.toolbox.api.ui.actions.RunnableActionDescription import com.jetbrains.toolbox.api.ui.components.LabelField @@ -22,7 +22,7 @@ import java.net.URL class TokenPage( context: CoderToolboxContext, deploymentURL: URL, - token: Pair?, + token: Pair?, private val onToken: ((token: String) -> Unit), ) : CoderPage(context, context.i18n.ptrl("Enter your token")) { private val tokenField = TextField(context.i18n.ptrl("Token"), token?.first ?: "", TextType.Password) diff --git a/src/test/kotlin/com/coder/toolbox/cli/CoderCLIManagerTest.kt b/src/test/kotlin/com/coder/toolbox/cli/CoderCLIManagerTest.kt index e73190b..e3806c7 100644 --- a/src/test/kotlin/com/coder/toolbox/cli/CoderCLIManagerTest.kt +++ b/src/test/kotlin/com/coder/toolbox/cli/CoderCLIManagerTest.kt @@ -4,17 +4,27 @@ import com.coder.toolbox.CoderToolboxContext import com.coder.toolbox.cli.ex.MissingVersionException import com.coder.toolbox.cli.ex.ResponseException import com.coder.toolbox.cli.ex.SSHConfigFormatException -import com.coder.toolbox.services.CoderSecretsService -import com.coder.toolbox.services.CoderSettingsService -import com.coder.toolbox.settings.CODER_SSH_CONFIG_OPTIONS -import com.coder.toolbox.settings.CoderSettings -import com.coder.toolbox.settings.CoderSettingsState import com.coder.toolbox.settings.Environment +import com.coder.toolbox.store.BINARY_DIRECTORY +import com.coder.toolbox.store.BINARY_NAME +import com.coder.toolbox.store.BINARY_SOURCE +import com.coder.toolbox.store.CODER_SSH_CONFIG_OPTIONS +import com.coder.toolbox.store.CoderSecretsStore +import com.coder.toolbox.store.CoderSettingsStore +import com.coder.toolbox.store.DATA_DIRECTORY +import com.coder.toolbox.store.DISABLE_AUTOSTART +import com.coder.toolbox.store.ENABLE_BINARY_DIR_FALLBACK +import com.coder.toolbox.store.ENABLE_DOWNLOADS +import com.coder.toolbox.store.HEADER_COMMAND +import com.coder.toolbox.store.SSH_CONFIG_OPTIONS +import com.coder.toolbox.store.SSH_CONFIG_PATH +import com.coder.toolbox.store.SSH_LOG_DIR import com.coder.toolbox.util.InvalidVersionException import com.coder.toolbox.util.OS import com.coder.toolbox.util.SemVer import com.coder.toolbox.util.escape import com.coder.toolbox.util.getOS +import com.coder.toolbox.util.pluginTestSettingsStore import com.coder.toolbox.util.sha1 import com.coder.toolbox.util.toURL import com.jetbrains.toolbox.api.core.diagnostics.Logger @@ -53,8 +63,12 @@ internal class CoderCLIManagerTest { mockk(), mockk(relaxed = true), mockk(), - mockk(), - mockk() + CoderSettingsStore( + pluginTestSettingsStore(), + Environment(), + mockk(relaxed = true) + ), + mockk() ) /** @@ -107,7 +121,15 @@ internal class CoderCLIManagerTest { @Test fun testServerInternalError() { val (srv, url) = mockServer(HttpURLConnection.HTTP_INTERNAL_ERROR) - val ccm = CoderCLIManager(url, context.logger) + val ccm = CoderCLIManager( + url, + context.logger, + CoderSettingsStore( + pluginTestSettingsStore(), + Environment(), + mockk(relaxed = true) + ).readOnly() + ) val ex = assertFailsWith( @@ -121,14 +143,14 @@ internal class CoderCLIManagerTest { @Test fun testUsesSettings() { - val settings = - CoderSettings( - CoderSettingsState( - dataDirectory = tmpdir.resolve("cli-data-dir").toString(), - binaryDirectory = tmpdir.resolve("cli-bin-dir").toString(), - ), - context.logger - ) + val settings = CoderSettingsStore( + pluginTestSettingsStore( + DATA_DIRECTORY to tmpdir.resolve("cli-data-dir").toString(), + BINARY_DIRECTORY to tmpdir.resolve("cli-bin-dir").toString(), + ), + Environment(), + context.logger + ).readOnly() val url = URL("http://localhost") val ccm1 = CoderCLIManager(url, context.logger, settings) @@ -150,17 +172,17 @@ internal class CoderCLIManagerTest { } val (srv, url) = mockServer() - val ccm = - CoderCLIManager( - url, - context.logger, - CoderSettings( - CoderSettingsState( - dataDirectory = tmpdir.resolve("cli-dir-fail-to-write").toString(), - ), - context.logger + val ccm = CoderCLIManager( + url, + context.logger, + CoderSettingsStore( + pluginTestSettingsStore( + DATA_DIRECTORY to tmpdir.resolve("cli-dir-fail-to-write").toString(), ), - ) + Environment(), + context.logger + ).readOnly(), + ) ccm.localBinaryPath.parent.toFile().mkdirs() ccm.localBinaryPath.parent.toFile().setWritable(false) @@ -184,17 +206,17 @@ internal class CoderCLIManagerTest { url = "https://dev.coder.com" } - val ccm = - CoderCLIManager( - url.toURL(), - context.logger, - CoderSettings( - CoderSettingsState( - dataDirectory = tmpdir.resolve("real-cli").toString(), - ), - context.logger + val ccm = CoderCLIManager( + url.toURL(), + context.logger, + CoderSettingsStore( + pluginTestSettingsStore( + DATA_DIRECTORY to tmpdir.resolve("real-cli").toString(), ), - ) + Environment(), + context.logger + ).readOnly(), + ) assertTrue(ccm.download()) assertDoesNotThrow { ccm.version() } @@ -212,18 +234,18 @@ internal class CoderCLIManagerTest { @Test fun testDownloadMockCLI() { val (srv, url) = mockServer() - var ccm = - CoderCLIManager( - url, - context.logger, - CoderSettings( - CoderSettingsState( - dataDirectory = tmpdir.resolve("mock-cli").toString(), - ), - context.logger, - binaryName = "coder.bat", + var ccm = CoderCLIManager( + url, + context.logger, + CoderSettingsStore( + pluginTestSettingsStore( + BINARY_NAME to "coder.bat", + DATA_DIRECTORY to tmpdir.resolve("mock-cli").toString(), ), - ) + Environment(), + context.logger, + ).readOnly(), + ) assertEquals(true, ccm.download()) assertEquals(SemVer(url.port.toLong(), 0, 0), ccm.version()) @@ -232,18 +254,18 @@ internal class CoderCLIManagerTest { assertEquals(false, ccm.download()) // Should use the source override. - ccm = - CoderCLIManager( - url, - context.logger, - CoderSettings( - CoderSettingsState( - binarySource = "/bin/override", - dataDirectory = tmpdir.resolve("mock-cli").toString(), - ), - context.logger + ccm = CoderCLIManager( + url, + context.logger, + CoderSettingsStore( + pluginTestSettingsStore( + BINARY_SOURCE to "/bin/override", + DATA_DIRECTORY to tmpdir.resolve("mock-cli").toString(), ), - ) + Environment(), + context.logger + ).readOnly(), + ) assertEquals(true, ccm.download()) assertContains(ccm.localBinaryPath.toFile().readText(), "0.0.0") @@ -253,17 +275,17 @@ internal class CoderCLIManagerTest { @Test fun testRunNonExistentBinary() { - val ccm = - CoderCLIManager( - URL("https://foo"), - context.logger, - CoderSettings( - CoderSettingsState( - dataDirectory = tmpdir.resolve("does-not-exist").toString(), - ), - context.logger + val ccm = CoderCLIManager( + URL("https://foo"), + context.logger, + CoderSettingsStore( + pluginTestSettingsStore( + DATA_DIRECTORY to tmpdir.resolve("does-not-exist").toString(), ), - ) + Environment(), + context.logger + ).readOnly(), + ) assertFailsWith( exceptionClass = ProcessInitException::class, @@ -274,17 +296,17 @@ internal class CoderCLIManagerTest { @Test fun testOverwritesWrongVersion() { val (srv, url) = mockServer() - val ccm = - CoderCLIManager( - url, - context.logger, - CoderSettings( - CoderSettingsState( - dataDirectory = tmpdir.resolve("overwrite-cli").toString(), - ), - context.logger + val ccm = CoderCLIManager( + url, + context.logger, + CoderSettingsStore( + pluginTestSettingsStore( + DATA_DIRECTORY to tmpdir.resolve("overwrite-cli").toString(), ), - ) + Environment(), + context.logger + ).readOnly(), + ) ccm.localBinaryPath.parent.toFile().mkdirs() ccm.localBinaryPath.toFile().writeText("cli") @@ -307,13 +329,13 @@ internal class CoderCLIManagerTest { val (srv1, url1) = mockServer() val (srv2, url2) = mockServer() - val settings = - CoderSettings( - CoderSettingsState( - dataDirectory = tmpdir.resolve("clobber-cli").toString(), - ), - context.logger - ) + val settings = CoderSettingsStore( + pluginTestSettingsStore( + DATA_DIRECTORY to tmpdir.resolve("clobber-cli").toString(), + ), + Environment(), + context.logger + ).readOnly() val ccm1 = CoderCLIManager(url1, context.logger, settings) val ccm2 = CoderCLIManager(url2, context.logger, settings) @@ -438,28 +460,29 @@ internal class CoderCLIManagerTest { tests.forEach { val settings = - CoderSettings( - CoderSettingsState( - disableAutostart = it.disableAutostart, - dataDirectory = tmpdir.resolve("configure-ssh").toString(), - headerCommand = it.headerCommand, - sshConfigOptions = it.extraConfig, - sshLogDirectory = it.sshLogDirectory?.toString() ?: "", + CoderSettingsStore( + pluginTestSettingsStore( + DISABLE_AUTOSTART to it.disableAutostart.toString(), + DATA_DIRECTORY to tmpdir.resolve("configure-ssh").toString(), + HEADER_COMMAND to it.headerCommand, + SSH_CONFIG_PATH to tmpdir.resolve(it.input + "_to_" + it.output + ".conf").toString(), + SSH_CONFIG_OPTIONS to it.extraConfig, + SSH_LOG_DIR to (it.sshLogDirectory?.toString() ?: "") ), - context.logger, - sshConfigPath = tmpdir.resolve(it.input + "_to_" + it.output + ".conf"), env = it.env, - ) + context.logger, + ).readOnly() val ccm = CoderCLIManager(URL("https://test.coder.invalid"), context.logger, settings) + val sshConfigPath = Path.of(settings.sshConfigPath) // Input is the configuration that we start with, if any. if (it.input != null) { - settings.sshConfigPath.parent.toFile().mkdirs() + sshConfigPath.parent.toFile().mkdirs() val originalConf = Path.of("src/test/resources/fixtures/inputs").resolve(it.input + ".conf").toFile().readText() .replace(newlineRe, System.lineSeparator()) - settings.sshConfigPath.toFile().writeText(originalConf) + sshConfigPath.toFile().writeText(originalConf) } // Output is the configuration we expect to have after configuring. @@ -483,7 +506,7 @@ internal class CoderCLIManagerTest { // Add workspaces. ccm.configSsh(it.workspaces.toSet(), it.features) - assertEquals(expectedConf, settings.sshConfigPath.toFile().readText()) + assertEquals(expectedConf, sshConfigPath.toFile().readText()) // SSH log directory should have been created. if (it.sshLogDirectory != null) { @@ -495,7 +518,7 @@ internal class CoderCLIManagerTest { // Remove is the configuration we expect after removing. assertEquals( - settings.sshConfigPath.toFile().readText(), + sshConfigPath.toFile().readText(), Path.of("src/test/resources/fixtures/inputs").resolve(it.remove + ".conf").toFile() .readText().replace(newlineRe, System.lineSeparator()), ) @@ -513,15 +536,17 @@ internal class CoderCLIManagerTest { ) tests.forEach { - val settings = - CoderSettings( - CoderSettingsState(), - context.logger, - sshConfigPath = tmpdir.resolve("configured$it.conf"), - ) - settings.sshConfigPath.parent.toFile().mkdirs() + val settings = CoderSettingsStore( + pluginTestSettingsStore( + SSH_CONFIG_PATH to tmpdir.resolve("configured$it.conf").normalize().toString(), + ), + Environment(), + context.logger + ).readOnly() + val sshConfigPath = Path.of(settings.sshConfigPath) + sshConfigPath.parent.toFile().mkdirs() Path.of("src/test/resources/fixtures/inputs").resolve("$it.conf").toFile().copyTo( - settings.sshConfigPath.toFile(), + sshConfigPath.toFile(), true, ) @@ -542,17 +567,17 @@ internal class CoderCLIManagerTest { ) tests.forEach { - val ccm = - CoderCLIManager( - URL("https://test.coder.invalid"), - context.logger, - CoderSettings( - CoderSettingsState( - headerCommand = it, - ), - context.logger + val ccm = CoderCLIManager( + URL("https://test.coder.invalid"), + context.logger, + CoderSettingsStore( + pluginTestSettingsStore( + HEADER_COMMAND to it, ), - ) + Environment(), + context.logger + ).readOnly(), + ) assertFailsWith( exceptionClass = Exception::class, @@ -593,18 +618,18 @@ internal class CoderCLIManagerTest { exit(1) to InvalidExitValueException::class, ) - val ccm = - CoderCLIManager( - URL("https://test.coder.parse-fail.invalid"), - context.logger, - CoderSettings( - CoderSettingsState( - binaryDirectory = tmpdir.resolve("bad-version").toString(), - ), - context.logger, - binaryName = "coder.bat", + val ccm = CoderCLIManager( + URL("https://test.coder.parse-fail.invalid"), + context.logger, + CoderSettingsStore( + pluginTestSettingsStore( + BINARY_NAME to "coder.bat", + BINARY_DIRECTORY to tmpdir.resolve("bad-version").toString(), ), - ) + Environment(), + context.logger, + ).readOnly(), + ) ccm.localBinaryPath.parent.toFile().mkdirs() tests.forEach { @@ -646,18 +671,18 @@ internal class CoderCLIManagerTest { Triple(exit(1), "v1.0.0", null), ) - val ccm = - CoderCLIManager( - URL("https://test.coder.matches-version.invalid"), - context.logger, - CoderSettings( - CoderSettingsState( - binaryDirectory = tmpdir.resolve("matches-version").toString(), - ), - context.logger, - binaryName = "coder.bat", + val ccm = CoderCLIManager( + URL("https://test.coder.matches-version.invalid"), + context.logger, + CoderSettingsStore( + pluginTestSettingsStore( + BINARY_NAME to "coder.bat", + BINARY_DIRECTORY to tmpdir.resolve("matches-version").toString(), ), - ) + Environment(), + context.logger, + ).readOnly(), + ) ccm.localBinaryPath.parent.toFile().mkdirs() test.forEach { @@ -745,17 +770,18 @@ internal class CoderCLIManagerTest { val (srv, url) = mockServer() tests.forEach { - val settings = - CoderSettings( - CoderSettingsState( - enableDownloads = it.enableDownloads, - enableBinaryDirectoryFallback = it.enableFallback, - dataDirectory = tmpdir.resolve("ensure-data-dir").toString(), - binaryDirectory = tmpdir.resolve("ensure-bin-dir").toString(), - ), - context.logger - ) - + val settingsStore = CoderSettingsStore( + pluginTestSettingsStore( + ENABLE_DOWNLOADS to it.enableDownloads.toString(), + ENABLE_BINARY_DIR_FALLBACK to it.enableFallback.toString(), + DATA_DIRECTORY to tmpdir.resolve("ensure-data-dir").toString(), + BINARY_DIRECTORY to tmpdir.resolve("ensure-bin-dir").toString(), + ), + Environment(), + context.logger + ) + val settings = settingsStore.readOnly() + val localContext = context.copy(settingsStore = settingsStore) // Clean up from previous test. tmpdir.resolve("ensure-data-dir").toFile().deleteRecursively() tmpdir.resolve("ensure-bin-dir").toFile().deleteRecursively() @@ -784,12 +810,12 @@ internal class CoderCLIManagerTest { Result.ERROR -> { assertFailsWith( exceptionClass = AccessDeniedException::class, - block = { ensureCLI(context, url, it.buildVersion, settings) }, + block = { ensureCLI(localContext, url, it.buildVersion) }, ) } Result.NONE -> { - val ccm = ensureCLI(context, url, it.buildVersion, settings) + val ccm = ensureCLI(localContext, url, it.buildVersion) assertEquals(settings.binPath(url), ccm.localBinaryPath) assertFailsWith( exceptionClass = ProcessInitException::class, @@ -798,25 +824,25 @@ internal class CoderCLIManagerTest { } Result.DL_BIN -> { - val ccm = ensureCLI(context, url, it.buildVersion, settings) + val ccm = ensureCLI(localContext, url, it.buildVersion) assertEquals(settings.binPath(url), ccm.localBinaryPath) assertEquals(SemVer(url.port.toLong(), 0, 0), ccm.version()) } Result.DL_DATA -> { - val ccm = ensureCLI(context, url, it.buildVersion, settings) + val ccm = ensureCLI(localContext, url, it.buildVersion) assertEquals(settings.binPath(url, true), ccm.localBinaryPath) assertEquals(SemVer(url.port.toLong(), 0, 0), ccm.version()) } Result.USE_BIN -> { - val ccm = ensureCLI(context, url, it.buildVersion, settings) + val ccm = ensureCLI(localContext, url, it.buildVersion) assertEquals(settings.binPath(url), ccm.localBinaryPath) assertEquals(SemVer.parse(it.version ?: ""), ccm.version()) } Result.USE_DATA -> { - val ccm = ensureCLI(context, url, it.buildVersion, settings) + val ccm = ensureCLI(localContext, url, it.buildVersion) assertEquals(settings.binPath(url, true), ccm.localBinaryPath) assertEquals(SemVer.parse(it.fallbackVersion ?: ""), ccm.version()) } @@ -844,18 +870,18 @@ internal class CoderCLIManagerTest { tests.forEach { val (srv, url) = mockServer(version = it.first) - val ccm = - CoderCLIManager( - url, - context.logger, - CoderSettings( - CoderSettingsState( - dataDirectory = tmpdir.resolve("features").toString(), - ), - context.logger, - binaryName = "coder.bat", + val ccm = CoderCLIManager( + url, + context.logger, + CoderSettingsStore( + pluginTestSettingsStore( + BINARY_NAME to "coder.bat", + DATA_DIRECTORY to tmpdir.resolve("features").toString(), ), - ) + Environment(), + context.logger, + ).readOnly(), + ) assertEquals(true, ccm.download()) assertEquals(it.second, ccm.features, "version: ${it.first}") diff --git a/src/test/kotlin/com/coder/toolbox/sdk/CoderRestClientTest.kt b/src/test/kotlin/com/coder/toolbox/sdk/CoderRestClientTest.kt index a316af6..a02a38b 100644 --- a/src/test/kotlin/com/coder/toolbox/sdk/CoderRestClientTest.kt +++ b/src/test/kotlin/com/coder/toolbox/sdk/CoderRestClientTest.kt @@ -13,10 +13,12 @@ import com.coder.toolbox.sdk.v2.models.WorkspaceBuild import com.coder.toolbox.sdk.v2.models.WorkspaceResource import com.coder.toolbox.sdk.v2.models.WorkspaceTransition import com.coder.toolbox.sdk.v2.models.WorkspacesResponse -import com.coder.toolbox.services.CoderSecretsService -import com.coder.toolbox.services.CoderSettingsService -import com.coder.toolbox.settings.CoderSettings -import com.coder.toolbox.settings.CoderSettingsState +import com.coder.toolbox.settings.Environment +import com.coder.toolbox.store.CoderSecretsStore +import com.coder.toolbox.store.CoderSettingsStore +import com.coder.toolbox.store.TLS_ALTERNATE_HOSTNAME +import com.coder.toolbox.store.TLS_CA_PATH +import com.coder.toolbox.util.pluginTestSettingsStore import com.coder.toolbox.util.sslContextFromPEMs import com.jetbrains.toolbox.api.core.diagnostics.Logger import com.jetbrains.toolbox.api.localization.LocalizableStringFactory @@ -100,8 +102,8 @@ class CoderRestClientTest { mockk(), mockk(relaxed = true), mockk(), - mockk(), - mockk() + CoderSettingsStore(pluginTestSettingsStore(), Environment(), mockk(relaxed = true)), + mockk() ) data class TestWorkspace(var workspace: Workspace, var resources: List? = emptyList()) @@ -431,16 +433,17 @@ class CoderRestClientTest { @Test fun testValidSelfSignedCert() { val settings = - CoderSettings( - CoderSettingsState( - tlsCAPath = Path.of("src/test/resources/fixtures/tls", "self-signed.crt").toString(), - tlsAlternateHostname = "localhost", + CoderSettingsStore( + pluginTestSettingsStore( + TLS_CA_PATH to Path.of("src/test/resources/fixtures/tls", "self-signed.crt").toString(), + TLS_ALTERNATE_HOSTNAME to "localhost", ), + Environment(), context.logger ) val user = DataGen.user() val (srv, url) = mockTLSServer("self-signed") - val client = CoderRestClient(context, URL(url), "token", settings) + val client = CoderRestClient(context.copy(settingsStore = settings), URL(url), "token") srv.createContext( "/api/v2/users/me", BaseHttpHandler("GET") { exchange -> @@ -458,15 +461,16 @@ class CoderRestClientTest { @Test fun testWrongHostname() { val settings = - CoderSettings( - CoderSettingsState( - tlsCAPath = Path.of("src/test/resources/fixtures/tls", "self-signed.crt").toString(), - tlsAlternateHostname = "fake.example.com", + CoderSettingsStore( + pluginTestSettingsStore( + TLS_CA_PATH to Path.of("src/test/resources/fixtures/tls", "self-signed.crt").toString(), + TLS_ALTERNATE_HOSTNAME to "fake.example.com", ), + Environment(), context.logger ) val (srv, url) = mockTLSServer("self-signed") - val client = CoderRestClient(context, URL(url), "token", settings) + val client = CoderRestClient(context.copy(settingsStore = settings), URL(url), "token") assertFailsWith( exceptionClass = SSLPeerUnverifiedException::class, @@ -478,15 +482,15 @@ class CoderRestClientTest { @Test fun testCertNotTrusted() { - val settings = - CoderSettings( - CoderSettingsState( - tlsCAPath = Path.of("src/test/resources/fixtures/tls", "self-signed.crt").toString(), - ), - context.logger - ) + val settings = CoderSettingsStore( + pluginTestSettingsStore( + TLS_CA_PATH to Path.of("src/test/resources/fixtures/tls", "self-signed.crt").toString(), + ), + Environment(), + context.logger + ) val (srv, url) = mockTLSServer("no-signing") - val client = CoderRestClient(context, URL(url), "token", settings) + val client = CoderRestClient(context.copy(settingsStore = settings), URL(url), "token") assertFailsWith( exceptionClass = SSLHandshakeException::class, @@ -499,15 +503,16 @@ class CoderRestClientTest { @Test fun testValidChain() { val settings = - CoderSettings( - CoderSettingsState( - tlsCAPath = Path.of("src/test/resources/fixtures/tls", "chain-root.crt").toString(), + CoderSettingsStore( + pluginTestSettingsStore( + TLS_CA_PATH to Path.of("src/test/resources/fixtures/tls", "chain-root.crt").toString(), ), + Environment(), context.logger ) val user = DataGen.user() val (srv, url) = mockTLSServer("chain") - val client = CoderRestClient(context, URL(url), "token", settings) + val client = CoderRestClient(context.copy(settingsStore = settings), URL(url), "token") srv.createContext( "/api/v2/users/me", BaseHttpHandler("GET") { exchange -> @@ -524,7 +529,7 @@ class CoderRestClientTest { @Test fun usesProxy() { - val settings = CoderSettings(CoderSettingsState(), context.logger) + val settings = CoderSettingsStore(pluginTestSettingsStore(), Environment(), context.logger) val workspaces = listOf(DataGen.workspace("ws1")) val (srv1, url1) = mockServer() srv1.createContext( @@ -539,10 +544,9 @@ class CoderRestClientTest { val srv2 = mockProxy() val client = CoderRestClient( - context, + context.copy(settingsStore = settings), URL(url1), "token", - settings, ProxyValues( "foo", "bar", diff --git a/src/test/kotlin/com/coder/toolbox/settings/CoderSettingsTest.kt b/src/test/kotlin/com/coder/toolbox/settings/CoderSettingsTest.kt index 6a3e69e..6f2e978 100644 --- a/src/test/kotlin/com/coder/toolbox/settings/CoderSettingsTest.kt +++ b/src/test/kotlin/com/coder/toolbox/settings/CoderSettingsTest.kt @@ -1,7 +1,22 @@ package com.coder.toolbox.settings +import com.coder.toolbox.store.BINARY_NAME +import com.coder.toolbox.store.CODER_SSH_CONFIG_OPTIONS +import com.coder.toolbox.store.CoderSettingsStore +import com.coder.toolbox.store.DEFAULT_URL +import com.coder.toolbox.store.DISABLE_AUTOSTART +import com.coder.toolbox.store.ENABLE_BINARY_DIR_FALLBACK +import com.coder.toolbox.store.ENABLE_DOWNLOADS +import com.coder.toolbox.store.HEADER_COMMAND +import com.coder.toolbox.store.SSH_CONFIG_OPTIONS +import com.coder.toolbox.store.SSH_LOG_DIR +import com.coder.toolbox.store.TLS_ALTERNATE_HOSTNAME +import com.coder.toolbox.store.TLS_CA_PATH +import com.coder.toolbox.store.TLS_CERT_PATH +import com.coder.toolbox.store.TLS_KEY_PATH import com.coder.toolbox.util.OS import com.coder.toolbox.util.getOS +import com.coder.toolbox.util.pluginTestSettingsStore import com.coder.toolbox.util.withPath import com.jetbrains.toolbox.api.core.diagnostics.Logger import io.mockk.mockk @@ -17,35 +32,34 @@ internal class CoderSettingsTest { @Test fun testExpands() { - val state = CoderSettingsState() - val settings = CoderSettings(state, logger) + val settings = CoderSettingsStore(pluginTestSettingsStore(), Environment(), logger) val url = URL("http://localhost") val home = Path.of(System.getProperty("user.home")) - state.binaryDirectory = Path.of("~/coder-toolbox-test/expand-bin-dir").toString() + settings.updateBinaryDirectory(Path.of("~/coder-toolbox-test/expand-bin-dir").toString()) var expected = home.resolve("coder-toolbox-test/expand-bin-dir/localhost") - assertEquals(expected.toAbsolutePath(), settings.binPath(url).parent) + assertEquals(expected.toAbsolutePath(), settings.readOnly().binPath(url).parent) - state.dataDirectory = Path.of("~/coder-toolbox-test/expand-data-dir").toString() + settings.updateDataDirectory(Path.of("~/coder-toolbox-test/expand-data-dir").toString()) expected = home.resolve("coder-toolbox-test/expand-data-dir/localhost") - assertEquals(expected.toAbsolutePath(), settings.dataDir(url)) + assertEquals(expected.toAbsolutePath(), settings.readOnly().dataDir(url)) } @Test fun testDataDir() { - val state = CoderSettingsState() + val sharedStore = pluginTestSettingsStore() val url = URL("http://localhost") - var settings = - CoderSettings( - state, logger, - env = Environment( - mapOf( - "LOCALAPPDATA" to "/tmp/coder-toolbox-test/localappdata", - "HOME" to "/tmp/coder-toolbox-test/home", - "XDG_DATA_HOME" to "/tmp/coder-toolbox-test/xdg-data", - ), + var settings = CoderSettingsStore( + sharedStore, + Environment( + mapOf( + "LOCALAPPDATA" to "/tmp/coder-toolbox-test/localappdata", + "HOME" to "/tmp/coder-toolbox-test/home", + "XDG_DATA_HOME" to "/tmp/coder-toolbox-test/xdg-data", ), - ) + ), + logger, + ) var expected = when (getOS()) { OS.WINDOWS -> "/tmp/coder-toolbox-test/localappdata/coder-toolbox/localhost" @@ -53,124 +67,120 @@ internal class CoderSettingsTest { else -> "/tmp/coder-toolbox-test/xdg-data/coder-toolbox/localhost" } - assertEquals(Path.of(expected).toAbsolutePath(), settings.dataDir(url)) - assertEquals(Path.of(expected).toAbsolutePath(), settings.binPath(url).parent) + assertEquals(Path.of(expected).toAbsolutePath(), settings.readOnly().dataDir(url)) + assertEquals(Path.of(expected).toAbsolutePath(), settings.readOnly().binPath(url).parent) // Fall back to HOME on Linux. if (getOS() == OS.LINUX) { - settings = - CoderSettings( - state, logger, - env = - Environment( - mapOf( - "XDG_DATA_HOME" to "", - "HOME" to "/tmp/coder-toolbox-test/home", - ), - ), - ) + settings = CoderSettingsStore( + sharedStore, + Environment( + mapOf( + "XDG_DATA_HOME" to "", + "HOME" to "/tmp/coder-toolbox-test/home", + ), + ), + logger, + ) expected = "/tmp/coder-toolbox-test/home/.local/share/coder-toolbox/localhost" - assertEquals(Path.of(expected).toAbsolutePath(), settings.dataDir(url)) - assertEquals(Path.of(expected).toAbsolutePath(), settings.binPath(url).parent) + assertEquals(Path.of(expected).toAbsolutePath(), settings.readOnly().dataDir(url)) + assertEquals(Path.of(expected).toAbsolutePath(), settings.readOnly().binPath(url).parent) } // Override environment with settings. - state.dataDirectory = "/tmp/coder-toolbox-test/data-dir" - settings = - CoderSettings( - state, logger, - env = - Environment( - mapOf( - "LOCALAPPDATA" to "/ignore", - "HOME" to "/ignore", - "XDG_DATA_HOME" to "/ignore", - ), - ), - ) + settings.updateDataDirectory("/tmp/coder-toolbox-test/data-dir") + settings = CoderSettingsStore( + sharedStore, + Environment( + mapOf( + "LOCALAPPDATA" to "/ignore", + "HOME" to "/ignore", + "XDG_DATA_HOME" to "/ignore", + ), + ), + logger, + ) expected = "/tmp/coder-toolbox-test/data-dir/localhost" - assertEquals(Path.of(expected).toAbsolutePath(), settings.dataDir(url)) - assertEquals(Path.of(expected).toAbsolutePath(), settings.binPath(url).parent) + assertEquals(Path.of(expected).toAbsolutePath(), settings.readOnly().dataDir(url)) + assertEquals(Path.of(expected).toAbsolutePath(), settings.readOnly().binPath(url).parent) // Check that the URL is encoded and includes the port, also omit environment. val newUrl = URL("https://dev.😉-coder.com:8080") - state.dataDirectory = "/tmp/coder-toolbox-test/data-dir" - settings = CoderSettings(state, logger) + settings.updateDataDirectory("/tmp/coder-toolbox-test/data-dir") + settings = CoderSettingsStore(sharedStore, Environment(), logger) expected = "/tmp/coder-toolbox-test/data-dir/dev.xn---coder-vx74e.com-8080" - assertEquals(Path.of(expected).toAbsolutePath(), settings.dataDir(newUrl)) - assertEquals(Path.of(expected).toAbsolutePath(), settings.binPath(newUrl).parent) + assertEquals(Path.of(expected).toAbsolutePath(), settings.readOnly().dataDir(newUrl)) + assertEquals(Path.of(expected).toAbsolutePath(), settings.readOnly().binPath(newUrl).parent) } - @Test fun testBinPath() { - val state = CoderSettingsState() - val settings = CoderSettings(state, logger) - val settings2 = CoderSettings(state, logger, binaryName = "foo-bar.baz") + val settings = CoderSettingsStore( + pluginTestSettingsStore( + BINARY_NAME to "foo-bar.baz" + ), Environment(), logger + ) // The binary path should fall back to the data directory but that is // already tested in the data directory tests. val url = URL("http://localhost") // Override with settings. - state.binaryDirectory = "/tmp/coder-toolbox-test/bin-dir" + settings.updateBinaryDirectory("/tmp/coder-toolbox-test/bin-dir") var expected = "/tmp/coder-toolbox-test/bin-dir/localhost" - assertEquals(Path.of(expected).toAbsolutePath(), settings.binPath(url).parent) - assertEquals(Path.of(expected).toAbsolutePath(), settings2.binPath(url).parent) + assertEquals(Path.of(expected).toAbsolutePath(), settings.readOnly().binPath(url).parent) // Second argument bypasses override. - state.dataDirectory = "/tmp/coder-toolbox-test/data-dir" + settings.updateDataDirectory("/tmp/coder-toolbox-test/data-dir") expected = "/tmp/coder-toolbox-test/data-dir/localhost" - assertEquals(Path.of(expected).toAbsolutePath(), settings.binPath(url, true).parent) - assertEquals(Path.of(expected).toAbsolutePath(), settings2.binPath(url, true).parent) + assertEquals(Path.of(expected).toAbsolutePath(), settings.readOnly().binPath(url, true).parent) - assertNotEquals("foo-bar.baz", settings.binPath(url).fileName.toString()) - assertEquals("foo-bar.baz", settings2.binPath(url).fileName.toString()) + assertNotEquals("foo-bar.baz", settings.readOnly().binPath(url).fileName.toString()) } @Test fun testCoderConfigDir() { - val state = CoderSettingsState() - var settings = - CoderSettings( - state, logger, - env = - Environment( - mapOf( - "APPDATA" to "/tmp/coder-toolbox-test/cli-appdata", - "HOME" to "/tmp/coder-toolbox-test/cli-home", - "XDG_CONFIG_HOME" to "/tmp/coder-toolbox-test/cli-xdg-config", - ), + val localStore = pluginTestSettingsStore() + var settings = CoderSettingsStore( + localStore, + env = + Environment( + mapOf( + "APPDATA" to "/tmp/coder-toolbox-test/cli-appdata", + "HOME" to "/tmp/coder-toolbox-test/cli-home", + "XDG_CONFIG_HOME" to "/tmp/coder-toolbox-test/cli-xdg-config", ), - ) + ), + logger, + ) var expected = when (getOS()) { OS.WINDOWS -> "/tmp/coder-toolbox-test/cli-appdata/coderv2" OS.MAC -> "/tmp/coder-toolbox-test/cli-home/Library/Application Support/coderv2" else -> "/tmp/coder-toolbox-test/cli-xdg-config/coderv2" } - assertEquals(Path.of(expected), settings.coderConfigDir) + assertEquals(expected, settings.readOnly().globalConfigDir) // Fall back to HOME on Linux. if (getOS() == OS.LINUX) { - settings = - CoderSettings( - state, logger, - env = - Environment( - mapOf( - "XDG_CONFIG_HOME" to "", - "HOME" to "/tmp/coder-toolbox-test/cli-home", - ), + settings = CoderSettingsStore( + localStore, + env = + Environment( + mapOf( + "XDG_CONFIG_HOME" to "", + "HOME" to "/tmp/coder-toolbox-test/cli-home", ), - ) + ), + logger + ) expected = "/tmp/coder-toolbox-test/cli-home/.config/coderv2" - assertEquals(Path.of(expected), settings.coderConfigDir) + assertEquals(expected, settings.readOnly().globalConfigDir) } // Read CODER_CONFIG_DIR. settings = - CoderSettings( - state, logger, + CoderSettingsStore( + localStore, env = Environment( mapOf( @@ -180,30 +190,31 @@ internal class CoderSettingsTest { "XDG_CONFIG_HOME" to "/ignore", ), ), + logger ) expected = "/tmp/coder-toolbox-test/coder-config-dir" - assertEquals(Path.of(expected), settings.coderConfigDir) + assertEquals(expected, settings.readOnly().globalConfigDir) } @Test fun binSource() { - val state = CoderSettingsState() - val settings = CoderSettings(state, logger) + val localStore = pluginTestSettingsStore() + val settings = CoderSettingsStore(localStore, Environment(), logger) // As-is if no source override. val url = URL("http://localhost/") assertContains( - settings.binSource(url).toString(), + settings.readOnly().binSource(url).toString(), url.withPath("/bin/coder-").toString(), ) // Override with absolute URL. val absolute = URL("http://dev.coder.com/some-path") - state.binarySource = absolute.toString() - assertEquals(absolute, settings.binSource(url)) + settings.updateBinarySource(absolute.toString()) + assertEquals(absolute, settings.readOnly().binSource(url)) // Override with relative URL. - state.binarySource = "/relative/path" - assertEquals(url.withPath("/relative/path"), settings.binSource(url)) + settings.updateBinarySource("/relative/path") + assertEquals(url.withPath("/relative/path"), settings.readOnly().binSource(url)) } @Test @@ -215,51 +226,55 @@ internal class CoderSettingsTest { expected.resolve("url").toFile().writeText("http://test.toolbox.coder.com$expected") expected.resolve("session").toFile().writeText("fake-token") - var got = CoderSettings(CoderSettingsState(), logger).readConfig(expected) + var got = CoderSettingsStore(pluginTestSettingsStore(), Environment(), logger).readOnly().readConfig(expected) assertEquals(Pair("http://test.toolbox.coder.com$expected", "fake-token"), got) // Ignore token if missing. expected.resolve("session").toFile().delete() - got = CoderSettings(CoderSettingsState(), logger).readConfig(expected) + got = CoderSettingsStore(pluginTestSettingsStore(), Environment(), logger).readOnly().readConfig(expected) assertEquals(Pair("http://test.toolbox.coder.com$expected", null), got) } @Test fun testSSHConfigOptions() { - var settings = CoderSettings(CoderSettingsState(sshConfigOptions = "ssh config options from state"), logger) - assertEquals("ssh config options from state", settings.sshConfigOptions) + var settings = CoderSettingsStore( + pluginTestSettingsStore(SSH_CONFIG_OPTIONS to "ssh config options from state"), + Environment(), logger + ) + assertEquals("ssh config options from state", settings.readOnly().sshConfigOptions) - settings = - CoderSettings( - CoderSettingsState(), - logger, - env = Environment(mapOf(CODER_SSH_CONFIG_OPTIONS to "ssh config options from env")), - ) - assertEquals("ssh config options from env", settings.sshConfigOptions) + settings = CoderSettingsStore( + pluginTestSettingsStore(), + env = Environment(mapOf(CODER_SSH_CONFIG_OPTIONS to "ssh config options from env")), + logger + ) + assertEquals("ssh config options from env", settings.readOnly().sshConfigOptions) // State has precedence. - settings = - CoderSettings( - CoderSettingsState(sshConfigOptions = "ssh config options from state"), - logger, - env = Environment(mapOf(CODER_SSH_CONFIG_OPTIONS to "ssh config options from env")), - ) - assertEquals("ssh config options from state", settings.sshConfigOptions) + settings = CoderSettingsStore( + pluginTestSettingsStore(SSH_CONFIG_OPTIONS to "ssh config options from state"), + env = Environment(mapOf(CODER_SSH_CONFIG_OPTIONS to "ssh config options from env")), + logger + ) + assertEquals("ssh config options from state", settings.readOnly().sshConfigOptions) } @Test fun testRequireTokenAuth() { - var settings = CoderSettings(CoderSettingsState(), logger) - assertEquals(true, settings.requireTokenAuth) + var settings = CoderSettingsStore(pluginTestSettingsStore(), Environment(), logger) + assertEquals(true, settings.readOnly().requireTokenAuth) - settings = CoderSettings(CoderSettingsState(tlsCertPath = "cert path"), logger) - assertEquals(true, settings.requireTokenAuth) + settings = CoderSettingsStore(pluginTestSettingsStore(TLS_CERT_PATH to "cert path"), Environment(), logger) + assertEquals(true, settings.readOnly().requireTokenAuth) - settings = CoderSettings(CoderSettingsState(tlsKeyPath = "key path"), logger) - assertEquals(true, settings.requireTokenAuth) + settings = CoderSettingsStore(pluginTestSettingsStore(TLS_KEY_PATH to "key path"), Environment(), logger) + assertEquals(true, settings.readOnly().requireTokenAuth) - settings = CoderSettings(CoderSettingsState(tlsCertPath = "cert path", tlsKeyPath = "key path"), logger) - assertEquals(false, settings.requireTokenAuth) + settings = CoderSettingsStore( + pluginTestSettingsStore(TLS_CERT_PATH to "cert path", TLS_KEY_PATH to "key path"), + Environment(), logger + ) + assertEquals(false, settings.readOnly().requireTokenAuth) } @Test @@ -270,15 +285,15 @@ internal class CoderSettingsTest { dir.toFile().deleteRecursively() // No config. - var settings = CoderSettings(CoderSettingsState(), logger, env = env) + var settings = CoderSettingsStore(pluginTestSettingsStore(), env, logger) assertEquals(null, settings.defaultURL()) // Read from global config. - val globalConfigPath = settings.coderConfigDir + val globalConfigPath = Path.of(settings.readOnly().globalConfigDir) globalConfigPath.toFile().mkdirs() globalConfigPath.resolve("url").toFile().writeText("url-from-global-config") - settings = CoderSettings(CoderSettingsState(), logger, env = env) - assertEquals("url-from-global-config" to Source.CONFIG, settings.defaultURL()) + settings = CoderSettingsStore(pluginTestSettingsStore(), env, logger) + assertEquals("url-from-global-config" to SettingSource.CONFIG, settings.defaultURL()) // Read from environment. env = @@ -288,19 +303,19 @@ internal class CoderSettingsTest { "CODER_CONFIG_DIR" to dir.toString(), ), ) - settings = CoderSettings(CoderSettingsState(), logger, env = env) - assertEquals("url-from-env" to Source.ENVIRONMENT, settings.defaultURL()) + settings = CoderSettingsStore(pluginTestSettingsStore(), env, logger) + assertEquals("url-from-env" to SettingSource.ENVIRONMENT, settings.defaultURL()) // Read from settings. settings = - CoderSettings( - CoderSettingsState( - defaultURL = "url-from-settings", + CoderSettingsStore( + pluginTestSettingsStore( + DEFAULT_URL to "url-from-settings", ), - logger, - env = env, + env, + logger ) - assertEquals("url-from-settings" to Source.SETTINGS, settings.defaultURL()) + assertEquals("url-from-settings" to SettingSource.SETTINGS, settings.defaultURL()) } @Test @@ -320,94 +335,92 @@ internal class CoderSettingsTest { dir.toFile().deleteRecursively() // No config. - var settings = CoderSettings(CoderSettingsState(), logger, env = env) - assertEquals(null, settings.token(url)) + var settings = CoderSettingsStore(pluginTestSettingsStore(), env, logger) + assertEquals(null, settings.readOnly().token(url)) - val globalConfigPath = settings.coderConfigDir + val globalConfigPath = Path.of(settings.readOnly().globalConfigDir) globalConfigPath.toFile().mkdirs() globalConfigPath.resolve("url").toFile().writeText(url.toString()) globalConfigPath.resolve("session").toFile().writeText("token-from-global-config") // Ignore global config if it does not match. - assertEquals(null, settings.token(URL("http://some.random.url"))) + assertEquals(null, settings.readOnly().token(URL("http://some.random.url"))) // Read from global config. - assertEquals("token-from-global-config" to Source.CONFIG, settings.token(url)) + assertEquals("token-from-global-config" to SettingSource.CONFIG, settings.readOnly().token(url)) // Compares exactly. - assertEquals(null, settings.token(url.withPath("/test"))) + assertEquals(null, settings.readOnly().token(url.withPath("/test"))) - val deploymentConfigPath = settings.dataDir(url).resolve("config") + val deploymentConfigPath = settings.readOnly().dataDir(url).resolve("config") deploymentConfigPath.toFile().mkdirs() deploymentConfigPath.resolve("url").toFile().writeText("url-from-deployment-config") deploymentConfigPath.resolve("session").toFile().writeText("token-from-deployment-config") // Read from deployment config. - assertEquals("token-from-deployment-config" to Source.DEPLOYMENT_CONFIG, settings.token(url)) + assertEquals("token-from-deployment-config" to SettingSource.DEPLOYMENT_CONFIG, settings.readOnly().token(url)) // Only compares host . - assertEquals("token-from-deployment-config" to Source.DEPLOYMENT_CONFIG, settings.token(url.withPath("/test"))) + assertEquals( + "token-from-deployment-config" to SettingSource.DEPLOYMENT_CONFIG, + settings.readOnly().token(url.withPath("/test")) + ) // Ignore if using mTLS. settings = - CoderSettings( - CoderSettingsState( - tlsKeyPath = "key", - tlsCertPath = "cert", + CoderSettingsStore( + pluginTestSettingsStore( + TLS_KEY_PATH to "key", + TLS_CERT_PATH to "cert", ), - logger, - env = env, + env, + logger ) - assertEquals(null, settings.token(url)) + assertEquals(null, settings.readOnly().token(url)) } @Test fun testDefaults() { // Test defaults for the remaining settings. - val settings = CoderSettings(CoderSettingsState(), logger) - assertEquals(true, settings.enableDownloads) - assertEquals(false, settings.enableBinaryDirectoryFallback) - assertEquals("", settings.headerCommand) - assertEquals("", settings.tls.certPath) - assertEquals("", settings.tls.keyPath) - assertEquals("", settings.tls.caPath) - assertEquals("", settings.tls.altHostname) - assertEquals(getOS() == OS.MAC, settings.disableAutostart) - assertEquals("", settings.setupCommand) - assertEquals(false, settings.ignoreSetupFailure) + val settings = CoderSettingsStore(pluginTestSettingsStore(), Environment(), logger) + assertEquals(true, settings.readOnly().enableDownloads) + assertEquals(false, settings.readOnly().enableBinaryDirectoryFallback) + assertEquals(null, settings.readOnly().headerCommand) + assertEquals(null, settings.readOnly().tls.certPath) + assertEquals(null, settings.readOnly().tls.keyPath) + assertEquals(null, settings.readOnly().tls.caPath) + assertEquals(null, settings.readOnly().tls.altHostname) + assertEquals(getOS() == OS.MAC, settings.readOnly().disableAutostart) } @Test fun testSettings() { // Make sure the remaining settings are being conveyed. val settings = - CoderSettings( - CoderSettingsState( - enableDownloads = false, - enableBinaryDirectoryFallback = true, - headerCommand = "test header", - tlsCertPath = "tls cert path", - tlsKeyPath = "tls key path", - tlsCAPath = "tls ca path", - tlsAlternateHostname = "tls alt hostname", - disableAutostart = getOS() != OS.MAC, - setupCommand = "test setup", - ignoreSetupFailure = true, - sshLogDirectory = "test ssh log directory", + CoderSettingsStore( + pluginTestSettingsStore( + ENABLE_DOWNLOADS to false.toString(), + ENABLE_BINARY_DIR_FALLBACK to true.toString(), + HEADER_COMMAND to "test header", + TLS_CERT_PATH to "tls cert path", + TLS_KEY_PATH to "tls key path", + TLS_CA_PATH to "tls ca path", + TLS_ALTERNATE_HOSTNAME to "tls alt hostname", + DISABLE_AUTOSTART to (getOS() != OS.MAC).toString(), + SSH_LOG_DIR to "test ssh log directory", ), + Environment(), logger, ) - assertEquals(false, settings.enableDownloads) - assertEquals(true, settings.enableBinaryDirectoryFallback) - assertEquals("test header", settings.headerCommand) - assertEquals("tls cert path", settings.tls.certPath) - assertEquals("tls key path", settings.tls.keyPath) - assertEquals("tls ca path", settings.tls.caPath) - assertEquals("tls alt hostname", settings.tls.altHostname) - assertEquals(getOS() != OS.MAC, settings.disableAutostart) - assertEquals("test setup", settings.setupCommand) - assertEquals(true, settings.ignoreSetupFailure) - assertEquals("test ssh log directory", settings.sshLogDirectory) + assertEquals(false, settings.readOnly().enableDownloads) + assertEquals(true, settings.readOnly().enableBinaryDirectoryFallback) + assertEquals("test header", settings.readOnly().headerCommand) + assertEquals("tls cert path", settings.readOnly().tls.certPath) + assertEquals("tls key path", settings.readOnly().tls.keyPath) + assertEquals("tls ca path", settings.readOnly().tls.caPath) + assertEquals("tls alt hostname", settings.readOnly().tls.altHostname) + assertEquals(getOS() != OS.MAC, settings.readOnly().disableAutostart) + assertEquals("test ssh log directory", settings.readOnly().sshLogDirectory) } } diff --git a/src/test/kotlin/com/coder/toolbox/util/PluginSettingsStoreUtil.kt b/src/test/kotlin/com/coder/toolbox/util/PluginSettingsStoreUtil.kt new file mode 100644 index 0000000..236d40a --- /dev/null +++ b/src/test/kotlin/com/coder/toolbox/util/PluginSettingsStoreUtil.kt @@ -0,0 +1,15 @@ +package com.coder.toolbox.util + +import com.jetbrains.toolbox.api.core.PluginSettingsStore + + +fun pluginTestSettingsStore(): PluginSettingsStore = PluginTestSettingsStoreImpl() + +fun pluginTestSettingsStore(vararg pairs: Pair): PluginSettingsStore = + PluginTestSettingsStoreImpl().apply { + putAll(pairs) + } + +private class PluginTestSettingsStoreImpl( + private val backingMap: MutableMap = HashMap() +) : PluginSettingsStore, MutableMap by backingMap \ No newline at end of file From a47e4b26ae94289dd353739ad7d84fe4e4469446 Mon Sep 17 00:00:00 2001 From: Faur Ioan-Aurel Date: Thu, 27 Mar 2025 01:23:22 +0200 Subject: [PATCH 13/21] impl: support for ssh wildcard config - resolves #46 - it is configurable in the Coder's plugin setting page --- .../com/coder/toolbox/cli/CoderCLIManager.kt | 49 ++++++++++++++----- .../coder/toolbox/settings/CoderSettings.kt | 2 + .../coder/toolbox/store/CoderSettingsStore.kt | 7 ++- .../com/coder/toolbox/store/StoreKeys.kt | 2 + .../coder/toolbox/views/CoderSettingsPage.kt | 5 ++ 5 files changed, 53 insertions(+), 12 deletions(-) diff --git a/src/main/kotlin/com/coder/toolbox/cli/CoderCLIManager.kt b/src/main/kotlin/com/coder/toolbox/cli/CoderCLIManager.kt index e6e776a..2ca4707 100644 --- a/src/main/kotlin/com/coder/toolbox/cli/CoderCLIManager.kt +++ b/src/main/kotlin/com/coder/toolbox/cli/CoderCLIManager.kt @@ -112,6 +112,7 @@ fun ensureCLI( data class Features( val disableAutostart: Boolean = false, val reportWorkspaceUsage: Boolean = false, + val wildcardSsh: Boolean = false, ) /** @@ -282,7 +283,37 @@ class CoderCLIManager( } else { "" } - val blockContent = + val options = """ + ConnectTimeout 0 + StrictHostKeyChecking no + UserKnownHostsFile /dev/null + LogLevel ERROR + SetEnv CODER_SSH_SESSION_TYPE=JetBrains + """.trimIndent() + + val blockContent = if (settings.isSshWildcardConfigEnabled && feats.wildcardSsh) { + startBlock + System.lineSeparator() + + """ + Host ${getWildcardHost(deploymentURL)}--* + ProxyCommand ${proxyArgs.joinToString(" ")} --ssh-host-prefix ${getWildcardHost(deploymentURL)}-- %h + """.trimIndent() + .plus("\n" + options.prependIndent(" ")) + .plus(extraConfig) + .plus("\n\n") + .plus( + """ + Host ${getWildcardHost(deploymentURL)}-bg--* + ProxyCommand ${backgroundProxyArgs.joinToString(" ")} --ssh-host-prefix ${ + getWildcardHost( + deploymentURL + ) + }-bg-- %h + """.trimIndent() + .plus("\n" + options.prependIndent(" ")) + .plus(extraConfig), + ).replace("\n", System.lineSeparator()) + + System.lineSeparator() + endBlock + } else { workspaceNames.joinToString( System.lineSeparator(), startBlock + System.lineSeparator(), @@ -291,28 +322,21 @@ class CoderCLIManager( """ Host ${getHostName(deploymentURL, it)} ProxyCommand ${proxyArgs.joinToString(" ")} $it - ConnectTimeout 0 - StrictHostKeyChecking no - UserKnownHostsFile /dev/null - LogLevel ERROR - SetEnv CODER_SSH_SESSION_TYPE=JetBrains """.trimIndent() + .plus("\n" + options.prependIndent(" ")) .plus(extraConfig) .plus("\n") .plus( """ Host ${getBackgroundHostName(deploymentURL, it)} ProxyCommand ${backgroundProxyArgs.joinToString(" ")} $it - ConnectTimeout 0 - StrictHostKeyChecking no - UserKnownHostsFile /dev/null - LogLevel ERROR - SetEnv CODER_SSH_SESSION_TYPE=JetBrains """.trimIndent() + .plus("\n" + options.prependIndent(" ")) .plus(extraConfig), ).replace("\n", System.lineSeparator()) }, ) + } if (contents == null) { logger.info("No existing SSH config to modify") @@ -475,6 +499,7 @@ class CoderCLIManager( Features( disableAutostart = version >= SemVer(2, 5, 0), reportWorkspaceUsage = version >= SemVer(2, 13, 0), + version >= SemVer(2, 19, 0), ) } } @@ -482,6 +507,8 @@ class CoderCLIManager( companion object { private val tokenRegex = "--token [^ ]+".toRegex() + fun getWildcardHost(url: URL): String = "coder-jetbrains-toolbox--${url.safeHost()}" + @JvmStatic fun getHostName( url: URL, diff --git a/src/main/kotlin/com/coder/toolbox/settings/CoderSettings.kt b/src/main/kotlin/com/coder/toolbox/settings/CoderSettings.kt index 3cc7ee6..867159c 100644 --- a/src/main/kotlin/com/coder/toolbox/settings/CoderSettings.kt +++ b/src/main/kotlin/com/coder/toolbox/settings/CoderSettings.kt @@ -86,6 +86,8 @@ data class CoderSettings( */ val disableAutostart: Boolean, + val isSshWildcardConfigEnabled: Boolean, + /** * The location of the SSH config. Defaults to ~/.ssh/config. */ diff --git a/src/main/kotlin/com/coder/toolbox/store/CoderSettingsStore.kt b/src/main/kotlin/com/coder/toolbox/store/CoderSettingsStore.kt index 9321056..d4a9606 100644 --- a/src/main/kotlin/com/coder/toolbox/store/CoderSettingsStore.kt +++ b/src/main/kotlin/com/coder/toolbox/store/CoderSettingsStore.kt @@ -37,7 +37,7 @@ class CoderSettingsStore( altHostname = store[TLS_ALTERNATE_HOSTNAME] ), disableAutostart = store[DISABLE_AUTOSTART]?.toBooleanStrictOrNull() ?: (getOS() == OS.MAC), - + isSshWildcardConfigEnabled = store[ENABLE_SSH_WILDCARD_CONFIG]?.toBooleanStrictOrNull() ?: false, sshConfigPath = store[SSH_CONFIG_PATH].takeUnless { it.isNullOrEmpty() } ?: Path.of(System.getProperty("user.home")).resolve(".ssh/config").normalize().toString(), sshLogDirectory = store[SSH_LOG_DIR], @@ -124,6 +124,11 @@ class CoderSettingsStore( store[DISABLE_AUTOSTART] = shouldDisableAutostart.toString() } + fun updateEnableSshWildcardConfig(enable: Boolean) { + backingSettings = backingSettings.copy(isSshWildcardConfigEnabled = enable) + store[ENABLE_SSH_WILDCARD_CONFIG] = enable.toString() + } + fun updateSshLogDir(path: String) { backingSettings = backingSettings.copy(sshLogDirectory = path) store[SSH_LOG_DIR] = path diff --git a/src/main/kotlin/com/coder/toolbox/store/StoreKeys.kt b/src/main/kotlin/com/coder/toolbox/store/StoreKeys.kt index 2d929ec..35040e3 100644 --- a/src/main/kotlin/com/coder/toolbox/store/StoreKeys.kt +++ b/src/main/kotlin/com/coder/toolbox/store/StoreKeys.kt @@ -30,6 +30,8 @@ internal const val TLS_ALTERNATE_HOSTNAME = "tlsAlternateHostname" internal const val DISABLE_AUTOSTART = "disableAutostart" +internal const val ENABLE_SSH_WILDCARD_CONFIG = "enableSshWildcardConfig" + internal const val SSH_CONFIG_PATH = "sshConfigPath" internal const val SSH_LOG_DIR = "sshLogDir" diff --git a/src/main/kotlin/com/coder/toolbox/views/CoderSettingsPage.kt b/src/main/kotlin/com/coder/toolbox/views/CoderSettingsPage.kt index c97537d..d4ac2c8 100644 --- a/src/main/kotlin/com/coder/toolbox/views/CoderSettingsPage.kt +++ b/src/main/kotlin/com/coder/toolbox/views/CoderSettingsPage.kt @@ -45,6 +45,9 @@ class CoderSettingsPage(context: CoderToolboxContext) : CoderPage(context, conte TextField(context.i18n.ptrl("TLS alternate hostname"), settings.tls.altHostname ?: "", TextType.General) private val disableAutostartField = CheckboxField(settings.disableAutostart, context.i18n.ptrl("Disable autostart")) + + private val enableSshWildCardConfig = + CheckboxField(settings.isSshWildcardConfigEnabled, context.i18n.ptrl("Enable SSH wildcard config")) private val sshExtraArgs = TextField(context.i18n.ptrl("Extra SSH options"), settings.sshConfigOptions ?: "", TextType.General) private val sshLogDirField = @@ -64,6 +67,7 @@ class CoderSettingsPage(context: CoderToolboxContext) : CoderPage(context, conte tlsCAPathField, tlsAlternateHostnameField, disableAutostartField, + enableSshWildCardConfig, sshLogDirField, sshExtraArgs, ) @@ -83,6 +87,7 @@ class CoderSettingsPage(context: CoderToolboxContext) : CoderPage(context, conte context.settingsStore.updateCAPath(tlsCAPathField.textState.value) context.settingsStore.updateAltHostname(tlsAlternateHostnameField.textState.value) context.settingsStore.updateDisableAutostart(disableAutostartField.checkedState.value) + context.settingsStore.updateEnableSshWildcardConfig(enableSshWildCardConfig.checkedState.value) context.settingsStore.updateSshLogDir(sshLogDirField.textState.value) context.settingsStore.updateSshConfigOptions(sshExtraArgs.textState.value) } From b183af0a790be51602e9eb4fb35bc0dc793f8322 Mon Sep 17 00:00:00 2001 From: Faur Ioan-Aurel Date: Thu, 27 Mar 2025 01:26:43 +0200 Subject: [PATCH 14/21] fix: missing i18n strings --- src/main/resources/localization/defaultMessages.po | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/resources/localization/defaultMessages.po b/src/main/resources/localization/defaultMessages.po index 9572682..f109f4f 100644 --- a/src/main/resources/localization/defaultMessages.po +++ b/src/main/resources/localization/defaultMessages.po @@ -149,4 +149,13 @@ msgid "Connect" msgstr "" msgid "Can't handle URI because workspace is not running and autostart is disabled. Please start the workspace manually and execute the URI again." -msgstr "" \ No newline at end of file +msgstr "" + +msgid "Enable SSH wildcard config" +msgstr "" + +msgid "Extra SSH options" +msgstr "" + +msgid "SSH proxy log directory" +msgstr "" From de8f9f06f71bfb87c77c79e46ccc55758f762d7d Mon Sep 17 00:00:00 2001 From: Faur Ioan-Aurel Date: Thu, 27 Mar 2025 01:28:29 +0200 Subject: [PATCH 15/21] impl: change plugin's display name - from Coder Toolbox to Coder --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index e0b1b04..42a1a79 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -71,7 +71,7 @@ val extension = ExtensionJson( version = properties("version"), meta = ExtensionJsonMeta( - name = "Coder Toolbox", + name = "Coder", description = "Connects your JetBrains IDE to Coder workspaces", vendor = "Coder", url = "https://github.com/coder/coder-jetbrains-toolbox-plugin", From 71cabd12d215c18923b95339cd032fdbe1145a48 Mon Sep 17 00:00:00 2001 From: Faur Ioan-Aurel Date: Thu, 27 Mar 2025 01:30:59 +0200 Subject: [PATCH 16/21] chore: revert --usage-app - back to jetbrains because there is no tracking support for toolbox, yet. --- src/main/kotlin/com/coder/toolbox/cli/CoderCLIManager.kt | 2 +- .../resources/fixtures/outputs/append-blank-newlines.conf | 2 +- src/test/resources/fixtures/outputs/append-blank.conf | 2 +- src/test/resources/fixtures/outputs/append-no-blocks.conf | 2 +- src/test/resources/fixtures/outputs/append-no-newline.conf | 2 +- .../resources/fixtures/outputs/append-no-related-blocks.conf | 2 +- src/test/resources/fixtures/outputs/disable-autostart.conf | 2 +- src/test/resources/fixtures/outputs/extra-config.conf | 2 +- .../resources/fixtures/outputs/header-command-windows.conf | 2 +- src/test/resources/fixtures/outputs/header-command.conf | 2 +- src/test/resources/fixtures/outputs/log-dir.conf | 2 +- src/test/resources/fixtures/outputs/multiple-workspaces.conf | 4 ++-- src/test/resources/fixtures/outputs/no-disable-autostart.conf | 2 +- .../resources/fixtures/outputs/replace-end-no-newline.conf | 2 +- src/test/resources/fixtures/outputs/replace-end.conf | 2 +- .../fixtures/outputs/replace-middle-ignore-unrelated.conf | 2 +- src/test/resources/fixtures/outputs/replace-middle.conf | 2 +- src/test/resources/fixtures/outputs/replace-only.conf | 2 +- src/test/resources/fixtures/outputs/replace-start.conf | 2 +- 19 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/main/kotlin/com/coder/toolbox/cli/CoderCLIManager.kt b/src/main/kotlin/com/coder/toolbox/cli/CoderCLIManager.kt index 2ca4707..595e8f7 100644 --- a/src/main/kotlin/com/coder/toolbox/cli/CoderCLIManager.kt +++ b/src/main/kotlin/com/coder/toolbox/cli/CoderCLIManager.kt @@ -273,7 +273,7 @@ class CoderCLIManager( val proxyArgs = baseArgs + listOfNotNull( if (!settings.sshLogDirectory.isNullOrBlank()) "--log-dir" else null, if (!settings.sshLogDirectory.isNullOrBlank()) escape(settings.sshLogDirectory) else null, - if (feats.reportWorkspaceUsage) "--usage-app=toolbox" else null, + if (feats.reportWorkspaceUsage) "--usage-app=jetbrains" else null, ) val backgroundProxyArgs = baseArgs + listOfNotNull(if (feats.reportWorkspaceUsage) "--usage-app=disable" else null) diff --git a/src/test/resources/fixtures/outputs/append-blank-newlines.conf b/src/test/resources/fixtures/outputs/append-blank-newlines.conf index 0adbc15..5691f22 100644 --- a/src/test/resources/fixtures/outputs/append-blank-newlines.conf +++ b/src/test/resources/fixtures/outputs/append-blank-newlines.conf @@ -4,7 +4,7 @@ # --- START CODER JETBRAINS TOOLBOX test.coder.invalid Host coder-jetbrains-toolbox--foo-bar--test.coder.invalid - ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=toolbox foo-bar + ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=jetbrains foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null diff --git a/src/test/resources/fixtures/outputs/append-blank.conf b/src/test/resources/fixtures/outputs/append-blank.conf index 7cea0dc..58519f6 100644 --- a/src/test/resources/fixtures/outputs/append-blank.conf +++ b/src/test/resources/fixtures/outputs/append-blank.conf @@ -1,6 +1,6 @@ # --- START CODER JETBRAINS TOOLBOX test.coder.invalid Host coder-jetbrains-toolbox--foo-bar--test.coder.invalid - ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=toolbox foo-bar + ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=jetbrains foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null diff --git a/src/test/resources/fixtures/outputs/append-no-blocks.conf b/src/test/resources/fixtures/outputs/append-no-blocks.conf index 3fd2205..f8210b5 100644 --- a/src/test/resources/fixtures/outputs/append-no-blocks.conf +++ b/src/test/resources/fixtures/outputs/append-no-blocks.conf @@ -5,7 +5,7 @@ Host test2 # --- START CODER JETBRAINS TOOLBOX test.coder.invalid Host coder-jetbrains-toolbox--foo-bar--test.coder.invalid - ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=toolbox foo-bar + ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=jetbrains foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null diff --git a/src/test/resources/fixtures/outputs/append-no-newline.conf b/src/test/resources/fixtures/outputs/append-no-newline.conf index 47b12d7..02954a4 100644 --- a/src/test/resources/fixtures/outputs/append-no-newline.conf +++ b/src/test/resources/fixtures/outputs/append-no-newline.conf @@ -4,7 +4,7 @@ Host test2 Port 443 # --- START CODER JETBRAINS TOOLBOX test.coder.invalid Host coder-jetbrains-toolbox--foo-bar--test.coder.invalid - ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=toolbox foo-bar + ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=jetbrains foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null diff --git a/src/test/resources/fixtures/outputs/append-no-related-blocks.conf b/src/test/resources/fixtures/outputs/append-no-related-blocks.conf index f387826..ecdd155 100644 --- a/src/test/resources/fixtures/outputs/append-no-related-blocks.conf +++ b/src/test/resources/fixtures/outputs/append-no-related-blocks.conf @@ -11,7 +11,7 @@ some jetbrains config # --- START CODER JETBRAINS TOOLBOX test.coder.invalid Host coder-jetbrains-toolbox--foo-bar--test.coder.invalid - ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=toolbox foo-bar + ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=jetbrains foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null diff --git a/src/test/resources/fixtures/outputs/disable-autostart.conf b/src/test/resources/fixtures/outputs/disable-autostart.conf index 2c2b730..11edcad 100644 --- a/src/test/resources/fixtures/outputs/disable-autostart.conf +++ b/src/test/resources/fixtures/outputs/disable-autostart.conf @@ -1,6 +1,6 @@ # --- START CODER JETBRAINS TOOLBOX test.coder.invalid Host coder-jetbrains-toolbox--foo--test.coder.invalid - ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --disable-autostart --usage-app=toolbox foo + ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --disable-autostart --usage-app=jetbrains foo ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null diff --git a/src/test/resources/fixtures/outputs/extra-config.conf b/src/test/resources/fixtures/outputs/extra-config.conf index ee0c9b5..c384c8f 100644 --- a/src/test/resources/fixtures/outputs/extra-config.conf +++ b/src/test/resources/fixtures/outputs/extra-config.conf @@ -1,6 +1,6 @@ # --- START CODER JETBRAINS TOOLBOX test.coder.invalid Host coder-jetbrains-toolbox--extra--test.coder.invalid - ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=toolbox extra + ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=jetbrains extra ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null diff --git a/src/test/resources/fixtures/outputs/header-command-windows.conf b/src/test/resources/fixtures/outputs/header-command-windows.conf index 496b577..9714f31 100644 --- a/src/test/resources/fixtures/outputs/header-command-windows.conf +++ b/src/test/resources/fixtures/outputs/header-command-windows.conf @@ -1,6 +1,6 @@ # --- START CODER JETBRAINS TOOLBOX test.coder.invalid Host coder-jetbrains-toolbox--header--test.coder.invalid - ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid --header-command "\"C:\Program Files\My Header Command\HeaderCommand.exe\" --url=\"%%CODER_URL%%\" --test=\"foo bar\"" ssh --stdio --usage-app=toolbox header + ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid --header-command "\"C:\Program Files\My Header Command\HeaderCommand.exe\" --url=\"%%CODER_URL%%\" --test=\"foo bar\"" ssh --stdio --usage-app=jetbrains header ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null diff --git a/src/test/resources/fixtures/outputs/header-command.conf b/src/test/resources/fixtures/outputs/header-command.conf index fc7e902..65af2b5 100644 --- a/src/test/resources/fixtures/outputs/header-command.conf +++ b/src/test/resources/fixtures/outputs/header-command.conf @@ -1,6 +1,6 @@ # --- START CODER JETBRAINS TOOLBOX test.coder.invalid Host coder-jetbrains-toolbox--header--test.coder.invalid - ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid --header-command 'my-header-command --url="$CODER_URL" --test="foo bar" --literal='\''$CODER_URL'\''' ssh --stdio --usage-app=toolbox header + ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid --header-command 'my-header-command --url="$CODER_URL" --test="foo bar" --literal='\''$CODER_URL'\''' ssh --stdio --usage-app=jetbrains header ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null diff --git a/src/test/resources/fixtures/outputs/log-dir.conf b/src/test/resources/fixtures/outputs/log-dir.conf index 8da54ef..f4a378d 100644 --- a/src/test/resources/fixtures/outputs/log-dir.conf +++ b/src/test/resources/fixtures/outputs/log-dir.conf @@ -1,6 +1,6 @@ # --- START CODER JETBRAINS TOOLBOX test.coder.invalid Host coder-jetbrains-toolbox--foo--test.coder.invalid - ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --log-dir /tmp/coder-toolbox/test.coder.invalid/logs --usage-app=toolbox foo + ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --log-dir /tmp/coder-toolbox/test.coder.invalid/logs --usage-app=jetbrains foo ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null diff --git a/src/test/resources/fixtures/outputs/multiple-workspaces.conf b/src/test/resources/fixtures/outputs/multiple-workspaces.conf index 9cd883b..999214c 100644 --- a/src/test/resources/fixtures/outputs/multiple-workspaces.conf +++ b/src/test/resources/fixtures/outputs/multiple-workspaces.conf @@ -1,6 +1,6 @@ # --- START CODER JETBRAINS TOOLBOX test.coder.invalid Host coder-jetbrains-toolbox--foo--test.coder.invalid - ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=toolbox foo + ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=jetbrains foo ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null @@ -14,7 +14,7 @@ Host coder-jetbrains-toolbox--foo--test.coder.invalid--bg LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains Host coder-jetbrains-toolbox--bar--test.coder.invalid - ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=toolbox bar + ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=jetbrains bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null diff --git a/src/test/resources/fixtures/outputs/no-disable-autostart.conf b/src/test/resources/fixtures/outputs/no-disable-autostart.conf index 4bcc966..8e05e28 100644 --- a/src/test/resources/fixtures/outputs/no-disable-autostart.conf +++ b/src/test/resources/fixtures/outputs/no-disable-autostart.conf @@ -1,6 +1,6 @@ # --- START CODER JETBRAINS TOOLBOX test.coder.invalid Host coder-jetbrains-toolbox--foo--test.coder.invalid - ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=toolbox foo + ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=jetbrains foo ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null diff --git a/src/test/resources/fixtures/outputs/replace-end-no-newline.conf b/src/test/resources/fixtures/outputs/replace-end-no-newline.conf index 132a79f..8911750 100644 --- a/src/test/resources/fixtures/outputs/replace-end-no-newline.conf +++ b/src/test/resources/fixtures/outputs/replace-end-no-newline.conf @@ -3,7 +3,7 @@ Host test Host test2 Port 443 # --- START CODER JETBRAINS TOOLBOX test.coder.invalid Host coder-jetbrains-toolbox--foo-bar--test.coder.invalid - ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=toolbox foo-bar + ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=jetbrains foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null diff --git a/src/test/resources/fixtures/outputs/replace-end.conf b/src/test/resources/fixtures/outputs/replace-end.conf index 47b12d7..02954a4 100644 --- a/src/test/resources/fixtures/outputs/replace-end.conf +++ b/src/test/resources/fixtures/outputs/replace-end.conf @@ -4,7 +4,7 @@ Host test2 Port 443 # --- START CODER JETBRAINS TOOLBOX test.coder.invalid Host coder-jetbrains-toolbox--foo-bar--test.coder.invalid - ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=toolbox foo-bar + ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=jetbrains foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null diff --git a/src/test/resources/fixtures/outputs/replace-middle-ignore-unrelated.conf b/src/test/resources/fixtures/outputs/replace-middle-ignore-unrelated.conf index b5838c3..49bc113 100644 --- a/src/test/resources/fixtures/outputs/replace-middle-ignore-unrelated.conf +++ b/src/test/resources/fixtures/outputs/replace-middle-ignore-unrelated.conf @@ -5,7 +5,7 @@ some coder config # ------------END-CODER------------ # --- START CODER JETBRAINS TOOLBOX test.coder.invalid Host coder-jetbrains-toolbox--foo-bar--test.coder.invalid - ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=toolbox foo-bar + ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=jetbrains foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null diff --git a/src/test/resources/fixtures/outputs/replace-middle.conf b/src/test/resources/fixtures/outputs/replace-middle.conf index 2b5ba5a..7b6cd6c 100644 --- a/src/test/resources/fixtures/outputs/replace-middle.conf +++ b/src/test/resources/fixtures/outputs/replace-middle.conf @@ -2,7 +2,7 @@ Host test Port 80 # --- START CODER JETBRAINS TOOLBOX test.coder.invalid Host coder-jetbrains-toolbox--foo-bar--test.coder.invalid - ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=toolbox foo-bar + ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=jetbrains foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null diff --git a/src/test/resources/fixtures/outputs/replace-only.conf b/src/test/resources/fixtures/outputs/replace-only.conf index 7cea0dc..58519f6 100644 --- a/src/test/resources/fixtures/outputs/replace-only.conf +++ b/src/test/resources/fixtures/outputs/replace-only.conf @@ -1,6 +1,6 @@ # --- START CODER JETBRAINS TOOLBOX test.coder.invalid Host coder-jetbrains-toolbox--foo-bar--test.coder.invalid - ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=toolbox foo-bar + ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=jetbrains foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null diff --git a/src/test/resources/fixtures/outputs/replace-start.conf b/src/test/resources/fixtures/outputs/replace-start.conf index 52505f5..d6fa5a3 100644 --- a/src/test/resources/fixtures/outputs/replace-start.conf +++ b/src/test/resources/fixtures/outputs/replace-start.conf @@ -1,6 +1,6 @@ # --- START CODER JETBRAINS TOOLBOX test.coder.invalid Host coder-jetbrains-toolbox--foo-bar--test.coder.invalid - ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=toolbox foo-bar + ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=jetbrains foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null From 67ec90631446ff7c948205383c3ff1464f6745a8 Mon Sep 17 00:00:00 2001 From: Faur Ioan-Aurel Date: Thu, 27 Mar 2025 01:32:31 +0200 Subject: [PATCH 17/21] fix: test exercising the features available --- src/test/kotlin/com/coder/toolbox/cli/CoderCLIManagerTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/kotlin/com/coder/toolbox/cli/CoderCLIManagerTest.kt b/src/test/kotlin/com/coder/toolbox/cli/CoderCLIManagerTest.kt index e3806c7..ca9040e 100644 --- a/src/test/kotlin/com/coder/toolbox/cli/CoderCLIManagerTest.kt +++ b/src/test/kotlin/com/coder/toolbox/cli/CoderCLIManagerTest.kt @@ -863,7 +863,7 @@ internal class CoderCLIManagerTest { listOf( Pair("2.5.0", Features(true)), Pair("2.13.0", Features(true, true)), - Pair("4.9.0", Features(true, true)), + Pair("4.9.0", Features(true, true, true)), Pair("2.4.9", Features(false)), Pair("1.0.1", Features(false)), ) From 76bb8b875b15ab8fd442baa6b7124724440cee43 Mon Sep 17 00:00:00 2001 From: Faur Ioan-Aurel Date: Thu, 27 Mar 2025 01:43:38 +0200 Subject: [PATCH 18/21] fix: test assert raw string paths on different platforms - use Java's Path to do platform independent path comparison --- .../com/coder/toolbox/settings/CoderSettingsTest.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/test/kotlin/com/coder/toolbox/settings/CoderSettingsTest.kt b/src/test/kotlin/com/coder/toolbox/settings/CoderSettingsTest.kt index 6f2e978..d80b237 100644 --- a/src/test/kotlin/com/coder/toolbox/settings/CoderSettingsTest.kt +++ b/src/test/kotlin/com/coder/toolbox/settings/CoderSettingsTest.kt @@ -25,7 +25,6 @@ import java.nio.file.Path import kotlin.test.Test import kotlin.test.assertContains import kotlin.test.assertEquals -import kotlin.test.assertNotEquals internal class CoderSettingsTest { private val logger = mockk(relaxed = true) @@ -114,6 +113,7 @@ internal class CoderSettingsTest { assertEquals(Path.of(expected).toAbsolutePath(), settings.readOnly().binPath(newUrl).parent) } + @Test fun testBinPath() { val settings = CoderSettingsStore( pluginTestSettingsStore( @@ -134,7 +134,7 @@ internal class CoderSettingsTest { expected = "/tmp/coder-toolbox-test/data-dir/localhost" assertEquals(Path.of(expected).toAbsolutePath(), settings.readOnly().binPath(url, true).parent) - assertNotEquals("foo-bar.baz", settings.readOnly().binPath(url).fileName.toString()) + assertEquals("foo-bar.baz", settings.readOnly().binPath(url).fileName.toString()) } @Test @@ -158,7 +158,7 @@ internal class CoderSettingsTest { OS.MAC -> "/tmp/coder-toolbox-test/cli-home/Library/Application Support/coderv2" else -> "/tmp/coder-toolbox-test/cli-xdg-config/coderv2" } - assertEquals(expected, settings.readOnly().globalConfigDir) + assertEquals(Path.of(expected), Path.of(settings.readOnly().globalConfigDir)) // Fall back to HOME on Linux. if (getOS() == OS.LINUX) { @@ -174,7 +174,7 @@ internal class CoderSettingsTest { logger ) expected = "/tmp/coder-toolbox-test/cli-home/.config/coderv2" - assertEquals(expected, settings.readOnly().globalConfigDir) + assertEquals(Path.of(expected), Path.of(settings.readOnly().globalConfigDir)) } // Read CODER_CONFIG_DIR. @@ -193,7 +193,7 @@ internal class CoderSettingsTest { logger ) expected = "/tmp/coder-toolbox-test/coder-config-dir" - assertEquals(expected, settings.readOnly().globalConfigDir) + assertEquals(Path.of(expected), Path.of(settings.readOnly().globalConfigDir)) } @Test From a53684d9f5aaf612700d2d09b603034f83fdfc82 Mon Sep 17 00:00:00 2001 From: Faur Ioan-Aurel Date: Thu, 27 Mar 2025 11:33:18 +0200 Subject: [PATCH 19/21] fix: add support for Toolbox 2.6.0.39689 - slf4j api is now bundled and exposed by Toolbox --- build.gradle.kts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 42a1a79..3dcab05 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -145,7 +145,8 @@ fun CopySpec.fromCompileDependencies() { "core-api", "ui-api", "annotations", - "localization-api" + "localization-api", + "slf4j-api" ).any { file.name.contains(it) } } }, From fd860d12d7d136e970327721cd1ddd59db431914 Mon Sep 17 00:00:00 2001 From: Faur Ioan-Aurel Date: Thu, 27 Mar 2025 21:06:20 +0200 Subject: [PATCH 20/21] impl: enable wildcard config by default --- src/main/kotlin/com/coder/toolbox/store/CoderSettingsStore.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/com/coder/toolbox/store/CoderSettingsStore.kt b/src/main/kotlin/com/coder/toolbox/store/CoderSettingsStore.kt index d4a9606..e5b96a1 100644 --- a/src/main/kotlin/com/coder/toolbox/store/CoderSettingsStore.kt +++ b/src/main/kotlin/com/coder/toolbox/store/CoderSettingsStore.kt @@ -37,7 +37,7 @@ class CoderSettingsStore( altHostname = store[TLS_ALTERNATE_HOSTNAME] ), disableAutostart = store[DISABLE_AUTOSTART]?.toBooleanStrictOrNull() ?: (getOS() == OS.MAC), - isSshWildcardConfigEnabled = store[ENABLE_SSH_WILDCARD_CONFIG]?.toBooleanStrictOrNull() ?: false, + isSshWildcardConfigEnabled = store[ENABLE_SSH_WILDCARD_CONFIG]?.toBooleanStrictOrNull() ?: true, sshConfigPath = store[SSH_CONFIG_PATH].takeUnless { it.isNullOrEmpty() } ?: Path.of(System.getProperty("user.home")).resolve(".ssh/config").normalize().toString(), sshLogDirectory = store[SSH_LOG_DIR], From 81eacfb7b1102d3bb528c8226c38bd9af9cdf68a Mon Sep 17 00:00:00 2001 From: Faur Ioan-Aurel Date: Thu, 27 Mar 2025 23:15:53 +0200 Subject: [PATCH 21/21] fix: provide the correct ssh host when wildcard ssh config is enabled - Toolbox calls the Coder environment to get SSH connection information. In this particular case for wildcard SSH connections the hostname is a bit different. - this patch computes the correct hostname depending on whether wildcard ssh config is enabled or not. --- .../coder/toolbox/CoderRemoteEnvironment.kt | 7 ++++- .../com/coder/toolbox/cli/CoderCLIManager.kt | 27 ++++++++++--------- .../coder/toolbox/views/EnvironmentView.kt | 9 ++++++- .../outputs/append-blank-newlines.conf | 4 +-- .../fixtures/outputs/append-blank.conf | 4 +-- .../fixtures/outputs/append-no-blocks.conf | 4 +-- .../fixtures/outputs/append-no-newline.conf | 4 +-- .../outputs/append-no-related-blocks.conf | 4 +-- .../fixtures/outputs/disable-autostart.conf | 4 +-- .../fixtures/outputs/extra-config.conf | 4 +-- .../outputs/header-command-windows.conf | 4 +-- .../fixtures/outputs/header-command.conf | 4 +-- .../resources/fixtures/outputs/log-dir.conf | 4 +-- .../fixtures/outputs/multiple-workspaces.conf | 8 +++--- .../outputs/no-disable-autostart.conf | 4 +-- .../fixtures/outputs/no-report-usage.conf | 4 +-- .../outputs/replace-end-no-newline.conf | 4 +-- .../fixtures/outputs/replace-end.conf | 4 +-- .../replace-middle-ignore-unrelated.conf | 4 +-- .../fixtures/outputs/replace-middle.conf | 4 +-- .../fixtures/outputs/replace-only.conf | 4 +-- .../fixtures/outputs/replace-start.conf | 4 +-- 22 files changed, 68 insertions(+), 55 deletions(-) diff --git a/src/main/kotlin/com/coder/toolbox/CoderRemoteEnvironment.kt b/src/main/kotlin/com/coder/toolbox/CoderRemoteEnvironment.kt index d9e7d95..8f265fe 100644 --- a/src/main/kotlin/com/coder/toolbox/CoderRemoteEnvironment.kt +++ b/src/main/kotlin/com/coder/toolbox/CoderRemoteEnvironment.kt @@ -123,7 +123,12 @@ class CoderRemoteEnvironment( * have to do is provide it a host name. */ override suspend - fun getContentsView(): EnvironmentContentsView = EnvironmentView(client.url, workspace, agent) + fun getContentsView(): EnvironmentContentsView = EnvironmentView( + context.settingsStore.readOnly(), + client.url, + workspace, + agent + ) override val connectionRequest: MutableStateFlow? = MutableStateFlow(false) diff --git a/src/main/kotlin/com/coder/toolbox/cli/CoderCLIManager.kt b/src/main/kotlin/com/coder/toolbox/cli/CoderCLIManager.kt index 595e8f7..d112c18 100644 --- a/src/main/kotlin/com/coder/toolbox/cli/CoderCLIManager.kt +++ b/src/main/kotlin/com/coder/toolbox/cli/CoderCLIManager.kt @@ -4,6 +4,8 @@ import com.coder.toolbox.CoderToolboxContext import com.coder.toolbox.cli.ex.MissingVersionException import com.coder.toolbox.cli.ex.ResponseException import com.coder.toolbox.cli.ex.SSHConfigFormatException +import com.coder.toolbox.sdk.v2.models.Workspace +import com.coder.toolbox.sdk.v2.models.WorkspaceAgent import com.coder.toolbox.settings.CoderSettings import com.coder.toolbox.util.CoderHostnameVerifier import com.coder.toolbox.util.InvalidVersionException @@ -294,17 +296,17 @@ class CoderCLIManager( val blockContent = if (settings.isSshWildcardConfigEnabled && feats.wildcardSsh) { startBlock + System.lineSeparator() + """ - Host ${getWildcardHost(deploymentURL)}--* - ProxyCommand ${proxyArgs.joinToString(" ")} --ssh-host-prefix ${getWildcardHost(deploymentURL)}-- %h + Host ${getHostnamePrefix(deploymentURL)}--* + ProxyCommand ${proxyArgs.joinToString(" ")} --ssh-host-prefix ${getHostnamePrefix(deploymentURL)}-- %h """.trimIndent() .plus("\n" + options.prependIndent(" ")) .plus(extraConfig) .plus("\n\n") .plus( """ - Host ${getWildcardHost(deploymentURL)}-bg--* + Host ${getHostnamePrefix(deploymentURL)}-bg--* ProxyCommand ${backgroundProxyArgs.joinToString(" ")} --ssh-host-prefix ${ - getWildcardHost( + getHostnamePrefix( deploymentURL ) }-bg-- %h @@ -507,23 +509,22 @@ class CoderCLIManager( companion object { private val tokenRegex = "--token [^ ]+".toRegex() - fun getWildcardHost(url: URL): String = "coder-jetbrains-toolbox--${url.safeHost()}" + fun getHostnamePrefix(url: URL): String = "coder-jetbrains-toolbox-${url.safeHost()}" + + fun getWildcardHostname(url: URL, workspace: Workspace, agent: WorkspaceAgent): String = + "${getHostnamePrefix(url)}-bg--${workspace.name}.${agent.name}" + + fun getHostname(url: URL, workspace: Workspace, agent: WorkspaceAgent) = + getHostName(url, "${workspace.name}.${agent.name}") - @JvmStatic fun getHostName( url: URL, workspaceName: String, - ): String = "coder-jetbrains-toolbox--$workspaceName--${url.safeHost()}" + ): String = "coder-jetbrains-toolbox-$workspaceName--${url.safeHost()}" - @JvmStatic fun getBackgroundHostName( url: URL, workspaceName: String, ): String = getHostName(url, workspaceName) + "--bg" - - @JvmStatic - fun getBackgroundHostName( - hostname: String, - ): String = hostname + "--bg" } } diff --git a/src/main/kotlin/com/coder/toolbox/views/EnvironmentView.kt b/src/main/kotlin/com/coder/toolbox/views/EnvironmentView.kt index ebee9fe..4f41eee 100644 --- a/src/main/kotlin/com/coder/toolbox/views/EnvironmentView.kt +++ b/src/main/kotlin/com/coder/toolbox/views/EnvironmentView.kt @@ -3,6 +3,7 @@ package com.coder.toolbox.views import com.coder.toolbox.cli.CoderCLIManager import com.coder.toolbox.sdk.v2.models.Workspace import com.coder.toolbox.sdk.v2.models.WorkspaceAgent +import com.coder.toolbox.settings.CoderSettings import com.jetbrains.toolbox.api.remoteDev.environments.SshEnvironmentContentsView import com.jetbrains.toolbox.api.remoteDev.ssh.SshConnectionInfo import java.net.URL @@ -16,6 +17,7 @@ import java.net.URL * SSH must be configured before this will work. */ class EnvironmentView( + private val settings: CoderSettings, private val url: URL, private val workspace: Workspace, private val agent: WorkspaceAgent, @@ -24,7 +26,7 @@ class EnvironmentView( /** * The host name generated by the cli manager for this workspace. */ - override val host: String = CoderCLIManager.getHostName(url, "${workspace.name}.${agent.name}") + override val host: String = resolveHost() /** * The port is ignored by the Coder proxy command. @@ -37,4 +39,9 @@ class EnvironmentView( override val userName: String? = "coder" } + + private fun resolveHost(): String = + if (settings.isSshWildcardConfigEnabled) + CoderCLIManager.getWildcardHostname(url, workspace, agent) + else CoderCLIManager.getHostname(url, workspace, agent) } diff --git a/src/test/resources/fixtures/outputs/append-blank-newlines.conf b/src/test/resources/fixtures/outputs/append-blank-newlines.conf index 5691f22..7124556 100644 --- a/src/test/resources/fixtures/outputs/append-blank-newlines.conf +++ b/src/test/resources/fixtures/outputs/append-blank-newlines.conf @@ -3,14 +3,14 @@ # --- START CODER JETBRAINS TOOLBOX test.coder.invalid -Host coder-jetbrains-toolbox--foo-bar--test.coder.invalid +Host coder-jetbrains-toolbox-foo-bar--test.coder.invalid ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=jetbrains foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-jetbrains-toolbox--foo-bar--test.coder.invalid--bg +Host coder-jetbrains-toolbox-foo-bar--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=disable foo-bar ConnectTimeout 0 StrictHostKeyChecking no diff --git a/src/test/resources/fixtures/outputs/append-blank.conf b/src/test/resources/fixtures/outputs/append-blank.conf index 58519f6..d884838 100644 --- a/src/test/resources/fixtures/outputs/append-blank.conf +++ b/src/test/resources/fixtures/outputs/append-blank.conf @@ -1,12 +1,12 @@ # --- START CODER JETBRAINS TOOLBOX test.coder.invalid -Host coder-jetbrains-toolbox--foo-bar--test.coder.invalid +Host coder-jetbrains-toolbox-foo-bar--test.coder.invalid ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=jetbrains foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-jetbrains-toolbox--foo-bar--test.coder.invalid--bg +Host coder-jetbrains-toolbox-foo-bar--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=disable foo-bar ConnectTimeout 0 StrictHostKeyChecking no diff --git a/src/test/resources/fixtures/outputs/append-no-blocks.conf b/src/test/resources/fixtures/outputs/append-no-blocks.conf index f8210b5..e4c161b 100644 --- a/src/test/resources/fixtures/outputs/append-no-blocks.conf +++ b/src/test/resources/fixtures/outputs/append-no-blocks.conf @@ -4,14 +4,14 @@ Host test2 Port 443 # --- START CODER JETBRAINS TOOLBOX test.coder.invalid -Host coder-jetbrains-toolbox--foo-bar--test.coder.invalid +Host coder-jetbrains-toolbox-foo-bar--test.coder.invalid ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=jetbrains foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-jetbrains-toolbox--foo-bar--test.coder.invalid--bg +Host coder-jetbrains-toolbox-foo-bar--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=disable foo-bar ConnectTimeout 0 StrictHostKeyChecking no diff --git a/src/test/resources/fixtures/outputs/append-no-newline.conf b/src/test/resources/fixtures/outputs/append-no-newline.conf index 02954a4..b5b9d2c 100644 --- a/src/test/resources/fixtures/outputs/append-no-newline.conf +++ b/src/test/resources/fixtures/outputs/append-no-newline.conf @@ -3,14 +3,14 @@ Host test Host test2 Port 443 # --- START CODER JETBRAINS TOOLBOX test.coder.invalid -Host coder-jetbrains-toolbox--foo-bar--test.coder.invalid +Host coder-jetbrains-toolbox-foo-bar--test.coder.invalid ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=jetbrains foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-jetbrains-toolbox--foo-bar--test.coder.invalid--bg +Host coder-jetbrains-toolbox-foo-bar--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=disable foo-bar ConnectTimeout 0 StrictHostKeyChecking no diff --git a/src/test/resources/fixtures/outputs/append-no-related-blocks.conf b/src/test/resources/fixtures/outputs/append-no-related-blocks.conf index ecdd155..87446f5 100644 --- a/src/test/resources/fixtures/outputs/append-no-related-blocks.conf +++ b/src/test/resources/fixtures/outputs/append-no-related-blocks.conf @@ -10,14 +10,14 @@ some jetbrains config # --- END CODER JETBRAINS TOOLBOX test.coder.unrelated # --- START CODER JETBRAINS TOOLBOX test.coder.invalid -Host coder-jetbrains-toolbox--foo-bar--test.coder.invalid +Host coder-jetbrains-toolbox-foo-bar--test.coder.invalid ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=jetbrains foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-jetbrains-toolbox--foo-bar--test.coder.invalid--bg +Host coder-jetbrains-toolbox-foo-bar--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=disable foo-bar ConnectTimeout 0 StrictHostKeyChecking no diff --git a/src/test/resources/fixtures/outputs/disable-autostart.conf b/src/test/resources/fixtures/outputs/disable-autostart.conf index 11edcad..cf993f8 100644 --- a/src/test/resources/fixtures/outputs/disable-autostart.conf +++ b/src/test/resources/fixtures/outputs/disable-autostart.conf @@ -1,12 +1,12 @@ # --- START CODER JETBRAINS TOOLBOX test.coder.invalid -Host coder-jetbrains-toolbox--foo--test.coder.invalid +Host coder-jetbrains-toolbox-foo--test.coder.invalid ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --disable-autostart --usage-app=jetbrains foo ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-jetbrains-toolbox--foo--test.coder.invalid--bg +Host coder-jetbrains-toolbox-foo--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --disable-autostart --usage-app=disable foo ConnectTimeout 0 StrictHostKeyChecking no diff --git a/src/test/resources/fixtures/outputs/extra-config.conf b/src/test/resources/fixtures/outputs/extra-config.conf index c384c8f..3acb86d 100644 --- a/src/test/resources/fixtures/outputs/extra-config.conf +++ b/src/test/resources/fixtures/outputs/extra-config.conf @@ -1,5 +1,5 @@ # --- START CODER JETBRAINS TOOLBOX test.coder.invalid -Host coder-jetbrains-toolbox--extra--test.coder.invalid +Host coder-jetbrains-toolbox-extra--test.coder.invalid ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=jetbrains extra ConnectTimeout 0 StrictHostKeyChecking no @@ -8,7 +8,7 @@ Host coder-jetbrains-toolbox--extra--test.coder.invalid SetEnv CODER_SSH_SESSION_TYPE=JetBrains ServerAliveInterval 5 ServerAliveCountMax 3 -Host coder-jetbrains-toolbox--extra--test.coder.invalid--bg +Host coder-jetbrains-toolbox-extra--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=disable extra ConnectTimeout 0 StrictHostKeyChecking no diff --git a/src/test/resources/fixtures/outputs/header-command-windows.conf b/src/test/resources/fixtures/outputs/header-command-windows.conf index 9714f31..84d0529 100644 --- a/src/test/resources/fixtures/outputs/header-command-windows.conf +++ b/src/test/resources/fixtures/outputs/header-command-windows.conf @@ -1,12 +1,12 @@ # --- START CODER JETBRAINS TOOLBOX test.coder.invalid -Host coder-jetbrains-toolbox--header--test.coder.invalid +Host coder-jetbrains-toolbox-header--test.coder.invalid ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid --header-command "\"C:\Program Files\My Header Command\HeaderCommand.exe\" --url=\"%%CODER_URL%%\" --test=\"foo bar\"" ssh --stdio --usage-app=jetbrains header ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-jetbrains-toolbox--header--test.coder.invalid--bg +Host coder-jetbrains-toolbox-header--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid --header-command "\"C:\Program Files\My Header Command\HeaderCommand.exe\" --url=\"%%CODER_URL%%\" --test=\"foo bar\"" ssh --stdio --usage-app=disable header ConnectTimeout 0 StrictHostKeyChecking no diff --git a/src/test/resources/fixtures/outputs/header-command.conf b/src/test/resources/fixtures/outputs/header-command.conf index 65af2b5..c8ee5cd 100644 --- a/src/test/resources/fixtures/outputs/header-command.conf +++ b/src/test/resources/fixtures/outputs/header-command.conf @@ -1,12 +1,12 @@ # --- START CODER JETBRAINS TOOLBOX test.coder.invalid -Host coder-jetbrains-toolbox--header--test.coder.invalid +Host coder-jetbrains-toolbox-header--test.coder.invalid ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid --header-command 'my-header-command --url="$CODER_URL" --test="foo bar" --literal='\''$CODER_URL'\''' ssh --stdio --usage-app=jetbrains header ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-jetbrains-toolbox--header--test.coder.invalid--bg +Host coder-jetbrains-toolbox-header--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid --header-command 'my-header-command --url="$CODER_URL" --test="foo bar" --literal='\''$CODER_URL'\''' ssh --stdio --usage-app=disable header ConnectTimeout 0 StrictHostKeyChecking no diff --git a/src/test/resources/fixtures/outputs/log-dir.conf b/src/test/resources/fixtures/outputs/log-dir.conf index f4a378d..a0be236 100644 --- a/src/test/resources/fixtures/outputs/log-dir.conf +++ b/src/test/resources/fixtures/outputs/log-dir.conf @@ -1,12 +1,12 @@ # --- START CODER JETBRAINS TOOLBOX test.coder.invalid -Host coder-jetbrains-toolbox--foo--test.coder.invalid +Host coder-jetbrains-toolbox-foo--test.coder.invalid ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --log-dir /tmp/coder-toolbox/test.coder.invalid/logs --usage-app=jetbrains foo ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-jetbrains-toolbox--foo--test.coder.invalid--bg +Host coder-jetbrains-toolbox-foo--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=disable foo ConnectTimeout 0 StrictHostKeyChecking no diff --git a/src/test/resources/fixtures/outputs/multiple-workspaces.conf b/src/test/resources/fixtures/outputs/multiple-workspaces.conf index 999214c..e54e00c 100644 --- a/src/test/resources/fixtures/outputs/multiple-workspaces.conf +++ b/src/test/resources/fixtures/outputs/multiple-workspaces.conf @@ -1,26 +1,26 @@ # --- START CODER JETBRAINS TOOLBOX test.coder.invalid -Host coder-jetbrains-toolbox--foo--test.coder.invalid +Host coder-jetbrains-toolbox-foo--test.coder.invalid ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=jetbrains foo ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-jetbrains-toolbox--foo--test.coder.invalid--bg +Host coder-jetbrains-toolbox-foo--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=disable foo ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-jetbrains-toolbox--bar--test.coder.invalid +Host coder-jetbrains-toolbox-bar--test.coder.invalid ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=jetbrains bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-jetbrains-toolbox--bar--test.coder.invalid--bg +Host coder-jetbrains-toolbox-bar--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=disable bar ConnectTimeout 0 StrictHostKeyChecking no diff --git a/src/test/resources/fixtures/outputs/no-disable-autostart.conf b/src/test/resources/fixtures/outputs/no-disable-autostart.conf index 8e05e28..cd9e3ad 100644 --- a/src/test/resources/fixtures/outputs/no-disable-autostart.conf +++ b/src/test/resources/fixtures/outputs/no-disable-autostart.conf @@ -1,12 +1,12 @@ # --- START CODER JETBRAINS TOOLBOX test.coder.invalid -Host coder-jetbrains-toolbox--foo--test.coder.invalid +Host coder-jetbrains-toolbox-foo--test.coder.invalid ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=jetbrains foo ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-jetbrains-toolbox--foo--test.coder.invalid--bg +Host coder-jetbrains-toolbox-foo--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=disable foo ConnectTimeout 0 StrictHostKeyChecking no diff --git a/src/test/resources/fixtures/outputs/no-report-usage.conf b/src/test/resources/fixtures/outputs/no-report-usage.conf index 2c03d91..03a8d81 100644 --- a/src/test/resources/fixtures/outputs/no-report-usage.conf +++ b/src/test/resources/fixtures/outputs/no-report-usage.conf @@ -1,12 +1,12 @@ # --- START CODER JETBRAINS TOOLBOX test.coder.invalid -Host coder-jetbrains-toolbox--foo--test.coder.invalid +Host coder-jetbrains-toolbox-foo--test.coder.invalid ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio foo ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-jetbrains-toolbox--foo--test.coder.invalid--bg +Host coder-jetbrains-toolbox-foo--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio foo ConnectTimeout 0 StrictHostKeyChecking no diff --git a/src/test/resources/fixtures/outputs/replace-end-no-newline.conf b/src/test/resources/fixtures/outputs/replace-end-no-newline.conf index 8911750..4d4e958 100644 --- a/src/test/resources/fixtures/outputs/replace-end-no-newline.conf +++ b/src/test/resources/fixtures/outputs/replace-end-no-newline.conf @@ -2,14 +2,14 @@ Host test Port 80 Host test2 Port 443 # --- START CODER JETBRAINS TOOLBOX test.coder.invalid -Host coder-jetbrains-toolbox--foo-bar--test.coder.invalid +Host coder-jetbrains-toolbox-foo-bar--test.coder.invalid ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=jetbrains foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-jetbrains-toolbox--foo-bar--test.coder.invalid--bg +Host coder-jetbrains-toolbox-foo-bar--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=disable foo-bar ConnectTimeout 0 StrictHostKeyChecking no diff --git a/src/test/resources/fixtures/outputs/replace-end.conf b/src/test/resources/fixtures/outputs/replace-end.conf index 02954a4..b5b9d2c 100644 --- a/src/test/resources/fixtures/outputs/replace-end.conf +++ b/src/test/resources/fixtures/outputs/replace-end.conf @@ -3,14 +3,14 @@ Host test Host test2 Port 443 # --- START CODER JETBRAINS TOOLBOX test.coder.invalid -Host coder-jetbrains-toolbox--foo-bar--test.coder.invalid +Host coder-jetbrains-toolbox-foo-bar--test.coder.invalid ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=jetbrains foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-jetbrains-toolbox--foo-bar--test.coder.invalid--bg +Host coder-jetbrains-toolbox-foo-bar--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=disable foo-bar ConnectTimeout 0 StrictHostKeyChecking no diff --git a/src/test/resources/fixtures/outputs/replace-middle-ignore-unrelated.conf b/src/test/resources/fixtures/outputs/replace-middle-ignore-unrelated.conf index 49bc113..36b03f3 100644 --- a/src/test/resources/fixtures/outputs/replace-middle-ignore-unrelated.conf +++ b/src/test/resources/fixtures/outputs/replace-middle-ignore-unrelated.conf @@ -4,14 +4,14 @@ Host test some coder config # ------------END-CODER------------ # --- START CODER JETBRAINS TOOLBOX test.coder.invalid -Host coder-jetbrains-toolbox--foo-bar--test.coder.invalid +Host coder-jetbrains-toolbox-foo-bar--test.coder.invalid ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=jetbrains foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-jetbrains-toolbox--foo-bar--test.coder.invalid--bg +Host coder-jetbrains-toolbox-foo-bar--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=disable foo-bar ConnectTimeout 0 StrictHostKeyChecking no diff --git a/src/test/resources/fixtures/outputs/replace-middle.conf b/src/test/resources/fixtures/outputs/replace-middle.conf index 7b6cd6c..437404c 100644 --- a/src/test/resources/fixtures/outputs/replace-middle.conf +++ b/src/test/resources/fixtures/outputs/replace-middle.conf @@ -1,14 +1,14 @@ Host test Port 80 # --- START CODER JETBRAINS TOOLBOX test.coder.invalid -Host coder-jetbrains-toolbox--foo-bar--test.coder.invalid +Host coder-jetbrains-toolbox-foo-bar--test.coder.invalid ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=jetbrains foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-jetbrains-toolbox--foo-bar--test.coder.invalid--bg +Host coder-jetbrains-toolbox-foo-bar--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=disable foo-bar ConnectTimeout 0 StrictHostKeyChecking no diff --git a/src/test/resources/fixtures/outputs/replace-only.conf b/src/test/resources/fixtures/outputs/replace-only.conf index 58519f6..d884838 100644 --- a/src/test/resources/fixtures/outputs/replace-only.conf +++ b/src/test/resources/fixtures/outputs/replace-only.conf @@ -1,12 +1,12 @@ # --- START CODER JETBRAINS TOOLBOX test.coder.invalid -Host coder-jetbrains-toolbox--foo-bar--test.coder.invalid +Host coder-jetbrains-toolbox-foo-bar--test.coder.invalid ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=jetbrains foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-jetbrains-toolbox--foo-bar--test.coder.invalid--bg +Host coder-jetbrains-toolbox-foo-bar--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=disable foo-bar ConnectTimeout 0 StrictHostKeyChecking no diff --git a/src/test/resources/fixtures/outputs/replace-start.conf b/src/test/resources/fixtures/outputs/replace-start.conf index d6fa5a3..aeb47d4 100644 --- a/src/test/resources/fixtures/outputs/replace-start.conf +++ b/src/test/resources/fixtures/outputs/replace-start.conf @@ -1,12 +1,12 @@ # --- START CODER JETBRAINS TOOLBOX test.coder.invalid -Host coder-jetbrains-toolbox--foo-bar--test.coder.invalid +Host coder-jetbrains-toolbox-foo-bar--test.coder.invalid ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=jetbrains foo-bar ConnectTimeout 0 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR SetEnv CODER_SSH_SESSION_TYPE=JetBrains -Host coder-jetbrains-toolbox--foo-bar--test.coder.invalid--bg +Host coder-jetbrains-toolbox-foo-bar--test.coder.invalid--bg ProxyCommand /tmp/coder-toolbox/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-toolbox/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --usage-app=disable foo-bar ConnectTimeout 0 StrictHostKeyChecking no