From fc66478b94f6a6a1ae4db6baa3fc9daf6a604581 Mon Sep 17 00:00:00 2001 From: Atif Ali Date: Wed, 1 Apr 2026 15:33:03 +0000 Subject: [PATCH] fix(jetbrains): scope HTTP version fetch to selected IDEs only (#822) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Problem The `data "http" "jetbrains_ide_versions"` resource fetches release info from `data.services.jetbrains.com` for **all configured IDE options** at plan time, regardless of what the user actually selected. When the API is unreachable (air-gapped environments, DNS failures, transient outages), this causes a fatal Terraform error that blocks the workspace build — even when no JetBrains IDEs were selected. ## Fix Changed the `for_each` on the HTTP data source (and all dependent locals) from iterating over `var.options`/`var.default` to `local.selected_ides` — the user's actual selection. | Scenario | Before | After | |---|---|---| | No IDEs selected (`[]`) | 9 HTTP requests | 0 HTTP requests | | 1 IDE selected (`["GO"]`) | 9 HTTP requests | 1 HTTP request | | All IDEs selected | 9 HTTP requests | 9 HTTP requests | ## Validation - All 17 existing `terraform test` cases pass - Tested end-to-end on [dev.coder.com](https://dev.coder.com) with Docker template: - `jetbrains_ides=[]` — zero HTTP requests, build succeeds - `jetbrains_ides=["GO"]` — single HTTP request for GoLand only, `coder_app.jetbrains["GO"]` created Closes #821 > 🤖 This PR was created with the help of Coder Agents, and needs a human review. 🧑💻 --- registry/coder/modules/jetbrains/README.md | 14 +++++++------- registry/coder/modules/jetbrains/main.tf | 20 +++++++++++--------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/registry/coder/modules/jetbrains/README.md b/registry/coder/modules/jetbrains/README.md index 7fa8f674..7d913481 100644 --- a/registry/coder/modules/jetbrains/README.md +++ b/registry/coder/modules/jetbrains/README.md @@ -14,7 +14,7 @@ This module adds JetBrains IDE buttons to launch IDEs directly from the dashboar module "jetbrains" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/jetbrains/coder" - version = "1.3.0" + version = "1.4.0" agent_id = coder_agent.main.id folder = "/home/coder/project" } @@ -39,7 +39,7 @@ When `default` contains IDE codes, those IDEs are created directly without user module "jetbrains" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/jetbrains/coder" - version = "1.3.0" + version = "1.4.0" agent_id = coder_agent.main.id folder = "/home/coder/project" default = ["PY", "IU"] # Pre-configure PyCharm and IntelliJ IDEA @@ -52,7 +52,7 @@ module "jetbrains" { module "jetbrains" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/jetbrains/coder" - version = "1.3.0" + version = "1.4.0" agent_id = coder_agent.main.id folder = "/home/coder/project" # Show parameter with limited options @@ -66,7 +66,7 @@ module "jetbrains" { module "jetbrains" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/jetbrains/coder" - version = "1.3.0" + version = "1.4.0" agent_id = coder_agent.main.id folder = "/home/coder/project" default = ["IU", "PY"] @@ -81,7 +81,7 @@ module "jetbrains" { module "jetbrains" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/jetbrains/coder" - version = "1.3.0" + version = "1.4.0" agent_id = coder_agent.main.id folder = "/workspace/project" @@ -108,7 +108,7 @@ module "jetbrains" { module "jetbrains_pycharm" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/jetbrains/coder" - version = "1.3.0" + version = "1.4.0" agent_id = coder_agent.main.id folder = "/workspace/project" @@ -128,7 +128,7 @@ Add helpful tooltip text that appears when users hover over the IDE app buttons: module "jetbrains" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/jetbrains/coder" - version = "1.3.0" + version = "1.4.0" agent_id = coder_agent.main.id folder = "/home/coder/project" default = ["IU", "PY"] diff --git a/registry/coder/modules/jetbrains/main.tf b/registry/coder/modules/jetbrains/main.tf index 2fac060f..018cf583 100644 --- a/registry/coder/modules/jetbrains/main.tf +++ b/registry/coder/modules/jetbrains/main.tf @@ -125,7 +125,7 @@ variable "download_base_link" { } data "http" "jetbrains_ide_versions" { - for_each = length(var.default) == 0 ? var.options : var.default + for_each = local.selected_ides url = "${var.releases_base_link}/products/releases?code=${each.key}&type=${var.channel}${var.major_version == "latest" ? "&latest=true" : ""}" } @@ -174,9 +174,14 @@ variable "ide_config" { } locals { + # Determine the user's actual IDE selection. + # This is computed before the HTTP data source so that version lookups + # are only performed for IDEs the user chose — not every option. + selected_ides = length(var.default) == 0 ? toset(jsondecode(coalesce(data.coder_parameter.jetbrains_ides[0].value, "[]"))) : toset(var.default) + # Parse HTTP responses once with error handling for air-gapped environments parsed_responses = { - for code in length(var.default) == 0 ? var.options : var.default : code => try( + for code in local.selected_ides : code => try( jsondecode(data.http.jetbrains_ide_versions[code].response_body), {} # Return empty object if API call fails ) @@ -184,7 +189,7 @@ 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 code in local.selected_ides : 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 ] @@ -192,13 +197,13 @@ locals { # 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 => + for code in local.selected_ides : 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 selected IDEs with fallback to ide_config options_metadata = { - for code in length(var.default) == 0 ? var.options : var.default : code => { + for code in local.selected_ides : code => { icon = var.ide_config[code].icon name = var.ide_config[code].name identifier = code @@ -211,9 +216,6 @@ locals { json_data = local.selected_releases[code] } } - - # Convert the parameter value to a set for for_each - selected_ides = length(var.default) == 0 ? toset(jsondecode(coalesce(data.coder_parameter.jetbrains_ides[0].value, "[]"))) : toset(var.default) } data "coder_parameter" "jetbrains_ides" {