Skip to content
This repository was archived by the owner on May 15, 2025. It is now read-only.
25 changes: 20 additions & 5 deletions jetbrains-gateway/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ module "jetbrains_gateway" {
agent_name = "example"
folder = "/home/coder/example"
jetbrains_ides = ["CL", "GO", "IU", "PY", "WS"]
default = "GO"
default = ["GO"]
}
```

Expand All @@ -37,7 +37,7 @@ module "jetbrains_gateway" {
agent_name = "example"
folder = "/home/coder/example"
jetbrains_ides = ["GO", "WS"]
default = "GO"
default = ["GO"]
}
```

Expand All @@ -51,7 +51,7 @@ module "jetbrains_gateway" {
agent_name = "example"
folder = "/home/coder/example"
jetbrains_ides = ["GO", "WS"]
default = "GO"
default = ["GO"]
latest = true
}
```
Expand All @@ -66,7 +66,7 @@ module "jetbrains_gateway" {
agent_name = "example"
folder = "/home/coder/example"
jetbrains_ides = ["GO", "WS"]
default = "GO"
default = ["GO"]
latest = true
channel = "eap"
}
Expand All @@ -86,7 +86,22 @@ module "jetbrains_gateway" {
jetbrains_ides = ["GO", "WS"]
releases_base_link = "https://releases.internal.site/"
download_base_link = "https://download.internal.site/"
default = "GO"
default = ["GO"]
}
```

### Add multiple IDEs

**Note:** This removes the choice of IDE from the user.

```tf
module "jetbrains_gateway" {
source = "registry.coder.com/modules/jetbrains-gateway/coder"
version = "1.0.23"
agent_id = coder_agent.example.id
agent_name = "example"
folder = "/home/coder/example"
default = ["GO", "WS"]
}
```

