Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Fix Coder connect workflows#2

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
fioan89 merged 13 commits intomainfromfix-connection-issues
Feb 26, 2025
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
Show all changes
13 commits
Select commitHold shift + click to select a range
335061a
build: rename project to coder-toolbox
fioan89Feb 18, 2025
4fe83ce
chore: update license
fioan89Feb 18, 2025
7151981
import code from coder/jetbrains-coder
fioan89Feb 18, 2025
36fba63
fix: remove Gateway string from title
fioan89Feb 18, 2025
e39cd05
impl: initial support for opening urls
fioan89Feb 19, 2025
94a6ff3
fix: use new URL opener
fioan89Feb 19, 2025
915d347
chore: replaces references to Gateway with Toolbox
fioan89Feb 19, 2025
5973b0d
impl: go to main page after signing in
fioan89Feb 20, 2025
b031c65
fix: connection status rendering
fioan89Feb 20, 2025
8faed95
fix: url glitch on new environment page
fioan89Feb 20, 2025
90d199c
impl: read plugin version from the extension.json
fioan89Feb 21, 2025
38e3e2b
fix: user agent did not have a proper version
fioan89Feb 21, 2025
33b4a60
Merge branch 'main' into fix-connection-issues
fioan89Feb 24, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 0 additions & 28 deletionssrc/main/kotlin/com/coder/toolbox/CoderGatewayExtension.kt
View file
Open in desktop

This file was deleted.

37 changes: 27 additions & 10 deletionssrc/main/kotlin/com/coder/toolbox/CoderRemoteEnvironment.kt
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,21 @@
package com.coder.toolbox

import com.coder.toolbox.browser.BrowserUtil
import com.coder.toolbox.models.WorkspaceAndAgentStatus
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.util.withPath
import com.coder.toolbox.views.Action
import com.coder.toolbox.views.EnvironmentView
import com.jetbrains.toolbox.api.core.ServiceLocator
import com.jetbrains.toolbox.api.remoteDev.AbstractRemoteProviderEnvironment
import com.jetbrains.toolbox.api.remoteDev.EnvironmentVisibilityState
import com.jetbrains.toolbox.api.remoteDev.environments.EnvironmentContentsView
import com.jetbrains.toolbox.api.remoteDev.states.EnvironmentStateConsumer
import com.jetbrains.toolbox.api.ui.ToolboxUi
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import java.util.concurrent.CompletableFuture

