Skip to content

Commit f304201

Browse files
Add IDE metadata output with tests and examples (#526)
## Description Exposes the metadata of the selected IDEs for use in the main template. Also adds tests to verify that the output metadata matches the "default" mappings. ## Type of Change - [ ] New module - [ ] New template - [ ] Bug fix - [x] Feature/enhancement - [ ] Documentation - [ ] Other ## Module Information **Path:** `registry/coder/modules/jetbrains` **New version:** `v1.2.0` **Breaking change:** [ ] Yes [x] No ## Testing & Validation - [N/A] Tests pass (`bun test`) - [x] Code formatted (`bun fmt`) - [x] Changes tested locally ## Related Issues None --------- Co-authored-by: DevCats <[email protected]>
1 parent 8bf1789 commit f304201

File tree

3 files changed

+171
-8
lines changed

3 files changed

+171
-8
lines changed

registry/coder/modules/jetbrains/README.md

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ This module adds JetBrains IDE buttons to launch IDEs directly from the dashboar
1414
module "jetbrains" {
1515
count = data.coder_workspace.me.start_count
1616
source = "registry.coder.com/coder/jetbrains/coder"
17-
version = "1.1.1"
17+
version = "1.2.0"
1818
agent_id = coder_agent.example.id
1919
folder = "/home/coder/project"
2020
# tooltip = "You need to [Install Coder Desktop](https://coder.com/docs/user-guides/desktop#install-coder-desktop) to use this button." # Optional
@@ -40,7 +40,7 @@ When `default` contains IDE codes, those IDEs are created directly without user
4040
module "jetbrains" {
4141
count = data.coder_workspace.me.start_count
4242
source = "registry.coder.com/coder/jetbrains/coder"
43-
version = "1.1.1"
43+
version = "1.2.0"
4444
agent_id = coder_agent.example.id
4545
folder = "/home/coder/project"
4646
default = ["PY", "IU"] # Pre-configure GoLand and IntelliJ IDEA
@@ -53,7 +53,7 @@ module "jetbrains" {
5353
module "jetbrains" {
5454
count = data.coder_workspace.me.start_count
5555
source = "registry.coder.com/coder/jetbrains/coder"
56-
version = "1.1.1"
56+
version = "1.2.0"
5757
agent_id = coder_agent.example.id
5858
folder = "/home/coder/project"
5959
# Show parameter with limited options
@@ -67,7 +67,7 @@ module "jetbrains" {
6767
module "jetbrains" {
6868
count = data.coder_workspace.me.start_count
6969
source = "registry.coder.com/coder/jetbrains/coder"
70-
version = "1.1.1"
70+
version = "1.2.0"
7171
agent_id = coder_agent.example.id
7272
folder = "/home/coder/project"
7373
default = ["IU", "PY"]
@@ -82,7 +82,7 @@ module "jetbrains" {
8282
module "jetbrains" {
8383
count = data.coder_workspace.me.start_count
8484
source = "registry.coder.com/coder/jetbrains/coder"
85-
version = "1.1.1"
85+
version = "1.2.0"
8686
agent_id = coder_agent.example.id
8787
folder = "/workspace/project"
8888
@@ -108,7 +108,7 @@ module "jetbrains" {
108108
module "jetbrains_pycharm" {
109109
count = data.coder_workspace.me.start_count
110110
source = "registry.coder.com/coder/jetbrains/coder"
111-
version = "1.1.1"
111+
version = "1.2.0"
112112
agent_id = coder_agent.example.id
113113
folder = "/workspace/project"
114114
@@ -128,14 +128,34 @@ Add helpful tooltip text that appears when users hover over the IDE app buttons:
128128
module "jetbrains" {
129129
count = data.coder_workspace.me.start_count
130130
source = "registry.coder.com/coder/jetbrains/coder"
131-
version = "1.1.1"
131+
version = "1.2.0"
132132
agent_id = coder_agent.example.id
133133
folder = "/home/coder/project"
134134
default = ["IU", "PY"]
135135
tooltip = "You need to [Install Coder Desktop](https://coder.com/docs/user-guides/desktop#install-coder-desktop) to use this button."
136136
}
137137
```
138138

139+
### Accessing the IDE Metadata
140+
141+
You can now reference the output `ide_metadata` as a map.
142+
143+
```tf
144+
# Add metadata to the container showing the installed IDEs and their build versions.
145+
resource "coder_metadata" "container_info" {
146+
count = data.coder_workspace.me.start_count
147+
resource_id = one(docker_container.workspace).id
148+
149+
dynamic "item" {
150+
for_each = length(module.jetbrains) > 0 ? one(module.jetbrains).ide_metadata : {}
151+
content {
152+
key = item.value.build
153+
value = "${item.value.name} [${item.key}]"
154+
}
155+
}
156+
}
157+
```
158+
139159
## Behavior
140160

141161
### Parameter vs Direct Apps

registry/coder/modules/jetbrains/jetbrains.tftest.hcl

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,53 @@
1+
variables {
2+
# Default IDE config, mirrored from main.tf for test assertions.
3+
# If main.tf defaults change, update this map to match.
4+
expected_ide_config = {
5+
"CL" = { name = "CLion", icon = "/icon/clion.svg", build = "251.26927.39" },
6+
"GO" = { name = "GoLand", icon = "/icon/goland.svg", build = "251.26927.50" },
7+
"IU" = { name = "IntelliJ IDEA", icon = "/icon/intellij.svg", build = "251.26927.53" },
8+
"PS" = { name = "PhpStorm", icon = "/icon/phpstorm.svg", build = "251.26927.60" },
9+
"PY" = { name = "PyCharm", icon = "/icon/pycharm.svg", build = "251.26927.74" },
10+
"RD" = { name = "Rider", icon = "/icon/rider.svg", build = "251.26927.67" },
11+
"RM" = { name = "RubyMine", icon = "/icon/rubymine.svg", build = "251.26927.47" },
12+
"RR" = { name = "RustRover", icon = "/icon/rustrover.svg", build = "251.26927.79" },
13+
"WS" = { name = "WebStorm", icon = "/icon/webstorm.svg", build = "251.26927.40" }
14+
}
15+
}
16+
17+
run "validate_test_config_matches_defaults" {
18+
command = plan
19+
20+
variables {
21+
# Provide minimal vars to allow plan to read module variables
22+
agent_id = "foo"
23+
folder = "/home/coder"
24+
}
25+
26+
assert {
27+
condition = length(var.ide_config) == length(var.expected_ide_config)
28+
error_message = "Test configuration mismatch: 'var.ide_config' in main.tf has ${length(var.ide_config)} items, but 'var.expected_ide_config' in the test file has ${length(var.expected_ide_config)} items. Please update the test file's global variables block."
29+
}
30+
31+
assert {
32+
# Check that all keys in the test local are present in the module's default
33+
condition = alltrue([
34+
for key in keys(var.expected_ide_config) :
35+
can(var.ide_config[key])
36+
])
37+
error_message = "Test configuration mismatch: Keys in 'var.expected_ide_config' are out of sync with 'var.ide_config' defaults. Please update the test file's global variables block."
38+
}
39+
40+
assert {
41+
# Check if all build numbers in the test local match the module's defaults
42+
# This relies on the previous two assertions passing (same length, same keys)
43+
condition = alltrue([
44+
for key, config in var.expected_ide_config :
45+
var.ide_config[key].build == config.build
46+
])
47+
error_message = "Test configuration mismatch: One or more build numbers in 'var.expected_ide_config' do not match the defaults in 'var.ide_config'. Please update the test file's global variables block."
48+
}
49+
}
50+
151
run "requires_agent_and_folder" {
252
command = plan
353

@@ -160,3 +210,87 @@ run "tooltip_null_when_not_provided" {
160210
error_message = "Expected coder_app tooltip to be null when not provided"
161211
}
162212
}
213+
214+
run "output_empty_when_default_empty" {
215+
command = plan
216+
217+
variables {
218+
agent_id = "foo"
219+
folder = "/home/coder"
220+
# var.default is empty
221+
}
222+
223+
assert {
224+
condition = length(output.ide_metadata) == 0
225+
error_message = "Expected ide_metadata output to be empty when var.default is not set"
226+
}
227+
}
228+
229+
run "output_single_ide_uses_fallback_build" {
230+
command = plan
231+
232+
variables {
233+
agent_id = "foo"
234+
folder = "/home/coder"
235+
default = ["GO"]
236+
# Force HTTP data source to fail to test fallback logic
237+
releases_base_link = "https://coder.com"
238+
}
239+
240+
assert {
241+
condition = length(output.ide_metadata) == 1
242+
error_message = "Expected ide_metadata output to have 1 item"
243+
}
244+
245+
assert {
246+
condition = can(output.ide_metadata["GO"])
247+
error_message = "Expected ide_metadata output to have key 'GO'"
248+
}
249+
250+
assert {
251+
condition = output.ide_metadata["GO"].name == var.expected_ide_config["GO"].name
252+
error_message = "Expected ide_metadata['GO'].name to be '${var.expected_ide_config["GO"].name}'"
253+
}
254+
255+
assert {
256+
condition = output.ide_metadata["GO"].build == var.expected_ide_config["GO"].build
257+
error_message = "Expected ide_metadata['GO'].build to use the fallback '${var.expected_ide_config["GO"].build}'"
258+
}
259+
260+
assert {
261+
condition = output.ide_metadata["GO"].icon == var.expected_ide_config["GO"].icon
262+
error_message = "Expected ide_metadata['GO'].icon to be '${var.expected_ide_config["GO"].icon}'"
263+
}
264+
}
265+
266+
run "output_multiple_ides" {
267+
command = plan
268+
269+
variables {
270+
agent_id = "foo"
271+
folder = "/home/coder"
272+
default = ["IU", "PY"]
273+
# Force HTTP data source to fail to test fallback logic
274+
releases_base_link = "https://coder.com"
275+
}
276+
277+
assert {
278+
condition = length(output.ide_metadata) == 2
279+
error_message = "Expected ide_metadata output to have 2 items"
280+
}
281+
282+
assert {
283+
condition = can(output.ide_metadata["IU"]) && can(output.ide_metadata["PY"])
284+
error_message = "Expected ide_metadata output to have keys 'IU' and 'PY'"
285+
}
286+
287+
assert {
288+
condition = output.ide_metadata["PY"].name == var.expected_ide_config["PY"].name
289+
error_message = "Expected ide_metadata['PY'].name to be '${var.expected_ide_config["PY"].name}'"
290+
}
291+
292+
assert {
293+
condition = output.ide_metadata["PY"].build == var.expected_ide_config["PY"].build
294+
error_message = "Expected ide_metadata['PY'].build to be the fallback '${var.expected_ide_config["PY"].build}'"
295+
}
296+
}

registry/coder/modules/jetbrains/main.tf

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,4 +257,13 @@ resource "coder_app" "jetbrains" {
257257
local.options_metadata[each.key].build,
258258
var.agent_name != null ? "&agent_name=${var.agent_name}" : "",
259259
])
260-
}
260+
}
261+
262+
output "ide_metadata" {
263+
description = "A map of the metadata for each selected JetBrains IDE."
264+
value = {
265+
# We iterate directly over the selected_ides map.
266+
# 'key' will be the IDE key (e.g., "IC", "PY")
267+
for key, val in local.selected_ides : key => local.options_metadata[key]
268+
}
269+
}

0 commit comments

Comments
 (0)