Expand Down
29 changes: 23 additions & 6 deletions jetbrains-gateway/main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,13 @@ describe("jetbrains-gateway", async () => {

it("should create a link with the default values", async () => {
const state = await runTerraformApply(import.meta.dir, {
// These are all required.
agent_id: "foo",
agent_name: "foo",
folder: "/home/coder",
});
expect(state.outputs.url.value).toBe(
expect(state.outputs.url.value).toEqual([
"jetbrains-gateway://connect#type=coder&workspace=default&owner=default&agent=foo&folder=/home/coder&url=https://mydeployment.coder.com&token=$SESSION_TOKEN&ide_product_code=IU&ide_build_number=241.14494.240&ide_download_link=https://download.jetbrains.com/idea/ideaIU-2024.1.tar.gz",
);
]);

const coder_app = state.resources.find(
(res) => res.type === "coder_app" && res.name === "gateway",
Expand All @@ -34,13 +33,31 @@ describe("jetbrains-gateway", async () => {
expect(coder_app?.instances[0].attributes.order).toBeNull();
});

it("default to first ide", async () => {
it("default to first IDE", async () => {
const state = await runTerraformApply(import.meta.dir, {
agent_id: "foo",
agent_name: "foo",
folder: "/home/foo",
jetbrains_ides: ["IU", "PY"],
});
expect(state.outputs.identifier.value).toEqual(["IU"]);
expect(state.outputs.url.value).toEqual([
"jetbrains-gateway://connect#type=coder&workspace=default&owner=default&agent=foo&folder=/home/foo&url=https://mydeployment.coder.com&token=$SESSION_TOKEN&ide_product_code=IU&ide_build_number=241.14494.240&ide_download_link=https://download.jetbrains.com/idea/ideaIU-2024.1.tar.gz",
]);
});

it("should create multiple IDEs", async () => {
const state = await runTerraformApply(import.meta.dir, {
agent_id: "foo",
agent_name: "foo",
folder: "/home/foo",
jetbrains_ides: '["IU", "GO", "PY"]',
default: ["GO", "IU", "PY"],
});
expect(state.outputs.identifier.value).toBe("IU");
expect(state.outputs.identifier.value).toEqual(["GO", "IU", "PY"]);
expect(state.outputs.url.value).toEqual([
"jetbrains-gateway://connect#type=coder&workspace=default&owner=default&agent=foo&folder=/home/foo&url=https://mydeployment.coder.com&token=$SESSION_TOKEN&ide_product_code=GO&ide_build_number=241.14494.238&ide_download_link=https://download.jetbrains.com/go/goland-2024.1.tar.gz",
"jetbrains-gateway://connect#type=coder&workspace=default&owner=default&agent=foo&folder=/home/foo&url=https://mydeployment.coder.com&token=$SESSION_TOKEN&ide_product_code=IU&ide_build_number=241.14494.240&ide_download_link=https://download.jetbrains.com/idea/ideaIU-2024.1.tar.gz",
"jetbrains-gateway://connect#type=coder&workspace=default&owner=default&agent=foo&folder=/home/foo&url=https://mydeployment.coder.com&token=$SESSION_TOKEN&ide_product_code=PY&ide_build_number=241.14494.241&ide_download_link=https://download.jetbrains.com/python/pycharm-professional-2024.1.tar.gz",
]);
});
});
95 changes: 68 additions & 27 deletions jetbrains-gateway/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,23 @@ variable "folder" {
}

variable "default" {
default = ""
type = string
description = "Default IDE"
default = []
type = list(string)
description = "List of default IDEs to be added to the Workspace page."
# check if the list is unique
validation {
condition = length(var.default) == length(toset(var.default))
error_message = "The default must not contain duplicates."
}
# check if default are valid jetbrains_ides
validation {
condition = (
alltrue([
for code in var.default : contains(["IU", "PS", "WS", "PY", "CL", "GO", "RM", "RD"], code)
])
)
error_message = "The default must be a list of valid product codes. Valid product codes are ${join(",", ["IU", "PS", "WS", "PY", "CL", "GO", "RM", "RD"])}."
}
}

variable "order" {
Expand Down Expand Up @@ -124,7 +138,7 @@ variable "jetbrains_ide_versions" {

variable "jetbrains_ides" {
type = list(string)
description = "The list of IDE product codes."
description = "The list of IDE product codes to be shown to the user. Does not apply when there are multiple defaults."
default = ["IU", "PS", "WS", "PY", "CL", "GO", "RM", "RD"]
validation {
condition = (
Expand Down Expand Up @@ -239,23 +253,42 @@ locals {
}
}

icon = local.jetbrains_ides[data.coder_parameter.jetbrains_ide.value].icon
json_data = var.latest ? jsondecode(data.http.jetbrains_ide_versions[data.coder_parameter.jetbrains_ide.value].response_body) : {}
key = var.latest ? keys(local.json_data)[0] : ""
display_name = local.jetbrains_ides[data.coder_parameter.jetbrains_ide.value].name
identifier = data.coder_parameter.jetbrains_ide.value
download_link = var.latest ? local.json_data[local.key][0].downloads.linux.link : local.jetbrains_ides[data.coder_parameter.jetbrains_ide.value].download_link
build_number = var.latest ? local.json_data[local.key][0].build : local.jetbrains_ides[data.coder_parameter.jetbrains_ide.value].build_number
version = var.latest ? local.json_data[local.key][0].version : var.jetbrains_ide_versions[data.coder_parameter.jetbrains_ide.value].version
identifier = try([data.coder_parameter.jetbrains_ide[0].value], var.default)
list_json_data = var.latest ? [
for ide in local.identifier : jsondecode(data.http.jetbrains_ide_versions[ide].response_body)
] : []
list_key = var.latest ? [
for j in local.list_json_data : keys(j)[0]
] : []
download_links = length(local.list_key) > 0 ? [
for i, j in local.list_json_data : j[local.list_key[i]][0].downloads.linux.link
] : [
for ide in local.identifier : local.jetbrains_ides[ide].download_link
]
build_numbers = length(local.list_key) > 0 ? [
for i, j in local.list_json_data : j[local.list_key[i]][0].build
] : [
for ide in local.identifier : local.jetbrains_ides[ide].build_number
]
versions = length(local.list_key) > 0 ? [
for i, j in local.list_json_data : j[local.list_key[i]][0].version
] : [
for ide in local.identifier : local.jetbrains_ides[ide].version
]
display_names = [for key in keys(coder_app.gateway) : coder_app.gateway[key].display_name]
icons = [for key in keys(coder_app.gateway) : coder_app.gateway[key].icon]
urls = [for key in keys(coder_app.gateway) : coder_app.gateway[key].url]
}

data "coder_parameter" "jetbrains_ide" {
# remove the coder_parameter if there are multiple default
count = length(var.default) > 1 ? 0 : 1
type = "string"
name = "jetbrains_ide"
display_name = "JetBrains IDE"
icon = "/icon/gateway.svg"
mutable = true
default = var.default == "" ? var.jetbrains_ides[0] : var.default
default = length(var.default) > 0 ? var.default[0] : var.jetbrains_ides[0]
order = var.coder_parameter_order

dynamic "option" {
Expand All @@ -272,10 +305,11 @@ data "coder_workspace" "me" {}
data "coder_workspace_owner" "me" {}

resource "coder_app" "gateway" {
for_each = length(var.default) > 1 ? toset(var.default) : toset([data.coder_parameter.jetbrains_ide[0].value])
agent_id = var.agent_id
slug = var.slug
display_name = local.display_name
icon = local.icon
slug = "${var.slug}-${lower(each.value)}"
display_name = local.jetbrains_ides[each.value].name
icon = local.jetbrains_ides[each.value].icon
external = true
order = var.order
url = join("", [
Expand All @@ -292,38 +326,45 @@ resource "coder_app" "gateway" {
"&token=",
"$SESSION_TOKEN",
"&ide_product_code=",
data.coder_parameter.jetbrains_ide.value,
each.value,
"&ide_build_number=",
local.build_number,
local.jetbrains_ides[each.value].build_number,
"&ide_download_link=",
local.download_link,
local.jetbrains_ides[each.value].download_link,
])
}

output "identifier" {
value = local.identifier
value = local.identifier
description = "The product code of the JetBrains IDE."
}

output "display_name" {
value = local.display_name
value = [for key in keys(coder_app.gateway) : coder_app.gateway[key].display_name]
description = "The display name of the JetBrains IDE."
}

output "icon" {
value = local.icon
value = [for key in keys(coder_app.gateway) : coder_app.gateway[key].icon]
description = "The icon of the JetBrains IDE."
}

output "download_link" {
value = local.download_link
value = local.download_links
description = "The download link of the JetBrains IDE."
}

output "build_number" {
value = local.build_number
value = local.build_numbers
description = "The build number of the JetBrains IDE."
}

output "version" {
value = local.version
value = local.versions
description = "The version of the JetBrains IDE."
}

output "url" {
value = coder_app.gateway.url
}
value = [for key in keys(coder_app.gateway) : coder_app.gateway[key].url]
description = "The URL to connect to the JetBrains IDE."
}
3 changes: 2 additions & 1 deletion test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,8 @@ export const runTerraformApply = async <TVars extends TerraformVariables>(

const combinedEnv = env === undefined ? {} : { ...env };
for (const [key, value] of Object.entries(vars)) {
combinedEnv[`TF_VAR_${key}`] = String(value);
// Convert arrays to JSON strings
combinedEnv[`TF_VAR_${key}`] = Array.isArray(value) ? JSON.stringify(value) : String(value);
}
Comment on lines +203 to 205
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was needed to support list(string) inputs for tests.


const proc = spawn(
Expand Down
Loading