/**
Expand All@@ -19,32 +24,44 @@ import java.util.concurrent.CompletableFuture
* Used in the environment list view.
*/
class CoderRemoteEnvironment(
private val serviceLocator: ServiceLocator,
private val client: CoderRestClient,
private var workspace: Workspace,
private var agent: WorkspaceAgent,
privateval ui: ToolboxUi,
privatevar cs: CoroutineScope,
) : AbstractRemoteProviderEnvironment() {
private var status = WorkspaceAndAgentStatus.from(workspace, agent)

private val ui: ToolboxUi = serviceLocator.getService(ToolboxUi::class.java)
override fun getId(): String = "${workspace.name}.${agent.name}"
override fun getName(): String = "${workspace.name}.${agent.name}"
private var status = WorkspaceAndAgentStatus.from(workspace, agent)

init {
actionsList.add(
Action("Open web terminal") {
// TODO - check this later
// ui.openUrl(client.url.withPath("/${workspace.ownerName}/$name/terminal").toString())
cs.launch {
BrowserUtil.browse(client.url.withPath("/${workspace.ownerName}/$name/terminal").toString()) {
ui.showErrorInfoPopup(it)
}
}
},
)
actionsList.add(
Action("Open in dashboard") {
// TODO - check this later
// ui.openUrl(client.url.withPath("/@${workspace.ownerName}/${workspace.name}").toString())
cs.launch {
BrowserUtil.browse(client.url.withPath("/@${workspace.ownerName}/${workspace.name}").toString()) {
ui.showErrorInfoPopup(it)
}
}
},
)
actionsList.add(
Action("View template") {
// TODO - check this later
// ui.openUrl(client.url.withPath("/templates/${workspace.templateName}").toString())
cs.launch {
BrowserUtil.browse(client.url.withPath("/templates/${workspace.templateName}").toString()) {
ui.showErrorInfoPopup(it)
}
}
},
)
actionsList.add(
Expand DownExpand Up@@ -79,7 +96,7 @@ class CoderRemoteEnvironment(
val newStatus = WorkspaceAndAgentStatus.from(workspace, agent)
if (newStatus != status) {
status = newStatus
val state = status.toRemoteEnvironmentState()
val state = status.toRemoteEnvironmentState(serviceLocator)
listenerSet.forEach { it.consume(state) }
}
}
Expand DownExpand Up@@ -108,7 +125,7 @@ class CoderRemoteEnvironment(
// connected state can mask the workspace state.
// TODO@JB: You can still press connect if the environment is
// unreachable. Is that expected?
consumer.consume(status.toRemoteEnvironmentState())
consumer.consume(status.toRemoteEnvironmentState(serviceLocator))
return super.addStateListener(consumer)
}

Expand Down
32 changes: 17 additions & 15 deletionssrc/main/kotlin/com/coder/toolbox/CoderRemoteProvider.kt
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,10 +18,12 @@ import com.coder.toolbox.views.SignInPage
import com.coder.toolbox.views.TokenPage
import com.jetbrains.toolbox.api.core.PluginSecretStore
import com.jetbrains.toolbox.api.core.PluginSettingsStore
import com.jetbrains.toolbox.api.core.ServiceLocator
import com.jetbrains.toolbox.api.core.ui.icons.SvgIcon
import com.jetbrains.toolbox.api.remoteDev.ProviderVisibilityState
import com.jetbrains.toolbox.api.remoteDev.RemoteEnvironmentConsumer
import com.jetbrains.toolbox.api.remoteDev.RemoteProvider
import com.jetbrains.toolbox.api.remoteDev.ui.EnvironmentUiPageManager
import com.jetbrains.toolbox.api.ui.ToolboxUi
import com.jetbrains.toolbox.api.ui.actions.RunnableActionDescription
import com.jetbrains.toolbox.api.ui.components.AccountDropdownField
Expand All@@ -39,15 +41,17 @@ import kotlin.coroutines.cancellation.CancellationException
import kotlin.time.Duration.Companion.seconds

class CoderRemoteProvider(
private val serviceLocator: ServiceLocator,
private val httpClient: OkHttpClient,
private val consumer: RemoteEnvironmentConsumer,
private val coroutineScope: CoroutineScope,
private val ui: ToolboxUi,
settingsStore: PluginSettingsStore,
secretsStore: PluginSecretStore,
) : RemoteProvider {
private val logger = LoggerFactory.getLogger(javaClass)

private val ui: ToolboxUi = serviceLocator.getService(ToolboxUi::class.java)
private val consumer: RemoteEnvironmentConsumer = serviceLocator.getService(RemoteEnvironmentConsumer::class.java)
private val coroutineScope: CoroutineScope = serviceLocator.getService(CoroutineScope::class.java)
private val settingsStore: PluginSettingsStore = serviceLocator.getService(PluginSettingsStore::class.java)
private val secretsStore: PluginSecretStore = serviceLocator.getService(PluginSecretStore::class.java)

// Current polling job.
private var pollJob: Job? = null
private var lastEnvironments: Set<CoderRemoteEnvironment>? = null
Expand All@@ -60,7 +64,7 @@ class CoderRemoteProvider(
private val dialogUi = DialogUi(settings, ui)
private val linkHandler = LinkHandler(settings, httpClient, dialogUi)

// The REST client, if we are signed in.
// The REST client, if we are signed in
private var client: CoderRestClient? = null

// If we have an error in the polling we store it here before going back to
Expand DownExpand Up@@ -96,7 +100,7 @@ class CoderRemoteProvider(
it.name
}?.map { agent ->
// If we have an environment already, update that.
val env = CoderRemoteEnvironment(client, ws, agent,ui)
val env = CoderRemoteEnvironment(serviceLocator,client, ws, agent,coroutineScope)
lastEnvironments?.firstOrNull { it == env }?.let {
it.update(ws, agent)
it
Expand DownExpand Up@@ -146,7 +150,6 @@ class CoderRemoteProvider(
// rememberMe to false so we do not try to automatically log in.
secrets.rememberMe = "false"
close()
reset()
}

/**
Expand DownExpand Up@@ -182,7 +185,7 @@ class CoderRemoteProvider(
consumer.consumeEnvironments(emptyList(), true)
}

override fun getName(): String = "Coder Gateway"
override fun getName(): String = "Coder"
override fun getSvgIcon(): SvgIcon =
SvgIcon(this::class.java.getResourceAsStream("/icon.svg")?.readAllBytes() ?: byteArrayOf())

Expand All@@ -208,7 +211,7 @@ class CoderRemoteProvider(
* Just displays the deployment URL at the moment, but we could use this as
* a form for creating new environments.
*/
override fun getNewEnvironmentUiPage(): UiPage = NewEnvironmentPage(client?.url?.toString())
override fun getNewEnvironmentUiPage(): UiPage = NewEnvironmentPage(getDeploymentURL()?.first)

/**
* We always show a list of environments.
Expand DownExpand Up@@ -251,9 +254,8 @@ class CoderRemoteProvider(
* ui.hideUiPage() which stacks and has built-in back navigation, rather
* than using multiple root pages.
*/
private fun reset() {
// TODO - check this later
// ui.showPluginEnvironmentsPage()
private fun goToEnvironmentsPage() {
serviceLocator.getService(EnvironmentUiPageManager::class.java).showPluginEnvironmentsPage()
}

/**
Expand DownExpand Up@@ -309,7 +311,7 @@ class CoderRemoteProvider(
settings,
httpClient,
coroutineScope,
{ reset() },
::goToEnvironmentsPage,
) { client, cli ->
// Store the URL and token for use next time.
secrets.lastDeploymentURL = client.url.toString()
Expand All@@ -320,7 +322,7 @@ class CoderRemoteProvider(
pollError = null
pollJob?.cancel()
pollJob = poll(client, cli)
reset()
goToEnvironmentsPage()
}

/**
Expand Down
19 changes: 19 additions & 0 deletionssrc/main/kotlin/com/coder/toolbox/CoderToolboxExtension.kt
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
package com.coder.toolbox

import com.jetbrains.toolbox.api.core.ServiceLocator
import com.jetbrains.toolbox.api.remoteDev.RemoteDevExtension
import com.jetbrains.toolbox.api.remoteDev.RemoteProvider
import okhttp3.OkHttpClient

/**
* Entry point into the extension.
*/
class CoderToolboxExtension : RemoteDevExtension {
// All services must be passed in here and threaded as necessary.
override fun createRemoteProviderPluginInstance(serviceLocator: ServiceLocator): RemoteProvider {
return CoderRemoteProvider(
serviceLocator,
OkHttpClient(),
)
}
}
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
package com.coder.toolbox.browser

import java.io.IOException

class BrowserException(msg: String, error: Throwable? = null) : IOException(msg, error)
66 changes: 66 additions & 0 deletionssrc/main/kotlin/com/coder/toolbox/browser/BrowserUtil.kt
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
package com.coder.toolbox.browser

import com.coder.toolbox.util.OS
import com.coder.toolbox.util.getOS
import org.zeroturnaround.exec.ProcessExecutor

class BrowserUtil {
companion object {
fun browse(url: String, errorHandler: (BrowserException) -> Unit) {
val os = getOS()
if (os == null) {
errorHandler(BrowserException("Failed to open the URL because we can't detect the OS"))
return
}
when (os) {
OS.LINUX -> linuxBrowse(url, errorHandler)
OS.MAC -> macBrowse(url, errorHandler)
OS.WINDOWS -> windowsBrowse(url, errorHandler)
}
}

private fun linuxBrowse(url: String, errorHandler: (BrowserException) -> Unit) {
try {
if (OS.LINUX.getDesktopEnvironment()?.uppercase()?.contains("GNOME") == true) {
exec("gnome-open", url)
} else {
exec("xdg-open", url)
}
} catch (e: Exception) {
errorHandler(
BrowserException(
"Failed to open URL because an error was encountered. Please make sure xdg-open from package xdg-utils is available!",
e
)
)
}
}

private fun macBrowse(url: String, errorHandler: (BrowserException) -> Unit) {
try {
exec("open", url)
} catch (e: Exception) {
errorHandler(BrowserException("Failed to open URL because an error was encountered.", e))
}
}

private fun windowsBrowse(url: String, errorHandler: (BrowserException) -> Unit) {
try {
exec("cmd", "start \"$url\"")
} catch (e: Exception) {
errorHandler(BrowserException("Failed to open URL because an error was encountered.", e))
}
}

private fun exec(vararg args: String): String {
val stdout =
ProcessExecutor()
.command(*args)
.exitValues(0)
.readOutput(true)
.execute()
.outputUTF8()
return stdout
}
}
}
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,10 +5,11 @@ import com.coder.toolbox.sdk.v2.models.WorkspaceAgent
import com.coder.toolbox.sdk.v2.models.WorkspaceAgentLifecycleState
import com.coder.toolbox.sdk.v2.models.WorkspaceAgentStatus
import com.coder.toolbox.sdk.v2.models.WorkspaceStatus
import com.jetbrains.toolbox.api.core.ui.color.Color
import com.jetbrains.toolbox.api.core.ServiceLocator
import com.jetbrains.toolbox.api.core.ui.color.StateColor
import com.jetbrains.toolbox.api.core.ui.color.ThemeColor
import com.jetbrains.toolbox.api.remoteDev.states.CustomRemoteEnvironmentState
import com.jetbrains.toolbox.api.remoteDev.states.EnvironmentStateColorPalette
import com.jetbrains.toolbox.api.remoteDev.states.StandardRemoteEnvironmentState

/**
* WorkspaceAndAgentStatus represents the combined status of a single agent and
Expand DownExpand Up@@ -57,27 +58,27 @@ enum class WorkspaceAndAgentStatus(val label: String, val description: String) {
* Note that a reachable environment will always display "connected" or
* "disconnected" regardless of the label we give that status.
*/
fun toRemoteEnvironmentState(): CustomRemoteEnvironmentState {
// Use comments; no named arguments for non-Kotlin functions.
// TODO@JB: Is there a set of default colors we could use?
fun toRemoteEnvironmentState(serviceLocator: ServiceLocator): CustomRemoteEnvironmentState {
val stateColor = getStateColor(serviceLocator)
return CustomRemoteEnvironmentState(
label,
StateColor(
ThemeColor(
Color(0.407f, 0.439f, 0.502f, 1.0f), // lightThemeColor
Color(0.784f, 0.784f, 0.784f, 0.784f), // darkThemeColor
),
ThemeColor(
Color(0.878f, 0.878f, 0.941f, 0.102f), // darkThemeBackgroundColor
Color(0.878f, 0.878f, 0.961f, 0.980f), // lightThemeBackgroundColor
)
),
stateColor,
ready(), // reachable
// TODO@JB: How does this work? Would like a spinner for pending states.
null, // iconId
)
}

private fun getStateColor(serviceLocator: ServiceLocator): StateColor {
val colorPalette = serviceLocator.getService(EnvironmentStateColorPalette::class.java)


return if (ready()) colorPalette.getColor(StandardRemoteEnvironmentState.Active)
else if (canStart()) colorPalette.getColor(StandardRemoteEnvironmentState.Failed)
else if (pending()) colorPalette.getColor(StandardRemoteEnvironmentState.Activating)
else colorPalette.getColor(StandardRemoteEnvironmentState.Unreachable)
}

/**
* Return true if the agent is in a connectable state.
*/
Expand Down
12 changes: 12 additions & 0 deletionssrc/main/kotlin/com/coder/toolbox/plugin/PluginInfo.kt
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
package com.coder.toolbox.plugin

import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass

/**
* Small subset representation of extension.json
*/
@JsonClass(generateAdapter = true)
data class PluginInfo(
@Json(name = "id") val id: String,
@Json(name = "version") val version: String)
Loading

[8]ページ先頭

©2009-2025 Movatter.jp