diff --git a/Makefile b/Makefile index 38a9a69d..4c3f888c 100644 --- a/Makefile +++ b/Makefile @@ -6,6 +6,7 @@ fmt: gen: go run github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs@latest go run ./scripts/docsgen/... + terraform fmt -recursive build: terraform-provider-coder diff --git a/README.md b/README.md index 4ae9be15..5c6d0f57 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,25 @@ to setup your local Terraform to use your local version rather than the registry ⚠️ Be sure to include `/v2` in the module path as it needs to match the version declared in the provider’s `go.mod`. +#### Documentation + +When adding new resources or attributes, use `@since:vX.Y.Z` markers to document version requirements: + +```go +// For new resources +Description: "Define a new feature. @since:v2.25.0", + +// For new attributes +"my_attribute": { + Description: "New feature flag. @since:v2.25.0", +} +``` + +Run `make gen` to generate documentation. The markers will be automatically: +- Extracted and formatted as version notes +- Sanitized from the final docs +- Validated for proper semver format + #### Terraform Acceptance Tests To run Terraform acceptance tests, run `make testacc`. This will test the provider against the locally installed version of Terraform. diff --git a/docs/data-sources/external_auth.md b/docs/data-sources/external_auth.md index d1e6d649..a267cfda 100644 --- a/docs/data-sources/external_auth.md +++ b/docs/data-sources/external_auth.md @@ -3,7 +3,7 @@ page_title: "coder_external_auth Data Source - terraform-provider-coder" subcategory: "" description: |- - Use this data source to require users to authenticate with an external service prior to workspace creation. This can be used to pre-authenticate external services https://coder.com/docs/admin/external-auth in a workspace. (e.g. Google Cloud, Github, Docker, etc.) + Use this data source to require users to authenticate with an external service prior to workspace creation. This can be used to pre-authenticate external services https://coder.com/docs/admin/external-auth in a workspace. (e.g. Google Cloud, Github, Docker, etc.) --- # coder_external_auth (Data Source) @@ -17,12 +17,12 @@ provider "coder" {} data "coder_external_auth" "github" { - id = "github" + id = "github" } data "coder_external_auth" "azure-identity" { - id = "azure-identiy" - optional = true + id = "azure-identiy" + optional = true } ``` diff --git a/docs/data-sources/parameter.md b/docs/data-sources/parameter.md index c1001835..4ff4f551 100644 --- a/docs/data-sources/parameter.md +++ b/docs/data-sources/parameter.md @@ -3,7 +3,7 @@ page_title: "coder_parameter Data Source - terraform-provider-coder" subcategory: "" description: |- - Use this data source to configure editable options for workspaces. + Use this data source to configure editable options for workspaces. --- # coder_parameter (Data Source) @@ -16,119 +16,119 @@ Use this data source to configure editable options for workspaces. provider "coder" {} data "coder_parameter" "example" { - name = "Region" - description = "Specify a region to place your workspace." - mutable = false - type = "string" - default = "us-central1-a" - option { - value = "us-central1-a" - name = "US Central" - icon = "/icons/1f1fa-1f1f8.png" - } - option { - value = "asia-southeast1-a" - name = "Singapore" - icon = "/icons/1f1f8-1f1ec.png" - } + name = "Region" + description = "Specify a region to place your workspace." + mutable = false + type = "string" + default = "us-central1-a" + option { + value = "us-central1-a" + name = "US Central" + icon = "/icons/1f1fa-1f1f8.png" + } + option { + value = "asia-southeast1-a" + name = "Singapore" + icon = "/icons/1f1f8-1f1ec.png" + } } data "coder_parameter" "ami" { - name = "Machine Image" - description = <<-EOT - # Provide the machine image - See the [registry](https://container.registry.blah/namespace) for options. - EOT - option { - value = "ami-xxxxxxxx" - name = "Ubuntu" - icon = "/icon/ubuntu.svg" - } + name = "Machine Image" + description = <<-EOT + # Provide the machine image + See the [registry](https://container.registry.blah/namespace) for options. + EOT + option { + value = "ami-xxxxxxxx" + name = "Ubuntu" + icon = "/icon/ubuntu.svg" + } } data "coder_parameter" "is_public_instance" { - name = "Is public instance?" - type = "bool" - icon = "/icon/docker.svg" - default = false + name = "Is public instance?" + type = "bool" + icon = "/icon/docker.svg" + default = false } data "coder_parameter" "cores" { - name = "CPU Cores" - type = "number" - icon = "/icon/cpu.svg" - default = 3 - order = 10 + name = "CPU Cores" + type = "number" + icon = "/icon/cpu.svg" + default = 3 + order = 10 } data "coder_parameter" "disk_size" { - name = "Disk Size" - type = "number" - default = "5" - order = 8 - validation { - # This can apply to number. - min = 0 - max = 10 - monotonic = "increasing" - } + name = "Disk Size" + type = "number" + default = "5" + order = 8 + validation { + # This can apply to number. + min = 0 + max = 10 + monotonic = "increasing" + } } data "coder_parameter" "cat_lives" { - name = "Cat Lives" - type = "number" - default = "9" - validation { - # This can apply to number. - min = 0 - max = 10 - monotonic = "decreasing" - } + name = "Cat Lives" + type = "number" + default = "9" + validation { + # This can apply to number. + min = 0 + max = 10 + monotonic = "decreasing" + } } data "coder_parameter" "fairy_tale" { - name = "Fairy Tale" - type = "string" - mutable = true - default = "Hansel and Gretel" - ephemeral = true + name = "Fairy Tale" + type = "string" + mutable = true + default = "Hansel and Gretel" + ephemeral = true } data "coder_parameter" "users" { - name = "system_users" - display_name = "System users" - type = "list(string)" - default = jsonencode(["root", "user1", "user2"]) + name = "system_users" + display_name = "System users" + type = "list(string)" + default = jsonencode(["root", "user1", "user2"]) } data "coder_parameter" "home_volume_size" { - name = "Home Volume Size" - description = <<-EOF - How large should your home volume be? - EOF - type = "number" - default = 30 - mutable = true - order = 3 - - option { - name = "30GB" - value = 30 - } - - option { - name = "60GB" - value = 60 - } - - option { - name = "100GB" - value = 100 - } - - validation { - monotonic = "increasing" - } + name = "Home Volume Size" + description = <<-EOF + How large should your home volume be? + EOF + type = "number" + default = 30 + mutable = true + order = 3 + + option { + name = "30GB" + value = 30 + } + + option { + name = "60GB" + value = 60 + } + + option { + name = "100GB" + value = 100 + } + + validation { + monotonic = "increasing" + } } ``` diff --git a/docs/data-sources/provisioner.md b/docs/data-sources/provisioner.md index ba930a2a..a1ddbe79 100644 --- a/docs/data-sources/provisioner.md +++ b/docs/data-sources/provisioner.md @@ -3,7 +3,7 @@ page_title: "coder_provisioner Data Source - terraform-provider-coder" subcategory: "" description: |- - Use this data source to get information about the Coder provisioner. + Use this data source to get information about the Coder provisioner. --- # coder_provisioner (Data Source) @@ -20,15 +20,15 @@ data "coder_provisioner" "dev" {} data "coder_workspace" "dev" {} resource "coder_agent" "main" { - arch = data.coder_provisioner.dev.arch - os = data.coder_provisioner.dev.os - dir = "/workspace" - display_apps { - vscode = true - vscode_insiders = false - web_terminal = true - ssh_helper = false - } + arch = data.coder_provisioner.dev.arch + os = data.coder_provisioner.dev.os + dir = "/workspace" + display_apps { + vscode = true + vscode_insiders = false + web_terminal = true + ssh_helper = false + } } ``` diff --git a/docs/data-sources/workspace.md b/docs/data-sources/workspace.md index 100e3381..010549f1 100644 --- a/docs/data-sources/workspace.md +++ b/docs/data-sources/workspace.md @@ -3,7 +3,7 @@ page_title: "coder_workspace Data Source - terraform-provider-coder" subcategory: "" description: |- - Use this data source to get information for the active workspace build. + Use this data source to get information for the active workspace build. --- # coder_workspace (Data Source) @@ -22,42 +22,42 @@ data "coder_workspace" "me" {} data "coder_workspace_owner" "me" {} resource "coder_agent" "dev" { - arch = "amd64" - os = "linux" - dir = "/workspace" + arch = "amd64" + os = "linux" + dir = "/workspace" } resource "docker_container" "workspace" { - count = data.coder_workspace.me.start_count - image = docker_image.main.name - # Uses lower() to avoid Docker restriction on container names. - name = "coder-${data.coder_workspace_owner.me.name}-${lower(data.coder_workspace.me.name)}" - # Hostname makes the shell more user friendly: coder@my-workspace:~$ - hostname = data.coder_workspace.me.name - # Use the docker gateway if the access URL is 127.0.0.1 - entrypoint = ["sh", "-c", replace(coder_agent.main.init_script, "/localhost|127\\.0\\.0\\.1/", "host.docker.internal")] - env = ["CODER_AGENT_TOKEN=${coder_agent.main.token}"] - host { - host = "host.docker.internal" - ip = "host-gateway" - } - # Add labels in Docker to keep track of orphan resources. - labels { - label = "coder.owner" - value = data.coder_workspace_owner.me.name - } - labels { - label = "coder.owner_id" - value = data.coder_workspace_owner.me.id - } - labels { - label = "coder.workspace_id" - value = data.coder_workspace.me.id - } - labels { - label = "coder.workspace_name" - value = data.coder_workspace.me.name - } + count = data.coder_workspace.me.start_count + image = docker_image.main.name + # Uses lower() to avoid Docker restriction on container names. + name = "coder-${data.coder_workspace_owner.me.name}-${lower(data.coder_workspace.me.name)}" + # Hostname makes the shell more user friendly: coder@my-workspace:~$ + hostname = data.coder_workspace.me.name + # Use the docker gateway if the access URL is 127.0.0.1 + entrypoint = ["sh", "-c", replace(coder_agent.main.init_script, "/localhost|127\\.0\\.0\\.1/", "host.docker.internal")] + env = ["CODER_AGENT_TOKEN=${coder_agent.main.token}"] + host { + host = "host.docker.internal" + ip = "host-gateway" + } + # Add labels in Docker to keep track of orphan resources. + labels { + label = "coder.owner" + value = data.coder_workspace_owner.me.name + } + labels { + label = "coder.owner_id" + value = data.coder_workspace_owner.me.id + } + labels { + label = "coder.workspace_id" + value = data.coder_workspace.me.id + } + labels { + label = "coder.workspace_name" + value = data.coder_workspace.me.name + } } ``` diff --git a/docs/data-sources/workspace_owner.md b/docs/data-sources/workspace_owner.md index f16480ef..0221c9ed 100644 --- a/docs/data-sources/workspace_owner.md +++ b/docs/data-sources/workspace_owner.md @@ -3,7 +3,7 @@ page_title: "coder_workspace_owner Data Source - terraform-provider-coder" subcategory: "" description: |- - Use this data source to fetch information about the workspace owner. + Use this data source to fetch information about the workspace owner. --- # coder_workspace_owner (Data Source) @@ -18,26 +18,26 @@ provider "coder" {} data "coder_workspace_owner" "me" {} resource "coder_agent" "dev" { - arch = "amd64" - os = "linux" - dir = "/workspace" - env = { - OIDC_TOKEN : data.coder_workspace_owner.me.oidc_access_token, - } + arch = "amd64" + os = "linux" + dir = "/workspace" + env = { + OIDC_TOKEN : data.coder_workspace_owner.me.oidc_access_token, + } } # Add git credentials from coder_workspace_owner resource "coder_env" "git_author_name" { - agent_id = coder_agent.agent_id - name = "GIT_AUTHOR_NAME" - value = coalesce(data.coder_workspace_owner.me.full_name, data.coder_workspace_owner.me.name) + agent_id = coder_agent.agent_id + name = "GIT_AUTHOR_NAME" + value = coalesce(data.coder_workspace_owner.me.full_name, data.coder_workspace_owner.me.name) } resource "coder_env" "git_author_email" { - agent_id = coder_agent.dev.id - name = "GIT_AUTHOR_EMAIL" - value = data.coder_workspace_owner.me.email - count = data.coder_workspace_owner.me.email != "" ? 1 : 0 + agent_id = coder_agent.dev.id + name = "GIT_AUTHOR_EMAIL" + value = data.coder_workspace_owner.me.email + count = data.coder_workspace_owner.me.email != "" ? 1 : 0 } ``` diff --git a/docs/data-sources/workspace_preset.md b/docs/data-sources/workspace_preset.md index e7de98e4..73e1fb07 100644 --- a/docs/data-sources/workspace_preset.md +++ b/docs/data-sources/workspace_preset.md @@ -3,7 +3,7 @@ page_title: "coder_workspace_preset Data Source - terraform-provider-coder" subcategory: "" description: |- - Use this data source to predefine common configurations for coder workspaces. Users will have the option to select a defined preset, which will automatically apply the selected configuration. Any parameters defined in the preset will be applied to the workspace. Parameters that are defined by the template but not defined by the preset will still be configurable when creating a workspace. + Use this data source to predefine common configurations for coder workspaces. Users will have the option to select a defined preset, which will automatically apply the selected configuration. Any parameters defined in the preset will be applied to the workspace. Parameters that are defined by the template but not defined by the preset will still be configurable when creating a workspace. --- # coder_workspace_preset (Data Source) @@ -21,25 +21,25 @@ provider "coder" {} # See the coder_parameter data source's documentation for examples of how to define # parameters like the ones used below. data "coder_workspace_preset" "example" { - name = "example" - description = "Example description of what this preset does." - icon = "/icon/example.svg" - parameters = { - (data.coder_parameter.example.name) = "us-central1-a" - (data.coder_parameter.ami.name) = "ami-xxxxxxxx" - } + name = "example" + description = "Example description of what this preset does." + icon = "/icon/example.svg" + parameters = { + (data.coder_parameter.example.name) = "us-central1-a" + (data.coder_parameter.ami.name) = "ami-xxxxxxxx" + } } # Example of a default preset that will be pre-selected for users data "coder_workspace_preset" "standard" { - name = "Standard" - description = "A workspace preset with medium compute in the US West region." - icon = "/icon/standard.svg" - default = true - parameters = { - (data.coder_parameter.instance_type.name) = "t3.medium" - (data.coder_parameter.region.name) = "us-west-2" - } + name = "Standard" + description = "A workspace preset with medium compute in the US West region." + icon = "/icon/standard.svg" + default = true + parameters = { + (data.coder_parameter.instance_type.name) = "t3.medium" + (data.coder_parameter.region.name) = "us-west-2" + } } ``` diff --git a/docs/data-sources/workspace_tags.md b/docs/data-sources/workspace_tags.md index 010adfe3..ef7835a1 100644 --- a/docs/data-sources/workspace_tags.md +++ b/docs/data-sources/workspace_tags.md @@ -3,7 +3,7 @@ page_title: "coder_workspace_tags Data Source - terraform-provider-coder" subcategory: "" description: |- - Use this data source to configure workspace tags to select provisioners. + Use this data source to configure workspace tags to select provisioners. --- # coder_workspace_tags (Data Source) @@ -16,52 +16,52 @@ Use this data source to configure workspace tags to select provisioners. provider "coder" {} data "coder_parameter" "os_selector" { - name = "os_selector" - display_name = "Operating System" - mutable = false - - default = "osx" - - option { - icon = "/icons/linux.png" - name = "Linux" - value = "linux" - } - option { - icon = "/icons/osx.png" - name = "OSX" - value = "osx" - } - option { - icon = "/icons/windows.png" - name = "Windows" - value = "windows" - } + name = "os_selector" + display_name = "Operating System" + mutable = false + + default = "osx" + + option { + icon = "/icons/linux.png" + name = "Linux" + value = "linux" + } + option { + icon = "/icons/osx.png" + name = "OSX" + value = "osx" + } + option { + icon = "/icons/windows.png" + name = "Windows" + value = "windows" + } } data "coder_parameter" "feature_cache_enabled" { - name = "feature_cache_enabled" - display_name = "Enable cache?" - type = "bool" + name = "feature_cache_enabled" + display_name = "Enable cache?" + type = "bool" - default = false + default = false } data "coder_parameter" "feature_debug_enabled" { - name = "feature_debug_enabled" - display_name = "Enable debug?" - type = "bool" + name = "feature_debug_enabled" + display_name = "Enable debug?" + type = "bool" - default = true + default = true } data "coder_workspace_tags" "custom_workspace_tags" { - tags = { - "cluster" = "developers" - "os" = data.coder_parameter.os_selector.value - "debug" = "${data.coder_parameter.feature_debug_enabled.value}+12345" - "cache" = data.coder_parameter.feature_cache_enabled.value == "true" ? "nix-with-cache" : "no-cache" - } + tags = { + "cluster" = "developers" + "os" = data.coder_parameter.os_selector.value + "debug" = "${data.coder_parameter.feature_debug_enabled.value}+12345" + "cache" = data.coder_parameter.feature_cache_enabled.value == "true" ? "nix-with-cache" : "no-cache" + } } ``` diff --git a/docs/index.md b/docs/index.md index c30f1477..bb97ff16 100644 --- a/docs/index.md +++ b/docs/index.md @@ -3,7 +3,7 @@ page_title: "Coder Provider" subcategory: "Infrastructure" description: |- - Terraform provider for managing Coder templates, which are the underlying infrastructure for Coder workspaces. + Terraform provider for managing Coder templates, which are the underlying infrastructure for Coder workspaces. --- # Coder Provider @@ -14,52 +14,58 @@ Terraform provider for managing Coder [templates](https://coder.com/docs/admin/t !> [`coder_git_auth`](https://registry.terraform.io/providers/coder/coder/1.0.4/docs/data-sources/git_auth) and owner related fields of [`coder_workspace`](https://registry.terraform.io/providers/coder/coder/1.0.4/docs/data-sources/workspace) data source have been removed. Follow the [Version 2 Upgrade Guide](https://registry.terraform.io/providers/coder/coder/latest/docs/guides/version-2-upgrade) to update your code. +## Version Compatibility + +This provider automatically documents version requirements for individual resources and features. + +~> **Note:** Individual resources may have higher version requirements. Check the documentation for each resource to see its specific minimum Coder version. + ## Example ```terraform terraform { - required_providers { - coder = { - source = "coder/coder" - } - } + required_providers { + coder = { + source = "coder/coder" + } + } } provider "google" { - region = "us-central1" + region = "us-central1" } data "coder_workspace" "me" {} resource "coder_agent" "dev" { - arch = "amd64" - os = "linux" - auth = "google-instance-identity" + arch = "amd64" + os = "linux" + auth = "google-instance-identity" } data "google_compute_default_service_account" "default" {} resource "google_compute_instance" "dev" { - zone = "us-central1-a" - count = data.coder_workspace.me.start_count - name = "coder-${data.coder_workspace.me.owner}-${data.coder_workspace.me.name}" - machine_type = "e2-medium" - network_interface { - network = "default" - access_config { - // Ephemeral public IP - } - } - boot_disk { - initialize_params { - image = "debian-cloud/debian-9" - } - } - service_account { - email = data.google_compute_default_service_account.default.email - scopes = ["cloud-platform"] - } - metadata_startup_script = coder_agent.dev.init_script + zone = "us-central1-a" + count = data.coder_workspace.me.start_count + name = "coder-${data.coder_workspace.me.owner}-${data.coder_workspace.me.name}" + machine_type = "e2-medium" + network_interface { + network = "default" + access_config { + // Ephemeral public IP + } + } + boot_disk { + initialize_params { + image = "debian-cloud/debian-9" + } + } + service_account { + email = data.google_compute_default_service_account.default.email + scopes = ["cloud-platform"] + } + metadata_startup_script = coder_agent.dev.init_script } ``` diff --git a/docs/resources/agent.md b/docs/resources/agent.md index 87ab4239..64b7d841 100644 --- a/docs/resources/agent.md +++ b/docs/resources/agent.md @@ -3,7 +3,7 @@ page_title: "coder_agent Resource - terraform-provider-coder" subcategory: "" description: |- - Use this resource to associate an agent. + Use this resource to associate an agent. --- # coder_agent (Resource) @@ -17,48 +17,48 @@ data "coder_workspace" "me" { } resource "coder_agent" "dev" { - os = "linux" - arch = "amd64" - dir = "/workspace" - api_key_scope = "all" - display_apps { - vscode = true - vscode_insiders = false - web_terminal = true - ssh_helper = false - } - - metadata { - display_name = "CPU Usage" - key = "cpu_usage" - script = "coder stat cpu" - interval = 10 - timeout = 1 - order = 2 - } - metadata { - display_name = "RAM Usage" - key = "ram_usage" - script = "coder stat mem" - interval = 10 - timeout = 1 - order = 1 - } - - order = 1 + os = "linux" + arch = "amd64" + dir = "/workspace" + api_key_scope = "all" + display_apps { + vscode = true + vscode_insiders = false + web_terminal = true + ssh_helper = false + } + + metadata { + display_name = "CPU Usage" + key = "cpu_usage" + script = "coder stat cpu" + interval = 10 + timeout = 1 + order = 2 + } + metadata { + display_name = "RAM Usage" + key = "ram_usage" + script = "coder stat mem" + interval = 10 + timeout = 1 + order = 1 + } + + order = 1 } resource "kubernetes_pod" "dev" { - count = data.coder_workspace.me.start_count - spec { - container { - command = ["sh", "-c", coder_agent.dev.init_script] - env { - name = "CODER_AGENT_TOKEN" - value = coder_agent.dev.token - } - } - } + count = data.coder_workspace.me.start_count + spec { + container { + command = ["sh", "-c", coder_agent.dev.init_script] + env { + name = "CODER_AGENT_TOKEN" + value = coder_agent.dev.token + } + } + } } ``` diff --git a/docs/resources/agent_instance.md b/docs/resources/agent_instance.md index ec855b12..063f6911 100644 --- a/docs/resources/agent_instance.md +++ b/docs/resources/agent_instance.md @@ -3,7 +3,7 @@ page_title: "coder_agent_instance Resource - terraform-provider-coder" subcategory: "" description: |- - Use this resource to associate an instance ID with an agent for zero-trust authentication. This association is done automatically for "google_compute_instance", "aws_instance", "azurerm_linux_virtual_machine", and "azurerm_windows_virtual_machine" resources. + Use this resource to associate an instance ID with an agent for zero-trust authentication. This association is done automatically for "google_compute_instance", "aws_instance", "azurerm_linux_virtual_machine", and "azurerm_windows_virtual_machine" resources. --- # coder_agent_instance (Resource) @@ -14,18 +14,18 @@ Use this resource to associate an instance ID with an agent for zero-trust authe ```terraform resource "coder_agent" "dev" { - os = "linux" - arch = "amd64" - auth = "google-instance-identity" + os = "linux" + arch = "amd64" + auth = "google-instance-identity" } resource "google_compute_instance" "dev" { - zone = "us-central1-a" + zone = "us-central1-a" } resource "coder_agent_instance" "dev" { - agent_id = coder_agent.dev.id - instance_id = google_compute_instance.dev.instance_id + agent_id = coder_agent.dev.id + instance_id = google_compute_instance.dev.instance_id } ``` diff --git a/docs/resources/ai_task.md b/docs/resources/ai_task.md index 80c3f419..f3eb7635 100644 --- a/docs/resources/ai_task.md +++ b/docs/resources/ai_task.md @@ -3,7 +3,7 @@ page_title: "coder_ai_task Resource - terraform-provider-coder" subcategory: "" description: |- - Use this resource to define Coder tasks. + Use this resource to define Coder tasks. --- # coder_ai_task (Resource) diff --git a/docs/resources/app.md b/docs/resources/app.md index 35a9951b..a802bef7 100644 --- a/docs/resources/app.md +++ b/docs/resources/app.md @@ -3,7 +3,7 @@ page_title: "coder_app Resource - terraform-provider-coder" subcategory: "" description: |- - Use this resource to define shortcuts to access applications in a workspace. + Use this resource to define shortcuts to access applications in a workspace. --- # coder_app (Resource) @@ -16,38 +16,38 @@ Use this resource to define shortcuts to access applications in a workspace. data "coder_workspace" "me" {} resource "coder_agent" "dev" { - os = "linux" - arch = "amd64" - dir = "/workspace" - startup_script = <"`. - `open_in` (String) Determines where the app will be opened. Valid values are `"tab"` and `"slim-window" (default)`. `"tab"` opens in a new tab in the same browser window. `"slim-window"` opens a new browser window without navigation controls. - `order` (Number) 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). diff --git a/docs/resources/devcontainer.md b/docs/resources/devcontainer.md index 06d7f6f3..d76591fa 100644 --- a/docs/resources/devcontainer.md +++ b/docs/resources/devcontainer.md @@ -3,15 +3,15 @@ page_title: "coder_devcontainer Resource - terraform-provider-coder" subcategory: "" description: |- - Define a Dev Container the agent should know of and attempt to autostart. - -> This resource is only available in Coder v2.21 and later. + Define a Dev Container the agent should know of and attempt to autostart. --- # coder_devcontainer (Resource) -Define a Dev Container the agent should know of and attempt to autostart. +Define a Dev Container the agent should know of and attempt to autostart. --> This resource is only available in Coder v2.21 and later. + +~> **Note:** This resource requires [Coder v2.21.0](https://github.com/coder/coder/releases/tag/v2.21.0) or later. diff --git a/docs/resources/env.md b/docs/resources/env.md index 3a5a7f3a..667234b7 100644 --- a/docs/resources/env.md +++ b/docs/resources/env.md @@ -3,7 +3,7 @@ page_title: "coder_env Resource - terraform-provider-coder" subcategory: "" description: |- - Use this resource to set an environment variable in a workspace. Note that this resource cannot be used to overwrite existing environment variables set on the coder_agent resource. + Use this resource to set an environment variable in a workspace. Note that this resource cannot be used to overwrite existing environment variables set on the coder_agent resource. --- # coder_env (Resource) @@ -16,21 +16,21 @@ Use this resource to set an environment variable in a workspace. Note that this data "coder_workspace" "me" {} resource "coder_agent" "dev" { - os = "linux" - arch = "amd64" - dir = "/workspace" + os = "linux" + arch = "amd64" + dir = "/workspace" } resource "coder_env" "welcome_message" { - agent_id = coder_agent.dev.id - name = "WELCOME_MESSAGE" - value = "Welcome to your Coder workspace!" + agent_id = coder_agent.dev.id + name = "WELCOME_MESSAGE" + value = "Welcome to your Coder workspace!" } resource "coder_env" "internal_api_url" { - agent_id = coder_agent.dev.id - name = "INTERNAL_API_URL" - value = "https://api.internal.company.com/v1" + agent_id = coder_agent.dev.id + name = "INTERNAL_API_URL" + value = "https://api.internal.company.com/v1" } ``` diff --git a/docs/resources/external_agent.md b/docs/resources/external_agent.md index b33f5b75..b3dfac4e 100644 --- a/docs/resources/external_agent.md +++ b/docs/resources/external_agent.md @@ -3,8 +3,8 @@ page_title: "coder_external_agent Resource - terraform-provider-coder" subcategory: "" description: |- - Define an external agent to be used in a workspace. - ~> Warning: External agents require a Premium https://coder.com/pricing Coder license. + Define an external agent to be used in a workspace. + ~> Warning: External agents require a Premium https://coder.com/pricing Coder license. --- # coder_external_agent (Resource) diff --git a/docs/resources/metadata.md b/docs/resources/metadata.md index d8329ea9..037f9766 100644 --- a/docs/resources/metadata.md +++ b/docs/resources/metadata.md @@ -3,8 +3,8 @@ page_title: "coder_metadata Resource - terraform-provider-coder" subcategory: "" description: |- - Use this resource to attach metadata to a resource. They will be displayed in the Coder dashboard alongside the resource. The resource containing the agent, and it's metadata, will be shown by default. - Alternatively, to attach metadata to the agent, use a metadata block within a coder_agent resource. + Use this resource to attach metadata to a resource. They will be displayed in the Coder dashboard alongside the resource. The resource containing the agent, and it's metadata, will be shown by default. + Alternatively, to attach metadata to the agent, use a metadata block within a coder_agent resource. --- # coder_metadata (Resource) @@ -20,40 +20,40 @@ data "coder_workspace" "me" { } resource "kubernetes_pod" "dev" { - count = data.coder_workspace.me.start_count - metadata { - name = "k8s_example" - namespace = "example" - } - spec { - # Draw the rest of the pod! - } + count = data.coder_workspace.me.start_count + metadata { + name = "k8s_example" + namespace = "example" + } + spec { + # Draw the rest of the pod! + } } resource "tls_private_key" "example_key_pair" { - algorithm = "ECDSA" - ecdsa_curve = "P256" + algorithm = "ECDSA" + ecdsa_curve = "P256" } resource "coder_metadata" "pod_info" { - count = data.coder_workspace.me.start_count - resource_id = kubernetes_pod.dev[0].id - # (Enterprise-only) this resource consumes 200 quota units - daily_cost = 200 - item { - key = "description" - value = "This description will show up in the Coder dashboard." - } - item { - key = "pod_uid" - value = kubernetes_pod.dev[0].uid - } - item { - key = "public_key" - value = tls_private_key.example_key_pair.public_key_openssh - # The value of this item will be hidden from view by default - sensitive = true - } + count = data.coder_workspace.me.start_count + resource_id = kubernetes_pod.dev[0].id + # (Enterprise-only) this resource consumes 200 quota units + daily_cost = 200 + item { + key = "description" + value = "This description will show up in the Coder dashboard." + } + item { + key = "pod_uid" + value = kubernetes_pod.dev[0].uid + } + item { + key = "public_key" + value = tls_private_key.example_key_pair.public_key_openssh + # The value of this item will be hidden from view by default + sensitive = true + } } ``` diff --git a/docs/resources/script.md b/docs/resources/script.md index 21bfaec9..a7236ea7 100644 --- a/docs/resources/script.md +++ b/docs/resources/script.md @@ -3,7 +3,7 @@ page_title: "coder_script Resource - terraform-provider-coder" subcategory: "" description: |- - Use this resource to run a script from an agent. When multiple scripts are assigned to the same agent, they are executed in parallel. + Use this resource to run a script from an agent. When multiple scripts are assigned to the same agent, they are executed in parallel. --- # coder_script (Resource) @@ -16,65 +16,65 @@ Use this resource to run a script from an agent. When multiple scripts are assig data "coder_workspace" "me" {} resource "coder_agent" "dev" { - os = "linux" - arch = "amd64" - dir = "/workspace" + os = "linux" + arch = "amd64" + dir = "/workspace" } resource "coder_script" "dotfiles" { - agent_id = coder_agent.dev.id - display_name = "Dotfiles" - icon = "/icon/dotfiles.svg" - run_on_start = true - script = templatefile("~/get_dotfiles.sh", { - DOTFILES_URI : var.dotfiles_uri, - DOTFILES_USER : var.dotfiles_user - }) + agent_id = coder_agent.dev.id + display_name = "Dotfiles" + icon = "/icon/dotfiles.svg" + run_on_start = true + script = templatefile("~/get_dotfiles.sh", { + DOTFILES_URI : var.dotfiles_uri, + DOTFILES_USER : var.dotfiles_user + }) } resource "coder_script" "code-server" { - agent_id = coder_agent.dev.id - display_name = "code-server" - icon = "/icon/code.svg" - run_on_start = true - start_blocks_login = true - script = templatefile("./install-code-server.sh", { - LOG_PATH : "/tmp/code-server.log" - }) + agent_id = coder_agent.dev.id + display_name = "code-server" + icon = "/icon/code.svg" + run_on_start = true + start_blocks_login = true + script = templatefile("./install-code-server.sh", { + LOG_PATH : "/tmp/code-server.log" + }) } resource "coder_script" "nightly_update" { - agent_id = coder_agent.dev.id - display_name = "Nightly update" - icon = "/icon/database.svg" - cron = "0 0 22 * * *" # Run at 22:00 (10 PM) every day - script = </tmp/pid.log 2>&1 & - EOF + agent_id = coder_agent.dev.id + display_name = "Stop daemon server" + run_on_stop = true + icon = "/icons/memory.svg" + script = </tmp/pid.log 2>&1 & + EOF } ``` diff --git a/provider/ai_task.go b/provider/ai_task.go index 01baf9ea..315ef0cc 100644 --- a/provider/ai_task.go +++ b/provider/ai_task.go @@ -28,7 +28,7 @@ func aiTaskResource() *schema.Resource { return &schema.Resource{ SchemaVersion: 1, - Description: "Use this resource to define Coder tasks.", + Description: "Use this resource to define Coder tasks.", CreateContext: func(c context.Context, resourceData *schema.ResourceData, i any) diag.Diagnostics { var diags diag.Diagnostics diff --git a/provider/app.go b/provider/app.go index dd0428a1..51d50fe7 100644 --- a/provider/app.go +++ b/provider/app.go @@ -247,7 +247,7 @@ func appResource() *schema.Resource { }, "hidden": { Type: schema.TypeBool, - Description: "Determines if the app is visible in the UI (minimum Coder version: v2.16).", + Description: "Determines if the app is visible in the UI.", Default: false, ForceNew: true, Optional: true, diff --git a/provider/devcontainer.go b/provider/devcontainer.go index 81a31194..d3b81017 100644 --- a/provider/devcontainer.go +++ b/provider/devcontainer.go @@ -13,7 +13,7 @@ func devcontainerResource() *schema.Resource { return &schema.Resource{ SchemaVersion: 1, - Description: "Define a Dev Container the agent should know of and attempt to autostart.\n\n-> This resource is only available in Coder v2.21 and later.", + Description: "Define a Dev Container the agent should know of and attempt to autostart. @since:v2.21.0", CreateContext: func(_ context.Context, rd *schema.ResourceData, _ interface{}) diag.Diagnostics { rd.SetId(uuid.NewString()) diff --git a/scripts/docsgen/main.go b/scripts/docsgen/main.go index 53b43ca4..6c597080 100644 --- a/scripts/docsgen/main.go +++ b/scripts/docsgen/main.go @@ -14,11 +14,22 @@ import ( "golang.org/x/xerrors" ) -// This script patches Markdown docs generated by `terraform-plugin-docs` to expose the original deprecation message. +// This script patches Markdown docs generated by `terraform-plugin-docs` to: +// 1. Expose the original deprecation message +// 2. Extract and format version information from resource/attribute descriptions +// 3. Add inline version markers for attributes with version requirements const docsDir = "docs" // FIXME expose as flag? -var reDeprecatedProperty = regexp.MustCompile("`([^`]+)` \\(([^,\\)]+), Deprecated\\) ([^\n]+)") +var ( + reDeprecatedProperty = regexp.MustCompile("`([^`]+)` \\(([^,\\)]+), Deprecated\\) ([^\n]+)") + // Pattern to extract version info from descriptions: @since:v2.16.0 + reVersionPattern = regexp.MustCompile(`@since:(v\d+\.\d+\.\d+)`) + // Pattern to find existing version info in descriptions (to clean up) + reExistingVersionInfo = regexp.MustCompile(`\s*\(@since:[^)]+\)|\s*\(minimum Coder version:[^)]+\)`) + // Pattern for version note marker (for idempotent updates) + reVersionNoteMarker = regexp.MustCompile(``) +) func main() { p := provider.New() @@ -26,6 +37,12 @@ func main() { if err != nil { log.Fatal(err) } + + // Process version information for all resources and data sources + err = processVersionInformation(p) + if err != nil { + log.Fatal(err) + } } func exposeDeprecationMessage(p *schema.Provider) error { @@ -66,6 +83,9 @@ func adjustDocFile(docPath string, schemas map[string]*schema.Schema) error { result := writeDeprecationMessage(doc, schemas) + // Clean @since markers from the final documentation + result = cleanVersionMarkers(result) + err = os.WriteFile(docPath, result, 0644) if err != nil { return xerrors.Errorf("can't write modified doc file: %w", err) @@ -87,3 +107,218 @@ func writeDeprecationMessage(doc []byte, schemas map[string]*schema.Schema) []by return bytes.Replace(m, []byte("Deprecated"), []byte(fmt.Sprintf("**Deprecated**: %s", sch.Deprecated)), 1) }) } + +// cleanVersionMarkers removes @since markers from the documentation +func cleanVersionMarkers(doc []byte) []byte { + // Remove @since:vX.Y.Z patterns from the documentation + result := reVersionPattern.ReplaceAll(doc, []byte("")) + // Clean up any double spaces that might be left after removing @since markers + result = regexp.MustCompile(` +`).ReplaceAll(result, []byte(" ")) + // Clean up trailing spaces at end of lines + result = regexp.MustCompile(` +$`).ReplaceAllFunc(result, func(m []byte) []byte { + return []byte{} + }) + return result +} + +func processVersionInformation(p *schema.Provider) error { + // Process data sources + for dataSourceName, dataSource := range p.DataSourcesMap { + docFile := filepath.Join(docsDir, "data-sources", strings.Replace(dataSourceName, "coder_", "", 1)+".md") + + // Extract version from description + version := extractVersionFromDescription(dataSource.Description) + if version != "" { + // Only add version note if explicitly specified + if err := addVersionToResourceDoc(docFile, dataSourceName, version, "data source"); err != nil { + return xerrors.Errorf("unable to add version to data-source doc file (data-source: %s): %w", dataSourceName, err) + } + } + + // Process attributes for version info + if err := processAttributeVersions(docFile, dataSource.Schema); err != nil { + return xerrors.Errorf("unable to process attribute versions for data-source (data-source: %s): %w", dataSourceName, err) + } + } + + // Process resources + for resourceName, resource := range p.ResourcesMap { + docFile := filepath.Join(docsDir, "resources", strings.Replace(resourceName, "coder_", "", 1)+".md") + + // Extract version from description + version := extractVersionFromDescription(resource.Description) + if version != "" { + // Only add version note if explicitly specified + if err := addVersionToResourceDoc(docFile, resourceName, version, "resource"); err != nil { + return xerrors.Errorf("unable to add version to resource doc file (resource: %s): %w", resourceName, err) + } + } + + // Process attributes for version info + if err := processAttributeVersions(docFile, resource.Schema); err != nil { + return xerrors.Errorf("unable to process attribute versions for resource (resource: %s): %w", resourceName, err) + } + } + + return nil +} + +func extractVersionFromDescription(description string) string { + matches := reVersionPattern.FindStringSubmatch(description) + if len(matches) > 1 { + return matches[1] + } + return "" +} + +func isValidVersion(version string) bool { + // Check if version matches vX.Y.Z format and is >= v2.0.0 + matches := regexp.MustCompile(`^v(\d+)\.(\d+)\.(\d+)$`).FindStringSubmatch(version) + if len(matches) < 2 { + return false + } + var major int + fmt.Sscanf(matches[1], "%d", &major) + if major < 2 { + log.Printf("warn: version %s is before baseline v2.0.0", version) + return false + } + return true +} + +func addVersionToResourceDoc(docPath string, resourceName string, version string, resourceType string) error { + // Validate version format + if !isValidVersion(version) { + log.Printf("warn: invalid version format %s for %s", version, resourceName) + return nil + } + + doc, err := os.ReadFile(docPath) + if err != nil { + return xerrors.Errorf("can't read the doc file: %w", err) + } + + docStr := string(doc) + + // Check if version note already exists with marker + existingMatches := reVersionNoteMarker.FindStringSubmatch(docStr) + if len(existingMatches) > 0 { + existingVersion := existingMatches[1] + if existingVersion == version { + // Same version, no update needed + return nil + } + // Different version - update the note + notePattern := regexp.MustCompile(`\n~> \*\*Note:\*\*[^\n]+\n`) + versionNote := fmt.Sprintf("\n~> **Note:** This %s requires [Coder %s](https://github.com/coder/coder/releases/tag/%s) or later.\n", + version, resourceType, version, version) + docStr = notePattern.ReplaceAllString(docStr, versionNote) + log.Printf("info: updated %s version note from %s to %s", resourceName, existingVersion, version) + return os.WriteFile(docPath, []byte(docStr), 0644) + } + + // Add version note after the description with marker + versionNote := fmt.Sprintf("\n\n\n~> **Note:** This %s requires [Coder %s](https://github.com/coder/coder/releases/tag/%s) or later.", + version, resourceType, version, version) + + // Find the end of frontmatter + frontmatterEnd := -1 + lines := strings.Split(docStr, "\n") + + if len(lines) > 0 && lines[0] == "---" { + for i := 1; i < len(lines); i++ { + if lines[i] == "---" { + frontmatterEnd = i + break + } + } + } + + // Find where to insert the version note + inserted := false + for i := frontmatterEnd + 1; i < len(lines); i++ { + // Skip empty lines and headings + if lines[i] == "" || strings.HasPrefix(lines[i], "#") { + continue + } + + // Insert after the first paragraph (non-empty, non-heading line) + lines[i] = lines[i] + versionNote + inserted = true + break + } + + if !inserted { + // If we couldn't find a good place, add it at the end + lines = append(lines, versionNote) + } + + docStr = strings.Join(lines, "\n") + + err = os.WriteFile(docPath, []byte(docStr), 0644) + if err != nil { + return xerrors.Errorf("can't write modified doc file: %w", err) + } + return nil +} + +func processAttributeVersions(docPath string, schemas map[string]*schema.Schema) error { + doc, err := os.ReadFile(docPath) + if err != nil { + return xerrors.Errorf("can't read the doc file: %w", err) + } + + docStr := string(doc) + modified := false + + // Process each attribute that has version information + for attrName, attrSchema := range schemas { + version := extractVersionFromDescription(attrSchema.Description) + if version != "" { + // Validate version format + if !isValidVersion(version) { + log.Printf("warn: invalid version format %s for attribute %s", version, attrName) + continue + } + + // Find the attribute in the documentation and add version marker + // Use anchored regex to match line structure precisely (prevents false matches) + attrPattern := regexp.MustCompile(fmt.Sprintf(`(?m)^(\s*)- \x60%s\x60 \(([^)]+)\) ([^\n]+)$`, regexp.QuoteMeta(attrName))) + + newDocStr := attrPattern.ReplaceAllStringFunc(docStr, func(match string) string { + // Check if version marker already exists for this version + if strings.Contains(match, "*(since "+version+")*") { + return match + } + // Check if it already has a different version marker + if strings.Contains(match, "*(since v") { + log.Printf("warn: attribute '%s' already has a version marker, skipping", attrName) + return match + } + // Clean version patterns from the description + cleanMatch := reVersionPattern.ReplaceAllString(match, "") + cleanMatch = reExistingVersionInfo.ReplaceAllString(cleanMatch, "") + // Add version marker at the end + result := strings.TrimRight(cleanMatch, " ") + fmt.Sprintf(" *(since %s)*", version) + log.Printf("info: added version marker to attribute '%s': %s", attrName, version) + return result + }) + + if newDocStr != docStr { + docStr = newDocStr + modified = true + } + } + } + + if modified { + // Clean any remaining @since markers from the final document + cleanedDoc := cleanVersionMarkers([]byte(docStr)) + err = os.WriteFile(docPath, cleanedDoc, 0644) + if err != nil { + return xerrors.Errorf("can't write modified doc file: %w", err) + } + } + + return nil +} diff --git a/templates/index.md.tmpl b/templates/index.md.tmpl index efdaae96..d34dc250 100644 --- a/templates/index.md.tmpl +++ b/templates/index.md.tmpl @@ -14,6 +14,12 @@ Terraform provider for managing Coder [templates](https://coder.com/docs/admin/t !> [`coder_git_auth`](https://registry.terraform.io/providers/coder/coder/1.0.4/docs/data-sources/git_auth) and owner related fields of [`coder_workspace`](https://registry.terraform.io/providers/coder/coder/1.0.4/docs/data-sources/workspace) data source have been removed. Follow the [Version 2 Upgrade Guide](https://registry.terraform.io/providers/coder/coder/latest/docs/guides/version-2-upgrade) to update your code. +## Version Compatibility + +This provider automatically documents version requirements for individual resources and features. + +~> **Note:** Individual resources may have higher version requirements. Check the documentation for each resource to see its specific minimum Coder version. + ## Example {{tffile "examples/provider/provider.tf"}}