feat(coder/modules/jetbrains): update to latest build numbers and clean up tests (#636)

Co-authored-by: DevCats <christofer@coder.com>
This commit is contained in:
Atif Ali
2026-01-07 01:06:31 +05:00
committed by GitHub
parent 60611ed593
commit 2701dc09af
4 changed files with 112 additions and 1106 deletions
+8 -16
View File
@@ -14,10 +14,9 @@ This module adds JetBrains IDE buttons to launch IDEs directly from the dashboar
module "jetbrains" { module "jetbrains" {
count = data.coder_workspace.me.start_count count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/jetbrains/coder" source = "registry.coder.com/coder/jetbrains/coder"
version = "1.2.1" version = "1.3.0"
agent_id = coder_agent.main.id agent_id = coder_agent.main.id
folder = "/home/coder/project" folder = "/home/coder/project"
# tooltip = "You need to [Install Coder Desktop](https://coder.com/docs/user-guides/desktop#install-coder-desktop) to use this button." # Optional
} }
``` ```
@@ -40,7 +39,7 @@ When `default` contains IDE codes, those IDEs are created directly without user
module "jetbrains" { module "jetbrains" {
count = data.coder_workspace.me.start_count count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/jetbrains/coder" source = "registry.coder.com/coder/jetbrains/coder"
version = "1.2.1" version = "1.3.0"
agent_id = coder_agent.main.id agent_id = coder_agent.main.id
folder = "/home/coder/project" folder = "/home/coder/project"
default = ["PY", "IU"] # Pre-configure GoLand and IntelliJ IDEA default = ["PY", "IU"] # Pre-configure GoLand and IntelliJ IDEA
@@ -53,7 +52,7 @@ module "jetbrains" {
module "jetbrains" { module "jetbrains" {
count = data.coder_workspace.me.start_count count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/jetbrains/coder" source = "registry.coder.com/coder/jetbrains/coder"
version = "1.2.1" version = "1.3.0"
agent_id = coder_agent.main.id agent_id = coder_agent.main.id
folder = "/home/coder/project" folder = "/home/coder/project"
# Show parameter with limited options # Show parameter with limited options
@@ -67,7 +66,7 @@ module "jetbrains" {
module "jetbrains" { module "jetbrains" {
count = data.coder_workspace.me.start_count count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/jetbrains/coder" source = "registry.coder.com/coder/jetbrains/coder"
version = "1.2.1" version = "1.3.0"
agent_id = coder_agent.main.id agent_id = coder_agent.main.id
folder = "/home/coder/project" folder = "/home/coder/project"
default = ["IU", "PY"] default = ["IU", "PY"]
@@ -82,7 +81,7 @@ module "jetbrains" {
module "jetbrains" { module "jetbrains" {
count = data.coder_workspace.me.start_count count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/jetbrains/coder" source = "registry.coder.com/coder/jetbrains/coder"
version = "1.2.1" version = "1.3.0"
agent_id = coder_agent.main.id agent_id = coder_agent.main.id
folder = "/workspace/project" folder = "/workspace/project"
@@ -109,7 +108,7 @@ module "jetbrains" {
module "jetbrains_pycharm" { module "jetbrains_pycharm" {
count = data.coder_workspace.me.start_count count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/jetbrains/coder" source = "registry.coder.com/coder/jetbrains/coder"
version = "1.2.1" version = "1.3.0"
agent_id = coder_agent.main.id agent_id = coder_agent.main.id
folder = "/workspace/project" folder = "/workspace/project"
@@ -129,11 +128,11 @@ Add helpful tooltip text that appears when users hover over the IDE app buttons:
module "jetbrains" { module "jetbrains" {
count = data.coder_workspace.me.start_count count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/jetbrains/coder" source = "registry.coder.com/coder/jetbrains/coder"
version = "1.2.1" version = "1.3.0"
agent_id = coder_agent.main.id agent_id = coder_agent.main.id
folder = "/home/coder/project" folder = "/home/coder/project"
default = ["IU", "PY"] default = ["IU", "PY"]
tooltip = "You need to [Install Coder Desktop](https://coder.com/docs/user-guides/desktop#install-coder-desktop) to use this button." tooltip = "You need to install [JetBrains Toolbox App](https://www.jetbrains.com/toolbox-app/) to use this button."
} }
``` ```
@@ -170,13 +169,6 @@ resource "coder_metadata" "container_info" {
- If the API is unreachable (air-gapped environments), the module automatically falls back to build numbers from `ide_config` - If the API is unreachable (air-gapped environments), the module automatically falls back to build numbers from `ide_config`
- `major_version` and `channel` control which API endpoint is queried (when API access is available) - `major_version` and `channel` control which API endpoint is queried (when API access is available)
### Tooltip
- **`tooltip`**: Optional markdown text displayed when hovering over IDE app buttons
- If not specified, no tooltip is shown
- Supports markdown formatting for rich text (bold, italic, links, etc.)
- All IDE apps created by this module will show the same tooltip text
## Supported IDEs ## Supported IDEs
All JetBrains IDEs with remote development capabilities: All JetBrains IDEs with remote development capabilities:
@@ -2,15 +2,15 @@ variables {
# Default IDE config, mirrored from main.tf for test assertions. # Default IDE config, mirrored from main.tf for test assertions.
# If main.tf defaults change, update this map to match. # If main.tf defaults change, update this map to match.
expected_ide_config = { expected_ide_config = {
"CL" = { name = "CLion", icon = "/icon/clion.svg", build = "251.26927.39" }, "CL" = { name = "CLion", icon = "/icon/clion.svg", build = "253.29346.141" },
"GO" = { name = "GoLand", icon = "/icon/goland.svg", build = "251.26927.50" }, "GO" = { name = "GoLand", icon = "/icon/goland.svg", build = "253.28294.337" },
"IU" = { name = "IntelliJ IDEA", icon = "/icon/intellij.svg", build = "251.26927.53" }, "IU" = { name = "IntelliJ IDEA", icon = "/icon/intellij.svg", build = "253.29346.138" },
"PS" = { name = "PhpStorm", icon = "/icon/phpstorm.svg", build = "251.26927.60" }, "PS" = { name = "PhpStorm", icon = "/icon/phpstorm.svg", build = "253.29346.151" },
"PY" = { name = "PyCharm", icon = "/icon/pycharm.svg", build = "251.26927.74" }, "PY" = { name = "PyCharm", icon = "/icon/pycharm.svg", build = "253.29346.142" },
"RD" = { name = "Rider", icon = "/icon/rider.svg", build = "251.26927.67" }, "RD" = { name = "Rider", icon = "/icon/rider.svg", build = "253.29346.144" },
"RM" = { name = "RubyMine", icon = "/icon/rubymine.svg", build = "251.26927.47" }, "RM" = { name = "RubyMine", icon = "/icon/rubymine.svg", build = "253.29346.140" },
"RR" = { name = "RustRover", icon = "/icon/rustrover.svg", build = "251.26927.79" }, "RR" = { name = "RustRover", icon = "/icon/rustrover.svg", build = "253.29346.139" },
"WS" = { name = "WebStorm", icon = "/icon/webstorm.svg", build = "251.26927.40" } "WS" = { name = "WebStorm", icon = "/icon/webstorm.svg", build = "253.29346.143" }
} }
} }
@@ -187,16 +187,16 @@ run "tooltip_when_provided" {
agent_id = "foo" agent_id = "foo"
folder = "/home/coder" folder = "/home/coder"
default = ["GO"] default = ["GO"]
tooltip = "You need to [Install Coder Desktop](https://coder.com/docs/user-guides/desktop#install-coder-desktop) to use this button." tooltip = "You need to install [JetBrains Toolbox App](https://www.jetbrains.com/toolbox-app/) to use this button."
} }
assert { assert {
condition = anytrue([for app in values(resource.coder_app.jetbrains) : app.tooltip == "You need to [Install Coder Desktop](https://coder.com/docs/user-guides/desktop#install-coder-desktop) to use this button."]) condition = anytrue([for app in values(resource.coder_app.jetbrains) : app.tooltip == "You need to install [JetBrains Toolbox App](https://www.jetbrains.com/toolbox-app/) to use this button."])
error_message = "Expected coder_app tooltip to be set when provided" error_message = "Expected coder_app tooltip to be set when provided"
} }
} }
run "tooltip_null_when_not_provided" { run "tooltip_default_when_not_provided" {
command = plan command = plan
variables { variables {
@@ -206,8 +206,41 @@ run "tooltip_null_when_not_provided" {
} }
assert { assert {
condition = anytrue([for app in values(resource.coder_app.jetbrains) : app.tooltip == null]) condition = anytrue([for app in values(resource.coder_app.jetbrains) : app.tooltip == "You need to install [JetBrains Toolbox App](https://www.jetbrains.com/toolbox-app/) to use this button."])
error_message = "Expected coder_app tooltip to be null when not provided" error_message = "Expected coder_app tooltip to be the default JetBrains Toolbox message when not provided"
}
}
run "channel_eap" {
command = plan
variables {
agent_id = "foo"
folder = "/home/coder"
default = ["GO"]
channel = "eap"
major_version = "latest"
}
assert {
condition = output.ide_metadata["GO"].json_data.type == "eap"
error_message = "Expected the API to return a release of type 'eap', but got '${output.ide_metadata["GO"].json_data.type}'"
}
}
run "specific_major_version" {
command = plan
variables {
agent_id = "foo"
folder = "/home/coder"
default = ["GO"]
major_version = "2025.3"
}
assert {
condition = output.ide_metadata["GO"].json_data.majorVersion == "2025.3"
error_message = "Expected the API to return a release for major version '2025.3', but got '${output.ide_metadata["GO"].json_data.majorVersion}'"
} }
} }
@@ -294,3 +327,27 @@ run "output_multiple_ides" {
error_message = "Expected ide_metadata['PY'].build to be the fallback '${var.expected_ide_config["PY"].build}'" error_message = "Expected ide_metadata['PY'].build to be the fallback '${var.expected_ide_config["PY"].build}'"
} }
} }
run "validate_output_schema" {
command = plan
variables {
agent_id = "foo"
folder = "/home/coder"
default = ["GO"]
}
assert {
condition = alltrue([
for key, meta in output.ide_metadata : (
can(meta.icon) &&
can(meta.name) &&
can(meta.identifier) &&
can(meta.key) &&
can(meta.build) &&
# json_data can be null, but the key must exist
can(meta.json_data)
)
])
error_message = "The ide_metadata output schema has changed. Please update the 'main.tf' and this test."
}
}
File diff suppressed because it is too large Load Diff
+33 -22
View File
@@ -62,7 +62,7 @@ variable "coder_parameter_order" {
variable "tooltip" { variable "tooltip" {
type = string type = string
description = "Markdown text that is displayed when hovering over workspace apps." description = "Markdown text that is displayed when hovering over workspace apps."
default = null default = "You need to install [JetBrains Toolbox App](https://www.jetbrains.com/toolbox-app/) to use this button."
} }
variable "major_version" { variable "major_version" {
@@ -70,8 +70,8 @@ variable "major_version" {
description = "The major version of the IDE. i.e. 2025.1" description = "The major version of the IDE. i.e. 2025.1"
default = "latest" default = "latest"
validation { validation {
condition = can(regex("^[0-9]{4}\\.[0-2]{1}$", var.major_version)) || var.major_version == "latest" condition = can(regex("^[0-9]{4}\\.[1-3]$", var.major_version)) || var.major_version == "latest"
error_message = "The major_version must be a valid version number. i.e. 2025.1 or latest" error_message = "The major_version must be a valid version number (e.g., 2025.1) or 'latest'"
} }
} }
@@ -126,7 +126,7 @@ variable "download_base_link" {
data "http" "jetbrains_ide_versions" { data "http" "jetbrains_ide_versions" {
for_each = length(var.default) == 0 ? var.options : var.default for_each = length(var.default) == 0 ? var.options : var.default
url = "${var.releases_base_link}/products/releases?code=${each.key}&type=${var.channel}&latest=true${var.major_version == "latest" ? "" : "&major_version=${var.major_version}"}" url = "${var.releases_base_link}/products/releases?code=${each.key}&type=${var.channel}${var.major_version == "latest" ? "&latest=true" : ""}"
} }
variable "ide_config" { variable "ide_config" {
@@ -138,9 +138,9 @@ variable "ide_config" {
- build: The build number of the IDE. - build: The build number of the IDE.
Example: Example:
{ {
"CL" = { name = "CLion", icon = "/icon/clion.svg", build = "251.26927.39" }, "CL" = { name = "CLion", icon = "/icon/clion.svg", build = "253.29346.141" },
"GO" = { name = "GoLand", icon = "/icon/goland.svg", build = "251.26927.50" }, "GO" = { name = "GoLand", icon = "/icon/goland.svg", build = "253.28294.337" },
"IU" = { name = "IntelliJ IDEA", icon = "/icon/intellij.svg", build = "251.26927.53" }, "IU" = { name = "IntelliJ IDEA", icon = "/icon/intellij.svg", build = "253.29346.138" },
} }
EOT EOT
type = map(object({ type = map(object({
@@ -149,15 +149,15 @@ variable "ide_config" {
build = string build = string
})) }))
default = { default = {
"CL" = { name = "CLion", icon = "/icon/clion.svg", build = "251.26927.39" }, "CL" = { name = "CLion", icon = "/icon/clion.svg", build = "253.29346.141" },
"GO" = { name = "GoLand", icon = "/icon/goland.svg", build = "251.26927.50" }, "GO" = { name = "GoLand", icon = "/icon/goland.svg", build = "253.28294.337" },
"IU" = { name = "IntelliJ IDEA", icon = "/icon/intellij.svg", build = "251.26927.53" }, "IU" = { name = "IntelliJ IDEA", icon = "/icon/intellij.svg", build = "253.29346.138" },
"PS" = { name = "PhpStorm", icon = "/icon/phpstorm.svg", build = "251.26927.60" }, "PS" = { name = "PhpStorm", icon = "/icon/phpstorm.svg", build = "253.29346.151" },
"PY" = { name = "PyCharm", icon = "/icon/pycharm.svg", build = "251.26927.74" }, "PY" = { name = "PyCharm", icon = "/icon/pycharm.svg", build = "253.29346.142" },
"RD" = { name = "Rider", icon = "/icon/rider.svg", build = "251.26927.67" }, "RD" = { name = "Rider", icon = "/icon/rider.svg", build = "253.29346.144" },
"RM" = { name = "RubyMine", icon = "/icon/rubymine.svg", build = "251.26927.47" }, "RM" = { name = "RubyMine", icon = "/icon/rubymine.svg", build = "253.29346.140" },
"RR" = { name = "RustRover", icon = "/icon/rustrover.svg", build = "251.26927.79" }, "RR" = { name = "RustRover", icon = "/icon/rustrover.svg", build = "253.29346.139" },
"WS" = { name = "WebStorm", icon = "/icon/webstorm.svg", build = "251.26927.40" } "WS" = { name = "WebStorm", icon = "/icon/webstorm.svg", build = "253.29346.143" }
} }
validation { validation {
condition = length(var.ide_config) > 0 condition = length(var.ide_config) > 0
@@ -182,6 +182,20 @@ locals {
) )
} }
# Filter the parsed response for the requested major version if not "latest"
filtered_releases = {
for code in length(var.default) == 0 ? var.options : var.default : code => [
for r in try(local.parsed_responses[code][keys(local.parsed_responses[code])[0]], []) :
r if var.major_version == "latest" || r.majorVersion == var.major_version
]
}
# Select the latest release for the requested major version (first item in the filtered list)
selected_releases = {
for code in length(var.default) == 0 ? var.options : var.default : code =>
length(local.filtered_releases[code]) > 0 ? local.filtered_releases[code][0] : null
}
# Dynamically generate IDE configurations based on options with fallback to ide_config # Dynamically generate IDE configurations based on options with fallback to ide_config
options_metadata = { options_metadata = {
for code in length(var.default) == 0 ? var.options : var.default : code => { for code in length(var.default) == 0 ? var.options : var.default : code => {
@@ -191,13 +205,10 @@ locals {
key = code key = code
# Use API build number if available, otherwise fall back to ide_config build number # Use API build number if available, otherwise fall back to ide_config build number
build = length(keys(local.parsed_responses[code])) > 0 ? ( build = local.selected_releases[code] != null ? local.selected_releases[code].build : var.ide_config[code].build
local.parsed_responses[code][keys(local.parsed_responses[code])[0]][0].build
) : var.ide_config[code].build
# Store API data for potential future use (only if API is available) # Store API data for potential future use
json_data = length(keys(local.parsed_responses[code])) > 0 ? local.parsed_responses[code][keys(local.parsed_responses[code])[0]][0] : null json_data = local.selected_releases[code]
response_key = length(keys(local.parsed_responses[code])) > 0 ? keys(local.parsed_responses[code])[0] : null
} }
} }