From 9f2a967967accc8545bdf6736aa648e214125a34 Mon Sep 17 00:00:00 2001 From: Faur Ioan-Aurel Date: Sat, 13 Sep 2025 00:34:52 +0300 Subject: [PATCH 1/4] fix: don't store token when certificates are configured This was a regression for the custom flows that required authentication via certificates, the http client and the coder cli were properly initialized but afterward the token was still required for storing. Note: the issue was hard to catch early on because the official coder cli is not supporting auth. via certificates. --- CHANGELOG.md | 4 ++++ .../kotlin/com/coder/toolbox/CoderRemoteProvider.kt | 10 +++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 63b71d2..ee09b84 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Fixed + +- token is no longer required when authentication is done via certificates + ## 0.6.4 - 2025-09-03 ### Added diff --git a/src/main/kotlin/com/coder/toolbox/CoderRemoteProvider.kt b/src/main/kotlin/com/coder/toolbox/CoderRemoteProvider.kt index 8c6dcd3..d13bf87 100644 --- a/src/main/kotlin/com/coder/toolbox/CoderRemoteProvider.kt +++ b/src/main/kotlin/com/coder/toolbox/CoderRemoteProvider.kt @@ -396,9 +396,13 @@ class CoderRemoteProvider( private fun onConnect(client: CoderRestClient, cli: CoderCLIManager) { // Store the URL and token for use next time. context.secrets.lastDeploymentURL = client.url.toString() - context.secrets.lastToken = client.token ?: "" - context.secrets.storeTokenFor(client.url, context.secrets.lastToken) - context.logger.info("Deployment URL and token were stored and will be available for automatic connection") + if (context.settingsStore.requireTokenAuth) { + context.secrets.lastToken = client.token ?: "" + context.secrets.storeTokenFor(client.url, context.secrets.lastToken) + context.logger.info("Deployment URL and token were stored and will be available for automatic connection") + } else { + context.logger.info("Deployment URL was stored and will be available for automatic connection") + } this.client = client pollJob?.let { it.cancel() From 35e64f2c1d9a6d995e02d1e69ae719e673e5876e Mon Sep 17 00:00:00 2001 From: Faur Ioan-Aurel Date: Sat, 13 Sep 2025 00:35:57 +0300 Subject: [PATCH 2/4] chore: next version is 0.6.5 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 3b06eb4..a2e2a3f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ -version=0.6.4 +version=0.6.5 group=com.coder.toolbox name=coder-toolbox \ No newline at end of file From 6aa83fd42daf4a9231f8d9581f336fc089567b10 Mon Sep 17 00:00:00 2001 From: Faur Ioan-Aurel Date: Sat, 13 Sep 2025 00:37:00 +0300 Subject: [PATCH 3/4] impl: improved poll job lifecycle report Poll jobs now have a random friendly name that changes each time a new job is created, and the logging for when the jobs are created or cancelled is also improved. The idea behind this commit is to improve debugging sessions on client logs by: - having clear log lines saying when a job is created/destructed - easy to remember and cross-match job names (instead of cryptic java reference values) --- .../com/coder/toolbox/CoderRemoteProvider.kt | 14 ++++++++--- .../coder/toolbox/util/CoroutineExtensions.kt | 6 +++++ .../com/coder/toolbox/util/NameGenerator.kt | 25 +++++++++++++++++++ 3 files changed, 41 insertions(+), 4 deletions(-) create mode 100644 src/main/kotlin/com/coder/toolbox/util/CoroutineExtensions.kt create mode 100644 src/main/kotlin/com/coder/toolbox/util/NameGenerator.kt diff --git a/src/main/kotlin/com/coder/toolbox/CoderRemoteProvider.kt b/src/main/kotlin/com/coder/toolbox/CoderRemoteProvider.kt index d13bf87..4e2607d 100644 --- a/src/main/kotlin/com/coder/toolbox/CoderRemoteProvider.kt +++ b/src/main/kotlin/com/coder/toolbox/CoderRemoteProvider.kt @@ -7,6 +7,8 @@ import com.coder.toolbox.sdk.ex.APIResponseException import com.coder.toolbox.sdk.v2.models.WorkspaceStatus import com.coder.toolbox.util.CoderProtocolHandler import com.coder.toolbox.util.DialogUi +import com.coder.toolbox.util.friendlyName +import com.coder.toolbox.util.name import com.coder.toolbox.util.toURL import com.coder.toolbox.util.waitForTrue import com.coder.toolbox.util.withPath @@ -89,7 +91,7 @@ class CoderRemoteProvider( * first time). */ private fun poll(client: CoderRestClient, cli: CoderCLIManager): Job = - context.cs.launch(CoroutineName("Workspace Poller")) { + context.cs.launch(CoroutineName("Workspace Poller - ${friendlyName()}")) { var lastPollTime = TimeSource.Monotonic.markNow() while (isActive) { try { @@ -242,7 +244,10 @@ class CoderRemoteProvider( * Also called as part of our own logout. */ override fun close() { - pollJob?.cancel() + pollJob?.let { + it.cancel() + context.logger.info("Cancelled workspace poll job ${pollJob.name()}") + } client?.close() lastEnvironments.clear() environments.value = LoadableState.Value(emptyList()) @@ -327,6 +332,7 @@ class CoderRemoteProvider( environments.showLoadingMessage() pollJob = poll(restClient, cli) + context.logger.info("Workspace poll job with name ${pollJob.name()} was created while handling URI $uri") isInitialized.waitForTrue() } } catch (ex: Exception) { @@ -406,13 +412,13 @@ class CoderRemoteProvider( this.client = client pollJob?.let { it.cancel() - context.logger.info("Workspace poll job with reference ${pollJob} was canceled") + context.logger.info("Cancelled workspace poll job ${pollJob.name()} in order to start a new one") } environments.showLoadingMessage() coderHeaderPage.setTitle(context.i18n.pnotr(client.url.toString())) context.logger.info("Displaying ${client.url} in the UI") pollJob = poll(client, cli) - context.logger.info("Workspace poll job created with reference $pollJob") + context.logger.info("Workspace poll job with name ${pollJob.name()} was created") } private fun MutableStateFlow>>.showLoadingMessage() { diff --git a/src/main/kotlin/com/coder/toolbox/util/CoroutineExtensions.kt b/src/main/kotlin/com/coder/toolbox/util/CoroutineExtensions.kt new file mode 100644 index 0000000..8382dbf --- /dev/null +++ b/src/main/kotlin/com/coder/toolbox/util/CoroutineExtensions.kt @@ -0,0 +1,6 @@ +package com.coder.toolbox.util + +import kotlinx.coroutines.CoroutineName +import kotlinx.coroutines.Job + +fun Job?.name(): String = this?.get(CoroutineName)?.toString() ?: this.toString() \ No newline at end of file diff --git a/src/main/kotlin/com/coder/toolbox/util/NameGenerator.kt b/src/main/kotlin/com/coder/toolbox/util/NameGenerator.kt new file mode 100644 index 0000000..b60751e --- /dev/null +++ b/src/main/kotlin/com/coder/toolbox/util/NameGenerator.kt @@ -0,0 +1,25 @@ +package com.coder.toolbox.util + +import kotlin.random.Random + +val adjectives = listOf( + "brave", "calm", "clever", "curious", "eager", + "fast", "gentle", "happy", "kind", "lively", + "mighty", "noble", "quiet", "rapid", "shiny", + "swift", "tough", "vast", "wise", "young" +) + +val nouns = listOf( + "bear", "cloud", "dragon", "eagle", "fire", + "forest", "hawk", "lion", "moon", "mountain", + "owl", "panther", "river", "shadow", "sky", + "star", "storm", "tree", "wolf", "wind" +) + +/** + * Easy to remember names, helps with logs investigation + */ +fun friendlyName(): String { + val number = Random.nextInt(10, 99) // 2 digits for extra uniqueness + return "${adjectives.random()}-${nouns.random()}-$number" +} \ No newline at end of file From 3997712960ea7fa1d12669fc64f05ef91007dec2 Mon Sep 17 00:00:00 2001 From: Faur Ioan-Aurel Date: Sat, 13 Sep 2025 01:36:28 +0300 Subject: [PATCH 4/4] chore: partially revert previous comment Seems like we can't properly get the coroutine name from a Job. The name of the coroutine is not super friendly but still workable with the new logs. --- .../com/coder/toolbox/CoderRemoteProvider.kt | 12 ++++----- .../coder/toolbox/util/CoroutineExtensions.kt | 6 ----- .../com/coder/toolbox/util/NameGenerator.kt | 25 ------------------- 3 files changed, 5 insertions(+), 38 deletions(-) delete mode 100644 src/main/kotlin/com/coder/toolbox/util/CoroutineExtensions.kt delete mode 100644 src/main/kotlin/com/coder/toolbox/util/NameGenerator.kt diff --git a/src/main/kotlin/com/coder/toolbox/CoderRemoteProvider.kt b/src/main/kotlin/com/coder/toolbox/CoderRemoteProvider.kt index 4e2607d..5d5acbb 100644 --- a/src/main/kotlin/com/coder/toolbox/CoderRemoteProvider.kt +++ b/src/main/kotlin/com/coder/toolbox/CoderRemoteProvider.kt @@ -7,8 +7,6 @@ import com.coder.toolbox.sdk.ex.APIResponseException import com.coder.toolbox.sdk.v2.models.WorkspaceStatus import com.coder.toolbox.util.CoderProtocolHandler import com.coder.toolbox.util.DialogUi -import com.coder.toolbox.util.friendlyName -import com.coder.toolbox.util.name import com.coder.toolbox.util.toURL import com.coder.toolbox.util.waitForTrue import com.coder.toolbox.util.withPath @@ -91,7 +89,7 @@ class CoderRemoteProvider( * first time). */ private fun poll(client: CoderRestClient, cli: CoderCLIManager): Job = - context.cs.launch(CoroutineName("Workspace Poller - ${friendlyName()}")) { + context.cs.launch(CoroutineName("Workspace Poller")) { var lastPollTime = TimeSource.Monotonic.markNow() while (isActive) { try { @@ -246,7 +244,7 @@ class CoderRemoteProvider( override fun close() { pollJob?.let { it.cancel() - context.logger.info("Cancelled workspace poll job ${pollJob.name()}") + context.logger.info("Cancelled workspace poll job ${pollJob.toString()}") } client?.close() lastEnvironments.clear() @@ -332,7 +330,7 @@ class CoderRemoteProvider( environments.showLoadingMessage() pollJob = poll(restClient, cli) - context.logger.info("Workspace poll job with name ${pollJob.name()} was created while handling URI $uri") + context.logger.info("Workspace poll job with name ${pollJob.toString()} was created while handling URI $uri") isInitialized.waitForTrue() } } catch (ex: Exception) { @@ -412,13 +410,13 @@ class CoderRemoteProvider( this.client = client pollJob?.let { it.cancel() - context.logger.info("Cancelled workspace poll job ${pollJob.name()} in order to start a new one") + context.logger.info("Cancelled workspace poll job ${pollJob.toString()} in order to start a new one") } environments.showLoadingMessage() coderHeaderPage.setTitle(context.i18n.pnotr(client.url.toString())) context.logger.info("Displaying ${client.url} in the UI") pollJob = poll(client, cli) - context.logger.info("Workspace poll job with name ${pollJob.name()} was created") + context.logger.info("Workspace poll job with name ${pollJob.toString()} was created") } private fun MutableStateFlow>>.showLoadingMessage() { diff --git a/src/main/kotlin/com/coder/toolbox/util/CoroutineExtensions.kt b/src/main/kotlin/com/coder/toolbox/util/CoroutineExtensions.kt deleted file mode 100644 index 8382dbf..0000000 --- a/src/main/kotlin/com/coder/toolbox/util/CoroutineExtensions.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.coder.toolbox.util - -import kotlinx.coroutines.CoroutineName -import kotlinx.coroutines.Job - -fun Job?.name(): String = this?.get(CoroutineName)?.toString() ?: this.toString() \ No newline at end of file diff --git a/src/main/kotlin/com/coder/toolbox/util/NameGenerator.kt b/src/main/kotlin/com/coder/toolbox/util/NameGenerator.kt deleted file mode 100644 index b60751e..0000000 --- a/src/main/kotlin/com/coder/toolbox/util/NameGenerator.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.coder.toolbox.util - -import kotlin.random.Random - -val adjectives = listOf( - "brave", "calm", "clever", "curious", "eager", - "fast", "gentle", "happy", "kind", "lively", - "mighty", "noble", "quiet", "rapid", "shiny", - "swift", "tough", "vast", "wise", "young" -) - -val nouns = listOf( - "bear", "cloud", "dragon", "eagle", "fire", - "forest", "hawk", "lion", "moon", "mountain", - "owl", "panther", "river", "shadow", "sky", - "star", "storm", "tree", "wolf", "wind" -) - -/** - * Easy to remember names, helps with logs investigation - */ -fun friendlyName(): String { - val number = Random.nextInt(10, 99) // 2 digits for extra uniqueness - return "${adjectives.random()}-${nouns.random()}-$number" -} \ No newline at end of file