Skip to content

Commit 3936a8b

Browse files
committed
add explicit form_types, and multi-select
1 parent e02b0d0 commit 3936a8b

File tree

4 files changed

+346
-2
lines changed

4 files changed

+346
-2
lines changed

extract/parameter.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ func ParameterFromBlock(block *terraform.Block) (*types.Parameter, hcl.Diagnosti
6969
Description: optionalString(block, "description"),
7070
Type: pType,
7171
FormType: formType,
72-
FormTypeMetadata: ftmeta,
72+
FormTypeMetadata: formTypeMeta,
7373
Mutable: optionalBoolean(block, "mutable"),
7474
// Default value is always written as a string, then converted
7575
// to the correct type.
Lines changed: 342 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,342 @@
1+
terraform {
2+
required_version = ">= 1.0"
3+
4+
required_providers {
5+
coder = {
6+
source = "coder/coder"
7+
version = ">= 0.17"
8+
}
9+
http = {
10+
source = "hashicorp/http"
11+
version = ">= 3.0"
12+
}
13+
}
14+
}
15+
16+
variable "agent_id" {
17+
type = string
18+
description = "The ID of a Coder agent."
19+
}
20+
21+
variable "slug" {
22+
type = string
23+
description = "The slug for the coder_app. Allows resuing the module with the same template."
24+
default = "gateway"
25+
}
26+
27+
variable "agent_name" {
28+
type = string
29+
description = "Agent name. (unused). Will be removed in a future version"
30+
31+
default = ""
32+
}
33+
34+
variable "folder" {
35+
type = string
36+
description = "The directory to open in the IDE. e.g. /home/coder/project"
37+
validation {
38+
condition = can(regex("^(?:/[^/]+)+$", var.folder))
39+
error_message = "The folder must be a full path and must not start with a ~."
40+
}
41+
}
42+
43+
variable "default" {
44+
default = ""
45+
type = string
46+
description = "Default IDE"
47+
}
48+
49+
variable "order" {
50+
type = number
51+
description = "The order determines the position of app in the UI presentation. The lowest order is shown first and apps with equal order are sorted by name (ascending order)."
52+
default = null
53+
}
54+
55+
variable "coder_parameter_order" {
56+
type = number
57+
description = "The order determines the position of a template parameter in the UI/CLI presentation. The lowest order is shown first and parameters with equal order are sorted by name (ascending order)."
58+
default = null
59+
}
60+
61+
variable "latest" {
62+
type = bool
63+
description = "Whether to fetch the latest version of the IDE."
64+
default = false
65+
}
66+
67+
variable "channel" {
68+
type = string
69+
description = "JetBrains IDE release channel. Valid values are release and eap."
70+
default = "release"
71+
validation {
72+
condition = can(regex("^(release|eap)$", var.channel))
73+
error_message = "The channel must be either release or eap."
74+
}
75+
}
76+
77+
variable "jetbrains_ide_versions" {
78+
type = map(object({
79+
build_number = string
80+
version = string
81+
}))
82+
description = "The set of versions for each jetbrains IDE"
83+
default = {
84+
"IU" = {
85+
build_number = "243.21565.193"
86+
version = "2024.3"
87+
}
88+
"PS" = {
89+
build_number = "243.21565.202"
90+
version = "2024.3"
91+
}
92+
"WS" = {
93+
build_number = "243.21565.180"
94+
version = "2024.3"
95+
}
96+
"PY" = {
97+
build_number = "243.21565.199"
98+
version = "2024.3"
99+
}
100+
"CL" = {
101+
build_number = "243.21565.238"
102+
version = "2024.1"
103+
}
104+
"GO" = {
105+
build_number = "243.21565.208"
106+
version = "2024.3"
107+
}
108+
"RM" = {
109+
build_number = "243.21565.197"
110+
version = "2024.3"
111+
}
112+
"RD" = {
113+
build_number = "243.21565.191"
114+
version = "2024.3"
115+
}
116+
"RR" = {
117+
build_number = "243.22562.230"
118+
version = "2024.3"
119+
}
120+
}
121+
validation {
122+
condition = (
123+
alltrue([
124+
for code in keys(var.jetbrains_ide_versions) : contains(["IU", "PS", "WS", "PY", "CL", "GO", "RM", "RD", "RR"], code)
125+
])
126+
)
127+
error_message = "The jetbrains_ide_versions must contain a map of valid product codes. Valid product codes are ${join(",", ["IU", "PS", "WS", "PY", "CL", "GO", "RM", "RD", "RR"])}."
128+
}
129+
}
130+
131+
variable "jetbrains_ides" {
132+
type = list(string)
133+
description = "The list of IDE product codes."
134+
default = ["IU", "PS", "WS", "PY", "CL", "GO", "RM", "RD", "RR"]
135+
validation {
136+
condition = (
137+
alltrue([
138+
for code in var.jetbrains_ides : contains(["IU", "PS", "WS", "PY", "CL", "GO", "RM", "RD", "RR"], code)
139+
])
140+
)
141+
error_message = "The jetbrains_ides must be a list of valid product codes. Valid product codes are ${join(",", ["IU", "PS", "WS", "PY", "CL", "GO", "RM", "RD", "RR"])}."
142+
}
143+
# check if the list is empty
144+
validation {
145+
condition = length(var.jetbrains_ides) > 0
146+
error_message = "The jetbrains_ides must not be empty."
147+
}
148+
# check if the list contains duplicates
149+
validation {
150+
condition = length(var.jetbrains_ides) == length(toset(var.jetbrains_ides))
151+
error_message = "The jetbrains_ides must not contain duplicates."
152+
}
153+
}
154+
155+
variable "releases_base_link" {
156+
type = string
157+
description = ""
158+
default = "https://data.services.jetbrains.com"
159+
validation {
160+
condition = can(regex("^https?://.+$", var.releases_base_link))
161+
error_message = "The releases_base_link must be a valid HTTP/S address."
162+
}
163+
}
164+
165+
variable "download_base_link" {
166+
type = string
167+
description = ""
168+
default = "https://download.jetbrains.com"
169+
validation {
170+
condition = can(regex("^https?://.+$", var.download_base_link))
171+
error_message = "The download_base_link must be a valid HTTP/S address."
172+
}
173+
}
174+
175+
data "http" "jetbrains_ide_versions" {
176+
for_each = var.latest ? toset(var.jetbrains_ides) : toset([])
177+
url = "${var.releases_base_link}/products/releases?code=${each.key}&latest=true&type=${var.channel}"
178+
}
179+
180+
locals {
181+
jetbrains_ides = {
182+
"GO" = {
183+
icon = "/icon/goland.svg",
184+
name = "GoLand",
185+
identifier = "GO",
186+
build_number = var.jetbrains_ide_versions["GO"].build_number,
187+
download_link = "${var.download_base_link}/go/goland-${var.jetbrains_ide_versions["GO"].version}.tar.gz"
188+
version = var.jetbrains_ide_versions["GO"].version
189+
},
190+
"WS" = {
191+
icon = "/icon/webstorm.svg",
192+
name = "WebStorm",
193+
identifier = "WS",
194+
build_number = var.jetbrains_ide_versions["WS"].build_number,
195+
download_link = "${var.download_base_link}/webstorm/WebStorm-${var.jetbrains_ide_versions["WS"].version}.tar.gz"
196+
version = var.jetbrains_ide_versions["WS"].version
197+
},
198+
"IU" = {
199+
icon = "/icon/intellij.svg",
200+
name = "IntelliJ IDEA Ultimate",
201+
identifier = "IU",
202+
build_number = var.jetbrains_ide_versions["IU"].build_number,
203+
download_link = "${var.download_base_link}/idea/ideaIU-${var.jetbrains_ide_versions["IU"].version}.tar.gz"
204+
version = var.jetbrains_ide_versions["IU"].version
205+
},
206+
"PY" = {
207+
icon = "/icon/pycharm.svg",
208+
name = "PyCharm Professional",
209+
identifier = "PY",
210+
build_number = var.jetbrains_ide_versions["PY"].build_number,
211+
download_link = "${var.download_base_link}/python/pycharm-professional-${var.jetbrains_ide_versions["PY"].version}.tar.gz"
212+
version = var.jetbrains_ide_versions["PY"].version
213+
},
214+
"CL" = {
215+
icon = "/icon/clion.svg",
216+
name = "CLion",
217+
identifier = "CL",
218+
build_number = var.jetbrains_ide_versions["CL"].build_number,
219+
download_link = "${var.download_base_link}/cpp/CLion-${var.jetbrains_ide_versions["CL"].version}.tar.gz"
220+
version = var.jetbrains_ide_versions["CL"].version
221+
},
222+
"PS" = {
223+
icon = "/icon/phpstorm.svg",
224+
name = "PhpStorm",
225+
identifier = "PS",
226+
build_number = var.jetbrains_ide_versions["PS"].build_number,
227+
download_link = "${var.download_base_link}/webide/PhpStorm-${var.jetbrains_ide_versions["PS"].version}.tar.gz"
228+
version = var.jetbrains_ide_versions["PS"].version
229+
},
230+
"RM" = {
231+
icon = "/icon/rubymine.svg",
232+
name = "RubyMine",
233+
identifier = "RM",
234+
build_number = var.jetbrains_ide_versions["RM"].build_number,
235+
download_link = "${var.download_base_link}/ruby/RubyMine-${var.jetbrains_ide_versions["RM"].version}.tar.gz"
236+
version = var.jetbrains_ide_versions["RM"].version
237+
},
238+
"RD" = {
239+
icon = "/icon/rider.svg",
240+
name = "Rider",
241+
identifier = "RD",
242+
build_number = var.jetbrains_ide_versions["RD"].build_number,
243+
download_link = "${var.download_base_link}/rider/JetBrains.Rider-${var.jetbrains_ide_versions["RD"].version}.tar.gz"
244+
version = var.jetbrains_ide_versions["RD"].version
245+
},
246+
"RR" = {
247+
icon = "/icon/rustrover.svg",
248+
name = "RustRover",
249+
identifier = "RR",
250+
build_number = var.jetbrains_ide_versions["RR"].build_number,
251+
download_link = "${var.download_base_link}/rustrover/RustRover-${var.jetbrains_ide_versions["RR"].version}.tar.gz"
252+
version = var.jetbrains_ide_versions["RR"].version
253+
}
254+
}
255+
256+
icon = local.jetbrains_ides[data.coder_parameter.jetbrains_ide.value].icon
257+
json_data = var.latest ? jsondecode(data.http.jetbrains_ide_versions[data.coder_parameter.jetbrains_ide.value].response_body) : {}
258+
key = var.latest ? keys(local.json_data)[0] : ""
259+
display_name = local.jetbrains_ides[data.coder_parameter.jetbrains_ide.value].name
260+
identifier = data.coder_parameter.jetbrains_ide.value
261+
download_link = var.latest ? local.json_data[local.key][0].downloads.linux.link : local.jetbrains_ides[data.coder_parameter.jetbrains_ide.value].download_link
262+
build_number = var.latest ? local.json_data[local.key][0].build : local.jetbrains_ides[data.coder_parameter.jetbrains_ide.value].build_number
263+
version = var.latest ? local.json_data[local.key][0].version : var.jetbrains_ide_versions[data.coder_parameter.jetbrains_ide.value].version
264+
}
265+
266+
data "coder_parameter" "jetbrains_ide" {
267+
type = "list(string)"
268+
form_type = "multi-select"
269+
name = "jetbrains_ide"
270+
display_name = "JetBrains IDE"
271+
icon = "/icon/gateway.svg"
272+
mutable = true
273+
default = jsonencode([var.default == "" ? var.jetbrains_ides[0] : var.default])
274+
order = var.coder_parameter_order
275+
276+
dynamic "option" {
277+
for_each = var.jetbrains_ides
278+
content {
279+
icon = local.jetbrains_ides[option.value].icon
280+
name = local.jetbrains_ides[option.value].name
281+
value = option.value
282+
}
283+
}
284+
}
285+
286+
data "coder_workspace" "me" {}
287+
data "coder_workspace_owner" "me" {}
288+
289+
resource "coder_app" "gateway" {
290+
agent_id = var.agent_id
291+
slug = var.slug
292+
display_name = local.display_name
293+
icon = local.icon
294+
external = true
295+
order = var.order
296+
url = join("", [
297+
"jetbrains-gateway://connect#type=coder&workspace=",
298+
data.coder_workspace.me.name,
299+
"&owner=",
300+
data.coder_workspace_owner.me.name,
301+
"&folder=",
302+
var.folder,
303+
"&url=",
304+
data.coder_workspace.me.access_url,
305+
"&token=",
306+
"$SESSION_TOKEN",
307+
"&ide_product_code=",
308+
data.coder_parameter.jetbrains_ide.value,
309+
"&ide_build_number=",
310+
local.build_number,
311+
"&ide_download_link=",
312+
local.download_link,
313+
])
314+
}
315+
316+
output "identifier" {
317+
value = local.identifier
318+
}
319+
320+
output "display_name" {
321+
value = local.display_name
322+
}
323+
324+
output "icon" {
325+
value = local.icon
326+
}
327+
328+
output "download_link" {
329+
value = local.download_link
330+
}
331+
332+
output "build_number" {
333+
value = local.build_number
334+
}
335+
336+
output "version" {
337+
value = local.version
338+
}
339+
340+
output "url" {
341+
value = coder_app.gateway.url
342+
}

testdata/demo_flat/main.tf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ data coder_workspace_owner "me" {}
2121

2222
module "jetbrains_gateway" {
2323
count = 1
24-
source = "registry.coder.com/modules/jetbrains-gateway/coder"
24+
source = "./jetbrains_ide"
2525
version = "1.0.28"
2626
agent_id = "random"
2727
folder = "/home/coder/example"

testdata/demo_flat/parameters.tf

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ data "coder_workspace_tags" "test" {
9999
}
100100

101101
// Advanced admin parameter
102+
// preview -g admin -p plan.json -v hash="52bb4d943694f2f5867a251780f85e5a68906787b4ffa3157e29b9ef510b1a97"
102103
data "coder_parameter" "image_hash" {
103104
count = local.isAdmin ? 1 : 0
104105
name = "hash"
@@ -122,6 +123,7 @@ data "coder_parameter" "region" {
122123
name = "Region"
123124
display_name = "Region"
124125
description = "What region are you in?"
126+
form_type = "dropdown"
125127
default = local.default_region
126128
icon = "/icon/memory.svg"
127129
mutable = false

0 commit comments

Comments
 (0)