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

Commitf4710e2

Browse files
authored
doc: initial guide to the plugin (#78)
The following topics are covered:- documentation on how to install the plugin- URI handling documentation- release procedure-resolves#64 This PR also includes:- improved Error Reporting for Unresolved Agents, now it shows a clear,human-readable error message instead- a rename of `project_path` query param to `folder`- and a fix to no longer ask for coder deployment details when userwants to install the plugin via URI.P.S: Some of the topics were copied without shame from the old CoderGateway plugin.
1 parente313f45 commitf4710e2

File tree

7 files changed

+163
-57
lines changed

7 files changed

+163
-57
lines changed

‎CHANGELOG.md‎

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,12 @@
55
###Fixed
66

77
- SSH connection to a Workspace is no longer established only once
8+
- authorization wizard automatically goes to a previous screen when an error is encountered during connection to Coder deployment
89

910
###Changed
1011

1112
- action buttons on the token input step were swapped to achieve better keyboard navigation
12-
13-
###Fixed
14-
15-
- authorization wizard automatically goes to a previous screen when an error is encountered during connection to Coder deployment
13+
- URI`project_path` query parameter was renamed to`folder`
1614

1715
##0.1.3 - 2025-04-09
1816

‎README.md‎

Lines changed: 105 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,109 @@
1-
#ToolboxGatewayplugin sample
1+
#CoderToolbox plugin
22

3-
To load plugin into the provided Toolbox App, run`./gradlew build copyPlugin`
3+
[!["Join us onDiscord"](https://img.shields.io/badge/join-us%20on%20Discord-gray.svg?longCache=true&logo=discord&colorB=purple)](https://discord.gg/coder)
4+
[![Twitter Follow](https://img.shields.io/twitter/follow/CoderHQ?label=%40CoderHQ&style=social)](https://twitter.com/coderhq)
5+
[![Coder Toolbox Plugin Build](https://github.com/coder/coder-jetbrains-toolbox/actions/workflows/build.yml/badge.svg)](https://github.com/coder/coder-jetbrains-toolbox/actions/workflows/build.yml)
46

5-
or put files in the following directory:
7+
Connects your JetBrains IDE to Coder workspaces
68

7-
* Windows:`%LocalAppData%/JetBrains/Toolbox/cache/plugins/plugin-id`
8-
* macOS:`~/Library/Caches/JetBrains/Toolbox/plugins/plugin-id`
9-
* Linux:`~/.local/share/JetBrains/Toolbox/plugins/plugin-id`
9+
##Getting Started
1010

11-
Put all required .jar files (do not include any dependencies already included with the Toolbox App to avoid possible resolution conflicts),
12-
`extensions.json` and`icon.svg` in this directory.
11+
To install this plugin using JetBrains Toolbox, follow the steps below.
12+
13+
1. Install[JetBrains Toolbox](https://www.jetbrains.com/toolbox-app/). Make sure it's the`2.6.0.40284` release or
14+
above.
15+
2. Launch the Toolbox app and sign in with your JetBrains account (if needed).
16+
17+
###Install Coder plugin via URI
18+
19+
You can quickly install the plugin using this JetBrains hyperlink.
20+
21+
👉[Install plugin](jetbrains://gateway/com.coder.toolbox)
22+
23+
This will open JetBrains Toolbox and prompt you to install the Coder Toolbox plugin automatically.
24+
25+
Alternatively, you can paste`jetbrains://gateway/com.coder.toolbox` into a browser.
26+
27+
###Manual install
28+
29+
There are two ways Coder Toolbox plugin can be installed. The first option is to manually download the plugin
30+
artifact from[JetBrains Marketplace](https://plugins.jetbrains.com/plugin/26968-coder/versions)
31+
or from[Coder's Github Release page](https://github.com/coder/coder-jetbrains-toolbox/releases).
32+
33+
The next step is to copy the zip content to one of the following locations, depending on your OS:
34+
35+
* Windows:`%LocalAppData%/JetBrains/Toolbox/plugins/com.coder.toolbox`
36+
* macOS:`~/Library/Caches/JetBrains/Toolbox/plugins/com.coder.toolbox`
37+
* Linux:`~/.local/share/JetBrains/Toolbox/plugins/com.coder.toolbox`
38+
39+
Alternatively, you can install it using the_Gradle_ tasks included in the project:
40+
41+
```shell
42+
43+
./gradlew cleanAll build copyPlugin
44+
```
45+
46+
Make sure Toolbox is closed before running the command.
47+
48+
##Connect to a Coder Workspace via JetBrains Toolbox URI
49+
50+
You can use specially crafted JetBrains Gateway URIs to automatically:
51+
52+
1. Open Toolbox
53+
54+
2. Install the Coder Toolbox plugin (if not already installed)
55+
56+
3. Connect to a specific Coder deployment using a URL and a token.
57+
58+
4. Select a running workspace
59+
60+
5. Install a specified JetBrains IDE on that Workspace
61+
62+
6. Open a project folder directly in the remote IDE
63+
64+
###Example URIs
65+
66+
```text
67+
jetbrains://gateway/com.coder.toolbox?url=https%3A%2F%2Fdev.coder.com&token=zeoX4SbSpP-j2qGpajkdwxR9jBdcekXS2&workspace=bobiverse-bob&agent=dev&ide_product_code=GO&ide_build_number=241.23774.119&folder=%2Fhome%2Fcoder%2Fworkspace%2Fhello-world-rs
68+
69+
jetbrains://gateway/com.coder.toolbox?url=https%3A%2F%2Fj5gj2r1so5nbi.pit-1.try.coder.app%2F&token=gqEirOoI1U-FfCQ6uj8iOLtybBIk99rr8&workspace=bobiverse-riker&agent=dev&ide_product_code=RR&ide_build_number=243.26053.17&folder=%2Fhome%2Fcoder%2Fworkspace%2Fhello-world-rs
70+
```
71+
72+
###URI Breakdown
73+
74+
```text
75+
jetbrains://gateway/com.coder.toolbox
76+
?url=http(s)://<your-coder-deployment>
77+
&token=<auth-token>
78+
&workspace=<workspace-name>
79+
&agent_id=<agent--id>
80+
&ide_product_code=<IDE-code>
81+
&ide_build_number=<IDE-build>
82+
&folder=<absolute-path-to-a-project-folder>
83+
```
84+
85+
| Query param| Description| Mandatory|
86+
|------------------|------------------------------------------------------------------------------|-----------|
87+
| url| Your Coder deployment URL (encoded)| Yes|
88+
| token| Coder authentication token| Yes|
89+
| workspace| Name of the Coder workspace to connect to.| Yes|
90+
| agent_id| ID of the agent associated with the workspace| No|
91+
| ide_product_code| JetBrains IDE product code (e.g., GO for GoLand, RR for Rider)| No|
92+
| ide_build_number| Specific build number of the JetBrains IDE to install on the workspace| No|
93+
| folder| Absolute path to the project folder to open in the remote IDE (URL-encoded)| No|
94+
95+
If only a single agent is available, specifying an agent ID is optional. However, if multiple agents exist,
96+
you must provide either the ID to target a specific one. Note that this version of the Coder Toolbox plugin
97+
does not automatically start agents if they are offline, so please ensure the selected agent is running before
98+
proceeding.
99+
100+
If`ide_product_code` and`ide_build_number` is missing, Toolbox will only open and highlight the workspace environment
101+
page. Coder Toolbox will attempt to start the workspace if it’s not already running; however, for the most reliable
102+
experience, it’s recommended to ensure the workspace is running prior to initiating the connection.
103+
104+
##Releasing
105+
106+
1. Check that the changelog lists all the important changes.
107+
2. Update the gradle.properties version.
108+
3. Publish the resulting draft release after validating it.
109+
4. Merge the resulting changelog PR.

‎build.gradle.kts‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,8 +191,10 @@ private fun getPluginInstallDir(): Path {
191191
}/"JetBrains"/"Toolbox"
192192

193193
val pluginsDir=when {
194-
SystemInfoRt.isWindows-> toolboxCachesDir/"cache"
195-
SystemInfoRt.isLinux||SystemInfoRt.isMac-> toolboxCachesDir
194+
SystemInfoRt.isWindows||
195+
SystemInfoRt.isLinux||
196+
SystemInfoRt.isMac-> toolboxCachesDir
197+
196198
else-> error("Unknown os")
197199
}/"plugins"
198200

‎gradle.properties‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
version=0.1.3
1+
version=0.1.4
22
group=com.coder.toolbox
33
name=coder-toolbox

‎src/main/kotlin/com/coder/toolbox/util/CoderProtocolHandler.kt‎

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,12 @@ open class CoderProtocolHandler(
4242
shouldWaitForAutoLogin:Boolean,
4343
reInitialize:suspend (CoderRestClient,CoderCLIManager)->Unit
4444
) {
45+
context.popupPluginMainPage()
4546
val params= uri.toQueryParameters()
47+
if (params.isEmpty()) {
48+
// probably a plugin installation scenario
49+
return
50+
}
4651

4752
val deploymentURL= params.url()?: askUrl()
4853
if (deploymentURL.isNullOrBlank()) {
@@ -123,7 +128,19 @@ open class CoderProtocolHandler(
123128
}
124129

125130
// TODO: Show a dropdown and ask for an agent if missing.
126-
val agent= getMatchingAgent(params, workspace)
131+
val agent:WorkspaceAgent
132+
try {
133+
agent= getMatchingAgent(params, workspace)
134+
}catch (e:IllegalArgumentException) {
135+
context.logger.error(e,"Can't resolve an agent for workspace$workspaceName from$deploymentURL")
136+
context.showErrorPopup(
137+
MissingArgumentException(
138+
"Can't handle URI because we can't resolve an agent for workspace$workspaceName from$deploymentURL",
139+
e
140+
)
141+
)
142+
return
143+
}
127144
val status=WorkspaceAndAgentStatus.from(workspace, agent)
128145

129146
if (!status.ready()) {
@@ -157,7 +174,7 @@ open class CoderProtocolHandler(
157174
context.envPageManager.showEnvironmentPage(environmentId,false)
158175
val productCode= params.ideProductCode()
159176
val buildNumber= params.ideBuildNumber()
160-
valprojectPath= params.projectPath()
177+
valprojectFolder= params.projectFolder()
161178
if (!productCode.isNullOrBlank()&&!buildNumber.isNullOrBlank()) {
162179
context.cs.launch {
163180
val ideVersion="$productCode-$buildNumber"
@@ -167,7 +184,7 @@ open class CoderProtocolHandler(
167184
}
168185
job.join()
169186
context.logger.info("launching$ideVersion on$environmentId")
170-
context.ideOrchestrator.connectToIde(environmentId, ideVersion,projectPath)
187+
context.ideOrchestrator.connectToIde(environmentId, ideVersion,projectFolder)
171188
}
172189
}
173190
}
@@ -262,10 +279,8 @@ internal fun resolveRedirects(url: URL): URL {
262279

263280
/**
264281
* Return the agent matching the provided agent ID or name in the parameters.
265-
* The name is ignored if the ID is set. If neither was supplied and the
266-
* workspace has only one agent, return that. Otherwise throw an error.
267282
*
268-
* @throws [MissingArgumentException,IllegalArgumentException]
283+
* @throws [IllegalArgumentException]
269284
*/
270285
internalfungetMatchingAgent(
271286
parameters:Map<String,String?>,
@@ -281,8 +296,6 @@ internal fun getMatchingAgent(
281296
val agent=
282297
if (!parameters.agentID().isNullOrBlank()) {
283298
agents.firstOrNull { it.id.toString()== parameters.agentID() }
284-
}elseif (!parameters.agentName().isNullOrBlank()) {
285-
agents.firstOrNull { it.name== parameters.agentName() }
286299
}elseif (agents.size==1) {
287300
agents.first()
288301
}else {
@@ -292,13 +305,9 @@ internal fun getMatchingAgent(
292305
if (agent==null) {
293306
if (!parameters.agentID().isNullOrBlank()) {
294307
throwIllegalArgumentException("The workspace\"${workspace.name}\" does not have an agent with ID\"${parameters.agentID()}\"")
295-
}elseif (!parameters.agentName().isNullOrBlank()) {
296-
throwIllegalArgumentException(
297-
"The workspace\"${workspace.name}\"does not have an agent named\"${parameters.agentName()}\"",
298-
)
299308
}else {
300309
throwMissingArgumentException(
301-
"Unable to determine which agent to connect to;one of\"$AGENT_NAME\" or\"$AGENT_ID\" must be set because the workspace\"${workspace.name}\" has more than one agent",
310+
"Unable to determine which agent to connect to;\"$AGENT_ID\" must be set because the workspace\"${workspace.name}\" has more than one agent",
302311
)
303312
}
304313
}
Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,24 @@
11
packagecom.coder.toolbox.util
22

3-
// These are keys that we support in our Gateway links and must not be changed.
4-
privateconstvalTYPE="type"
53
constvalURL="url"
64
constvalTOKEN="token"
75
constvalWORKSPACE="workspace"
86
constvalAGENT_NAME="agent"
97
constvalAGENT_ID="agent_id"
108
privateconstvalIDE_PRODUCT_CODE="ide_product_code"
119
privateconstvalIDE_BUILD_NUMBER="ide_build_number"
12-
privateconstvalPROJECT_PATH="project_path"
13-
14-
// Helper functions for reading from the map. Prefer these to directly
15-
// interacting with the map.
16-
17-
funMap<String,String>.isCoder():Boolean=this[TYPE]=="coder"
10+
privateconstvalFOLDER="folder"
1811

1912
funMap<String,String>.url()=this[URL]
2013

2114
funMap<String,String>.token()=this[TOKEN]
2215

2316
funMap<String,String>.workspace()=this[WORKSPACE]
2417

25-
funMap<String,String?>.agentName()=this[AGENT_NAME]
26-
2718
funMap<String,String?>.agentID()=this[AGENT_ID]
2819

2920
funMap<String,String>.ideProductCode()=this[IDE_PRODUCT_CODE]
3021

3122
funMap<String,String>.ideBuildNumber()=this[IDE_BUILD_NUMBER]
3223

33-
funMap<String,String>.projectPath()=this[PROJECT_PATH]
24+
funMap<String,String>.projectFolder()=this[FOLDER]

‎src/test/kotlin/com/coder/toolbox/util/LinkHandlerTest.kt‎

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -54,16 +54,21 @@ internal class LinkHandlerTest {
5454

5555
val tests=
5656
listOf(
57-
Pair(mapOf("agent" to"agent_name"),"9a920eee-47fb-4571-9501-e4b3120c12f2"),
58-
Pair(mapOf("agent_id" to"9a920eee-47fb-4571-9501-e4b3120c12f2"),"9a920eee-47fb-4571-9501-e4b3120c12f2"),
59-
Pair(mapOf("agent" to"agent_name_2"),"fb3daea4-da6b-424d-84c7-36b90574cfef"),
60-
Pair(mapOf("agent_id" to"fb3daea4-da6b-424d-84c7-36b90574cfef"),"fb3daea4-da6b-424d-84c7-36b90574cfef"),
61-
Pair(mapOf("agent" to"agent_name_3"),"b0e4c54d-9ba9-4413-8512-11ca1e826a24"),
62-
Pair(mapOf("agent_id" to"b0e4c54d-9ba9-4413-8512-11ca1e826a24"),"b0e4c54d-9ba9-4413-8512-11ca1e826a24"),
57+
Pair(
58+
mapOf("agent_id" to"9a920eee-47fb-4571-9501-e4b3120c12f2"),
59+
"9a920eee-47fb-4571-9501-e4b3120c12f2"
60+
),
61+
Pair(
62+
mapOf("agent_id" to"fb3daea4-da6b-424d-84c7-36b90574cfef"),
63+
"fb3daea4-da6b-424d-84c7-36b90574cfef"
64+
),
65+
Pair(
66+
mapOf("agent_id" to"b0e4c54d-9ba9-4413-8512-11ca1e826a24"),
67+
"b0e4c54d-9ba9-4413-8512-11ca1e826a24"
68+
),
6369
// Prefer agent_id.
6470
Pair(
6571
mapOf(
66-
"agent" to"agent_name",
6772
"agent_id" to"b0e4c54d-9ba9-4413-8512-11ca1e826a24",
6873
),
6974
"b0e4c54d-9ba9-4413-8512-11ca1e826a24",
@@ -81,15 +86,14 @@ internal class LinkHandlerTest {
8186
val tests=
8287
listOf(
8388
Triple(emptyMap(),MissingArgumentException::class,"Unable to determine"),
84-
Triple(mapOf("agent" to""),MissingArgumentException::class,"Unable to determine"),
8589
Triple(mapOf("agent_id" to""),MissingArgumentException::class,"Unable to determine"),
86-
Triple(mapOf("agent" tonull),MissingArgumentException::class,"Unable to determine"),
8790
Triple(mapOf("agent_id" tonull),MissingArgumentException::class,"Unable to determine"),
88-
Triple(mapOf("agent" to"ws"),IllegalArgumentException::class,"agent named"),
89-
Triple(mapOf("agent" to"ws.agent_name"),IllegalArgumentException::class,"agent named"),
90-
Triple(mapOf("agent" to"agent_name_4"),IllegalArgumentException::class,"agent named"),
9191
Triple(mapOf("agent_id" to"not-a-uuid"),IllegalArgumentException::class,"agent with ID"),
92-
Triple(mapOf("agent_id" to"ceaa7bcf-1612-45d7-b484-2e0da9349168"),IllegalArgumentException::class,"agent with ID"),
92+
Triple(
93+
mapOf("agent_id" to"ceaa7bcf-1612-45d7-b484-2e0da9349168"),
94+
IllegalArgumentException::class,
95+
"agent with ID"
96+
),
9397
// Will ignore agent if agent_id is set even if agent matches.
9498
Triple(
9599
mapOf(
@@ -139,10 +143,11 @@ internal class LinkHandlerTest {
139143
val ws=DataGen.workspace("ws", agents= oneAgent)
140144
val tests=
141145
listOf(
142-
Triple(mapOf("agent" to"ws"),IllegalArgumentException::class,"agent named"),
143-
Triple(mapOf("agent" to"ws.agent_name_3"),IllegalArgumentException::class,"agent named"),
144-
Triple(mapOf("agent" to"agent_name_4"),IllegalArgumentException::class,"agent named"),
145-
Triple(mapOf("agent_id" to"ceaa7bcf-1612-45d7-b484-2e0da9349168"),IllegalArgumentException::class,"agent with ID"),
146+
Triple(
147+
mapOf("agent_id" to"ceaa7bcf-1612-45d7-b484-2e0da9349168"),
148+
IllegalArgumentException::class,
149+
"agent with ID"
150+
),
146151
)
147152

148153
tests.forEach {
@@ -166,7 +171,11 @@ internal class LinkHandlerTest {
166171
Triple(mapOf("agent" tonull),IllegalArgumentException::class,"has no agents"),
167172
Triple(mapOf("agent_id" tonull),IllegalArgumentException::class,"has no agents"),
168173
Triple(mapOf("agent" to"agent_name"),IllegalArgumentException::class,"has no agents"),
169-
Triple(mapOf("agent_id" to"9a920eee-47fb-4571-9501-e4b3120c12f2"),IllegalArgumentException::class,"has no agents"),
174+
Triple(
175+
mapOf("agent_id" to"9a920eee-47fb-4571-9501-e4b3120c12f2"),
176+
IllegalArgumentException::class,
177+
"has no agents"
178+
),
170179
)
171180

172181
tests.forEach {

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp