Skip to content

Add terraform validation to linting #170

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Feb 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .sample/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ resource "coder_app" "MODULE_NAME" {
slug = "MODULE_NAME"
display_name = "MODULE_NAME"
url = "http://localhost:${var.port}"
icon = loocal.icon_url
icon = local.icon_url
subdomain = false
share = "owner"

Expand Down
8 changes: 3 additions & 5 deletions jetbrains-gateway/main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,16 @@ describe("jetbrains-gateway", async () => {
await testRequiredVariables(import.meta.dir, {
agent_id: "foo",
agent_name: "foo",
folder: "/baz/",
folder: "/home/foo",
});

it("default to first ide", async () => {
const state = await runTerraformApply(import.meta.dir, {
agent_id: "foo",
agent_name: "foo",
folder: "/baz/",
folder: "/home/foo",
jetbrains_ides: '["IU", "GO", "PY"]',
});
expect(state.outputs.jetbrains_ides.value).toBe(
'["IU","232.10203.10","https://download.jetbrains.com/idea/ideaIU-2023.2.4.tar.gz"]',
);
expect(state.outputs.identifier.value).toBe("IU");
});
});
124 changes: 80 additions & 44 deletions jetbrains-gateway/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ variable "agent_name" {
variable "folder" {
type = string
description = "The directory to open in the IDE. e.g. /home/coder/project"
validation {
condition = can(regex("^(?:/[^/]+)+$", var.folder))
error_message = "The folder must be a full path and must not start with a ~."
}
}

variable "default" {
Expand All @@ -30,10 +34,6 @@ variable "default" {
description = "Default IDE"
}

locals {
supported_ides = ["IU", "PS", "WS", "PY", "CL", "GO", "RM"]
}

variable "jetbrains_ide_versions" {
type = map(object({
build_number = string
Expand Down Expand Up @@ -69,29 +69,28 @@ variable "jetbrains_ide_versions" {
build_number = "232.10203.15"
version = "2023.2.4"
}

}
validation {
condition = (
alltrue([
for code in var.jetbrains_ide_versions : contains(local.supported_ides, code)
for code in keys(var.jetbrains_ide_versions) : contains(["IU", "PS", "WS", "PY", "CL", "GO", "RM"], code)
])
)
error_message = "The jetbrains_ide_versions must contain a map of valid product codes. Valid product codes are ${join(",", local.supported_ides)}."
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"])}."
}
}

variable "jetbrains_ides" {
type = list(string)
description = "The list of IDE product codes."
default = local.supported_ides
default = ["IU", "PS", "WS", "PY", "CL", "GO", "RM"]
validation {
condition = (
alltrue([
for code in var.jetbrains_ides : contains(local.supported_ides, code)
for code in var.jetbrains_ides : contains(["IU", "PS", "WS", "PY", "CL", "GO", "RM"], code)
])
)
error_message = "The jetbrains_ides must be a list of valid product codes. Valid product codes are ${join(",", local.supported_ides)}."
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"])}."
}
# check if the list is empty
validation {
Expand All @@ -108,58 +107,71 @@ variable "jetbrains_ides" {
locals {
jetbrains_ides = {
"GO" = {
icon = "/icon/goland.svg",
name = "GoLand",
value = jsonencode(["GO", var.jetbrains_ide_versions["GO"].build_number, "https://download.jetbrains.com/go/goland-${var.jetbrains_ide_versions["GO"].version}.tar.gz"])
icon = "/icon/goland.svg",
name = "GoLand",
identifier = "GO",
build_number = var.jetbrains_ide_versions["GO"].build_number,
download_link = "https://download.jetbrains.com/go/goland-${var.jetbrains_ide_versions["GO"].version}.tar.gz"
},
"WS" = {
icon = "/icon/webstorm.svg",
name = "WebStorm",
value = jsonencode(["WS", var.jetbrains_ide_versions["WS"].build_number, "https://download.jetbrains.com/webstorm/WebStorm-${var.jetbrains_ide_versions["WS"].version}.tar.gz"])
icon = "/icon/webstorm.svg",
name = "WebStorm",
identifier = "WS",
build_number = var.jetbrains_ide_versions["WS"].build_number,
download_link = "https://download.jetbrains.com/webstorm/WebStorm-${var.jetbrains_ide_versions["WS"].version}.tar.gz"
},
"IU" = {
icon = "/icon/intellij.svg",
name = "IntelliJ IDEA Ultimate",
value = jsonencode(["IU", var.jetbrains_ide_versions["IU"].build_number, "https://download.jetbrains.com/idea/ideaIU-${var.jetbrains_ide_versions["IU"].version}.tar.gz"])
icon = "/icon/intellij.svg",
name = "IntelliJ IDEA Ultimate",
identifier = "IU",
build_number = var.jetbrains_ide_versions["IU"].build_number,
download_link = "https://download.jetbrains.com/idea/ideaIU-${var.jetbrains_ide_versions["IU"].version}.tar.gz"
},
"PY" = {
icon = "/icon/pycharm.svg",
name = "PyCharm Professional",
value = jsonencode(["PY", var.jetbrains_ide_versions["PY"].build_number, "https://download.jetbrains.com/python/pycharm-professional-${var.jetbrains_ide_versions["PY"].version}.tar.gz"])
icon = "/icon/pycharm.svg",
name = "PyCharm Professional",
identifier = "PY",
build_number = var.jetbrains_ide_versions["PY"].build_number,
download_link = "https://download.jetbrains.com/python/pycharm-professional-${var.jetbrains_ide_versions["PY"].version}.tar.gz"
},
"CL" = {
icon = "/icon/clion.svg",
name = "CLion",
value = jsonencode(["CL", var.jetbrains_ide_versions["CL"].build_number, "https://download.jetbrains.com/cpp/CLion-${var.jetbrains_ide_versions["CL"].version}.tar.gz"])
icon = "/icon/clion.svg",
name = "CLion",
identifier = "CL",
build_number = var.jetbrains_ide_versions["CL"].build_number,
download_link = "https://download.jetbrains.com/cpp/CLion-${var.jetbrains_ide_versions["CL"].version}.tar.gz"
},
"PS" = {
icon = "/icon/phpstorm.svg",
name = "PhpStorm",
value = jsonencode(["PS", var.jetbrains_ide_versions["PS"].build_number, "https://download.jetbrains.com/webide/PhpStorm-${var.jetbrains_ide_versions["PS"].version}.tar.gz"])
icon = "/icon/phpstorm.svg",
name = "PhpStorm",
identifier = "PS",
build_number = var.jetbrains_ide_versions["PS"].build_number,
download_link = "https://download.jetbrains.com/webide/PhpStorm-${var.jetbrains_ide_versions["PS"].version}.tar.gz"
},
"RM" = {
icon = "/icon/rubymine.svg",
name = "RubyMine",
value = jsonencode(["RM", var.jetbrains_ide_versions["RM"].build_number, "https://download.jetbrains.com/ruby/RubyMine-${var.jetbrains_ide_versions["RM"].version}.tar.gz"])
icon = "/icon/rubymine.svg",
name = "RubyMine",
identifier = "RM",
build_number = var.jetbrains_ide_versions["RM"].build_number,
download_link = "https://download.jetbrains.com/ruby/RubyMine-${var.jetbrains_ide_versions["RM"].version}.tar.gz"
}
}
}

data "coder_parameter" "jetbrains_ide" {
type = "list(string)"
type = "string"
name = "jetbrains_ide"
display_name = "JetBrains IDE"
icon = "/icon/gateway.svg"
mutable = true
# check if default is in the jet_brains_ides list and if it is not empty or null otherwise set it to null
default = var.default != null && var.default != "" && contains(var.jetbrains_ides, var.default) ? local.jetbrains_ides[var.default].value : local.jetbrains_ides[var.jetbrains_ides[0]].value
default = var.default == "" ? var.jetbrains_ides[0] : var.default

dynamic "option" {
for_each = { for key, value in local.jetbrains_ides : key => value if contains(var.jetbrains_ides, key) }
for_each = var.jetbrains_ides
content {
icon = option.value.icon
name = option.value.name
value = option.value.value
icon = lookup(local.jetbrains_ides, option.value).icon
name = lookup(local.jetbrains_ides, option.value).name
value = lookup(local.jetbrains_ides, option.value).identifier
}
}
}
Expand All @@ -168,9 +180,9 @@ data "coder_workspace" "me" {}

resource "coder_app" "gateway" {
agent_id = var.agent_id
display_name = data.coder_parameter.jetbrains_ide.option[index(data.coder_parameter.jetbrains_ide.option.*.value, data.coder_parameter.jetbrains_ide.value)].name
slug = "gateway"
icon = data.coder_parameter.jetbrains_ide.option[index(data.coder_parameter.jetbrains_ide.option.*.value, data.coder_parameter.jetbrains_ide.value)].icon
display_name = try(lookup(local.jetbrains_ides, data.coder_parameter.jetbrains_ide.value).name, "JetBrains IDE")
icon = try(lookup(local.jetbrains_ides, data.coder_parameter.jetbrains_ide.value).icon, "/icon/gateway.svg")
external = true
url = join("", [
"jetbrains-gateway://connect#type=coder&workspace=",
Expand All @@ -184,14 +196,38 @@ resource "coder_app" "gateway" {
"&token=",
"$SESSION_TOKEN",
"&ide_product_code=",
jsondecode(data.coder_parameter.jetbrains_ide.value)[0],
local.jetbrains_ides[data.coder_parameter.jetbrains_ide.value].identifier,
"&ide_build_number=",
jsondecode(data.coder_parameter.jetbrains_ide.value)[1],
local.jetbrains_ides[data.coder_parameter.jetbrains_ide.value].build_number,
"&ide_download_link=",
jsondecode(data.coder_parameter.jetbrains_ide.value)[2],
local.jetbrains_ides[data.coder_parameter.jetbrains_ide.value].download_link
])
}

output "jetbrains_ides" {
output "identifier" {
value = data.coder_parameter.jetbrains_ide.value
}

output "name" {
value = coder_app.gateway.display_name
}

output "icon" {
value = coder_app.gateway.icon
}

output "download_link" {
value = lookup(local.jetbrains_ides, data.coder_parameter.jetbrains_ide.value).download_link
}

output "build_number" {
value = lookup(local.jetbrains_ides, data.coder_parameter.jetbrains_ide.value).build_number
}

output "version" {
value = var.jetbrains_ide_versions[data.coder_parameter.jetbrains_ide.value].version
}

output "url" {
value = coder_app.gateway.url
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"test": "bun test",
"fmt": "bun x prettier -w **/*.sh .sample/run.sh new.sh **/*.ts **/*.md *.md && terraform fmt **/*.tf .sample/main.tf",
"fmt:ci": "bun x prettier --check **/*.sh .sample/run.sh new.sh **/*.ts **/*.md *.md && terraform fmt -check **/*.tf .sample/main.tf",
"lint": "bun run lint.ts",
"lint": "bun run lint.ts && ./terraform_validate.sh",
"update-version": "./update-version.sh"
},
"devDependencies": {
Expand Down
29 changes: 29 additions & 0 deletions terraform_validate.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/bin/bash

set -euo pipefail

# Function to run terraform init and validate in a directory
run_terraform() {
local dir="$1"
echo "Running terraform init and validate in $dir"
pushd "$dir"
terraform init -upgrade
terraform validate
popd
}

# Main script
main() {
# Get the directory of the script
script_dir=$(dirname "$(readlink -f "$0")")

# Get all subdirectories in the repository
subdirs=$(find "$script_dir" -mindepth 1 -maxdepth 1 -type d -not -name ".*" | sort)

for dir in $subdirs; do
run_terraform "$dir"
done
}

# Run the main script
main