feat: add merge_strategy support for coder_env resources (#23107)

## Description

Implements the server-side merge logic for the `merge_strategy`
attribute added to `coder_env` in [terraform-provider-coder
v2.15.0](https://github.com/coder/terraform-provider-coder/pull/489).
This allows template authors to control how duplicate environment
variable names are combined across multiple `coder_env` resources.

Relates to https://github.com/coder/coder/issues/21885

## Supported strategies

| Strategy | Behavior |
|----------|----------|
| `replace` (default) | Last value wins — backward compatible |
| `append` | Joins values with `:` separator (e.g. PATH additions) |
| `prepend` | Prepends value with `:` separator |
| `error` | Fails the build if the variable is already defined |

## Example

```hcl
resource "coder_env" "path_tools" {
  agent_id       = coder_agent.dev.id
  name           = "PATH"
  value          = "/home/coder/tools/bin"
  merge_strategy = "append"
}
```

## Changes

- **Proto**: Added `merge_strategy` field to `Env` message in
`provisioner.proto`
- **State reader**: Updated `agentEnvAttributes` struct and proto
construction in `resources.go`
- **Merge logic**: Added `mergeExtraEnvs()` function in
`provisionerdserver.go` with strategy-aware merging for both agent envs
and devcontainer subagent envs
- **Tests**: 15 unit tests covering all strategies, edge cases (empty
values, mixed strategies, multiple appends)
- **Dependency**: Bumped `terraform-provider-coder` v2.14.0 → v2.15.0
- **Fixtures**: Updated `duplicate-env-keys` test fixtures and golden
files

## Ordering

When multiple resources `append` or `prepend` to the same key, they are
processed in alphabetical order by Terraform resource address (per the
determinism fix in #22706).
This commit is contained in:
Kacper Sawicki
2026-03-18 15:43:28 +01:00
committed by GitHub
parent 84de391f26
commit 1e07ec49a6
18 changed files with 912 additions and 530 deletions
+7 -5
View File
@@ -118,9 +118,10 @@ type agentAppAttributes struct {
}
type agentEnvAttributes struct {
AgentID string `mapstructure:"agent_id"`
Name string `mapstructure:"name"`
Value string `mapstructure:"value"`
AgentID string `mapstructure:"agent_id"`
Name string `mapstructure:"name"`
Value string `mapstructure:"value"`
MergeStrategy string `mapstructure:"merge_strategy"`
}
type agentScriptAttributes struct {
@@ -648,8 +649,9 @@ func ConvertState(ctx context.Context, modules []*tfjson.StateModule, rawGraph s
}
env := &proto.Env{
Name: attrs.Name,
Value: attrs.Value,
Name: attrs.Name,
Value: attrs.Value,
MergeStrategy: attrs.MergeStrategy,
}
envAgentLoop:
+6 -4
View File
@@ -382,12 +382,14 @@ func TestConvertResources(t *testing.T) {
Architecture: "amd64",
ExtraEnvs: []*proto.Env{
{
Name: "PATH",
Value: "/a/bin",
Name: "PATH",
Value: "/a/bin",
MergeStrategy: "append",
},
{
Name: "PATH",
Value: "/b/bin",
Name: "PATH",
Value: "/b/bin",
MergeStrategy: "append",
},
{
Name: "UNIQUE",
@@ -21,11 +21,13 @@
"extra_envs": [
{
"name": "PATH",
"value": "/a/bin"
"value": "/a/bin",
"merge_strategy": "append"
},
{
"name": "PATH",
"value": "/b/bin"
"value": "/b/bin",
"merge_strategy": "append"
},
{
"name": "UNIQUE",
@@ -22,11 +22,13 @@
"extra_envs": [
{
"name": "PATH",
"value": "/a/bin"
"value": "/a/bin",
"merge_strategy": "append"
},
{
"name": "PATH",
"value": "/b/bin"
"value": "/b/bin",
"merge_strategy": "append"
},
{
"name": "UNIQUE",
@@ -44,7 +44,8 @@
"schema_version": 1,
"values": {
"name": "PATH",
"value": "/a/bin"
"value": "/a/bin",
"merge_strategy": "append"
},
"sensitive_values": {}
},
@@ -57,7 +58,8 @@
"schema_version": 1,
"values": {
"name": "PATH",
"value": "/b/bin"
"value": "/b/bin",
"merge_strategy": "append"
},
"sensitive_values": {}
},
@@ -60,7 +60,8 @@
"agent_id": "aaaaaaaa-1111-2222-3333-444444444444",
"id": "bbbbbbbb-1111-2222-3333-444444444444",
"name": "PATH",
"value": "/a/bin"
"value": "/a/bin",
"merge_strategy": "append"
},
"sensitive_values": {},
"depends_on": [
@@ -78,7 +79,8 @@
"agent_id": "aaaaaaaa-1111-2222-3333-444444444444",
"id": "cccccccc-1111-2222-3333-444444444444",
"name": "PATH",
"value": "/b/bin"
"value": "/b/bin",
"merge_strategy": "append"
},
"sensitive_values": {},
"depends_on": [