Skip to content

fix: propagate templates.settings.env to gomplate datasources#2084

Merged
aknysh merged 6 commits intomainfrom
aknysh/fix-template-datasources-auth-1
Feb 17, 2026
Merged

fix: propagate templates.settings.env to gomplate datasources#2084
aknysh merged 6 commits intomainfrom
aknysh/fix-template-datasources-auth-1

Conversation

@aknysh
Copy link
Copy Markdown
Member

@aknysh aknysh commented Feb 17, 2026

what

  • Fixed three interconnected bugs that prevented templates.settings.env from propagating environment variables to gomplate datasources
  • mapstructure:"-" tag on TemplatesSettings.Env silently dropped the env field during the encode/decode/merge pipeline in ProcessTmplWithDatasources
  • Viper lowercased env var keys (e.g., AWS_PROFILEaws_profile) and caseSensitivePaths didn't include templates.settings.env
  • Stack manifest env vars were dropped at 4 caller decode sites in utils.go and describe_stacks.go
  • Added deferred env cleanup to restore original env values after template processing, preventing pollution across components
  • Updated dependency compatibility (gocloud.dev, terraform-exec) after go.mod update

why

  • Users configuring templates.settings.env with AWS credentials (e.g., AWS_PROFILE, AWS_REGION) expected gomplate datasources (S3, SSM, Secrets Manager) to use those credentials, but env vars were never set in the OS environment
  • The os.Setenv loop in ProcessTmplWithDatasources never executed because templateSettings.Env was always nil after mapstructure dropped it
  • Without env cleanup, env vars set for one component could leak into subsequent component processing

references

Summary by CodeRabbit

  • Bug Fixes

    • Fixed environment variable propagation in template datasources—vars from config and stack manifests are preserved during template processing.
    • Improved case sensitivity handling for template environment variables.
    • Removed an unnecessary debug log line.
  • New Features

    • Terraform workspace selection accepts optional parameters.
  • Documentation

    • Added docs describing datasource environment variable propagation and the fix.
  • Tests

    • Added integration and unit tests covering env propagation and case sensitivity.
  • Chores

    • Updated dependency licenses/versions and NOTICE entries.
    • Component describe outputs now include a new deprecated field placeholder.

aknysh and others added 2 commits February 16, 2026 23:06
- Update TerraformRunner interface for terraform-exec v0.25.0 (WorkspaceSelect
  now accepts variadic options)
- Downgrade gocloud.dev to v0.41.0 and go-fsimpl to v0.3.1 to maintain
  compatibility with gomplate/v3

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Three interconnected bugs prevented `templates.settings.env` from reaching
gomplate datasources: mapstructure:"-" tag silently dropped the Env field
during encode/decode/merge pipeline, Viper lowercased env var keys (e.g.,
AWS_PROFILE → aws_profile), and stack manifest env vars were dropped at
caller decode sites.

Fix extracts env from raw sources before mapstructure drops it, restores
env after merge, adds deferred cleanup to prevent env pollution across
components, and extends case-sensitive path support.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@aknysh aknysh added the patch A minor, backward compatible change label Feb 17, 2026
@aknysh aknysh requested a review from a team as a code owner February 17, 2026 05:49
@aknysh aknysh added the patch A minor, backward compatible change label Feb 17, 2026
@github-actions github-actions bot added the size/xl Extra large size PR label Feb 17, 2026
@aknysh aknysh self-assigned this Feb 17, 2026
@mergify
Copy link
Copy Markdown

mergify bot commented Feb 17, 2026

Warning

This PR exceeds the recommended limit of 1,000 lines.

Large PRs are difficult to review and may be rejected due to their size.

Please verify that this PR does not address multiple issues.
Consider refactoring it into smaller, more focused PRs to facilitate a smoother review process.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Feb 17, 2026

Dependency Review

The following issues were found:
  • ✅ 0 vulnerable package(s)
  • ✅ 0 package(s) with incompatible licenses
  • ✅ 0 package(s) with invalid SPDX license definitions
  • ⚠️ 10 package(s) with unknown licenses.
See the Details below.

License Issues

go.mod

PackageVersionLicenseIssue Type
github.com/go-git/go-git/v55.16.5NullUnknown License
github.com/grpc-ecosystem/grpc-gateway/v22.27.8NullUnknown License
github.com/hashicorp/terraform-config-inspect0.0.0-20260210152655-f4be3ba97d94NullUnknown License
github.com/hashicorp/terraform-json0.27.2NullUnknown License
github.com/huandu/go-sqlbuilder1.39.1NullUnknown License
github.com/klauspost/compress1.18.4NullUnknown License
golang.org/x/exp0.0.0-20260212183809-81e46e3db34aNullUnknown License
golang.org/x/term0.40.0NullUnknown License
k8s.io/client-go0.35.1NullUnknown License
github.com/kevinburke/ssh_config1.6.0NullUnknown License
Allowed Licenses: MIT, MIT-0, Apache-2.0, BSD-2-Clause, BSD-2-Clause-Views, BSD-3-Clause, ISC, MPL-2.0, 0BSD, Unlicense, CC0-1.0, CC-BY-3.0, CC-BY-4.0, CC-BY-SA-3.0, Python-2.0, OFL-1.1, LicenseRef-scancode-generic-cla, LicenseRef-scancode-unknown-license-reference, LicenseRef-scancode-unicode, LicenseRef-scancode-google-patent-license-golang

Scanned Files

  • go.mod

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Feb 17, 2026

📝 Walkthrough

Walkthrough

Adds extraction and restoration of template environment variables across config loading and template processing (including Gomplate datasources), extends case-sensitive key handling for templates.settings.env, updates dependency versions/licenses, adjusts Terraform WorkspaceSelect signature, and adds tests and fixtures for env propagation.

Changes

Cohort / File(s) Summary
Core env propagation
internal/exec/template_utils.go, internal/exec/utils.go, internal/exec/describe_stacks.go
New helpers to extract env from raw maps, set OS env with a restore function, and rehydrate settings.templates.settings.env after decoding; integrated into template processing paths (Terraform/Helmfile/Packer).
Tests & Integration
internal/exec/template_utils_env_test.go, internal/exec/template_env_integration_test.go
Comprehensive unit and integration tests for env extraction, restoration, case sensitivity, multi-pass evaluation, and template datasource access.
Config & Schema
pkg/config/load.go, pkg/schema/schema.go, pkg/schema/schema_test.go
Preserve and restore case for templates.settings.env via GetCaseSensitiveMap; added tests and log updates reflecting preserved keys.
Fixtures
tests/fixtures/scenarios/template-env-vars/*
New scenario (atmos.yaml, stack manifest, Terraform files) validating env propagation from Atmos config and stack-level overrides.
Terraform runner API & mocks
pkg/terraform/output/executor.go, pkg/terraform/output/mock_executor_test.go
WorkspaceSelect signature updated to accept variadic tfexec.WorkspaceSelectOption; mocks updated accordingly.
Snapshots & outputs
tests/snapshots/*
Updated test snapshots: added templates.settings.env to preserved-keys log, removed pager-failure debug line, removed a Terraform help entry, and many snapshots gained a new empty deprecated field.
Dependencies & NOTICE
go.mod, NOTICE
Bumped multiple dependency versions and refreshed license URL entries; added license blocks for go.uber.org/atomic.

Sequence Diagram

sequenceDiagram
    participant CLI as Atmos CLI
    participant Config as Config Loader
    participant TmplUtils as Template Utils
    participant Gomplate as Gomplate Engine
    participant OS as OS Environment

    CLI->>Config: LoadConfig(atmos.yaml + stack)
    Config->>TmplUtils: Provide raw templates.settings map
    TmplUtils->>TmplUtils: extractEnvFromRawMap(raw)
    TmplUtils->>TmplUtils: setEnvVarsWithRestore(extractedEnv)
    TmplUtils->>OS: Set env vars (via OS)
    TmplUtils->>Gomplate: Execute templates/datasources
    Gomplate->>OS: Read environment variables
    Gomplate-->>TmplUtils: Return processed templates
    TmplUtils->>TmplUtils: Restore original OS env (deferred cleanup)
    TmplUtils-->>Config: Return templated result (with env rehydrated)
    Config-->>CLI: Final configuration
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • osterman
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Title accurately describes the main fix: propagating templates.settings.env to gomplate datasources to restore credential access.
Linked Issues check ✅ Passed Changes fully address #2083: extract/restore env vars to bypass mapstructure, add case-sensitive path handling, and implement deferred cleanup for OS env vars.
Out of Scope Changes check ✅ Passed Snapshot updates reflect expected output changes from schema additions and case preservation. Dependency updates and pager setting align with gomplate datasource fixes.
Docstring Coverage ✅ Passed Docstring coverage is 95.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch aknysh/fix-template-datasources-auth-1

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Feb 17, 2026

📝 Walkthrough

Walkthrough

This PR fixes environment variable propagation from Atmos templates into gomplate datasources by preserving environment variables dropped by mapstructure during decoding. It introduces helper functions to extract and restore environment state across template processing, extends case-sensitive key handling for template settings, and updates dependency versions.

Changes

Cohort / File(s) Summary
Environment Variable Preservation
internal/exec/template_utils.go, internal/exec/describe_stacks.go, internal/exec/utils.go
Added extractEnvFromRawMap and setEnvVarsWithRestore helper functions to restore environment variables lost during mapstructure decode. Logic applied in three code paths (Terraform, Helmfile, Packer) before template processing, with restoration scheduled to prevent environment leakage across components.
Case-Sensitive Configuration
pkg/config/load.go, pkg/schema/schema.go, pkg/schema/schema_test.go
Extended GetCaseSensitiveMap to recognize templates.settings.env path and preserve original casing for environment variable keys. Added corresponding test cases validating env retrieval with and without CaseMaps.
Environment Testing
internal/exec/template_utils_env_test.go
New comprehensive test suite covering extractEnvFromRawMap behavior, setEnvVarsWithRestore functionality, ProcessTmplWithDatasources integration, non-polluting behavior across evaluations, and edge cases (nil maps, type variations, case sensitivity).
Interface Updates
pkg/terraform/output/executor.go, pkg/terraform/output/mock_executor_test.go
TerraformRunner.WorkspaceSelect now accepts optional variadic tfexec.WorkspaceSelectOption parameter; mock implementation updated accordingly.
Documentation
docs/fixes/2026-02-16-gomplate-datasource-env-vars.md
High-severity fix documentation detailing root causes (mapstructure decoding, Viper case-insensitivity, stack-manifest decoding), approach, code changes across multiple files, and test coverage.
Dependency Updates
go.mod, LICENSE
Version bumps for cloud.google.com/go/storage, aws-sdk-go-v2, charmbracelet, hashicorp tools, mikefarah/yq, redis, golang.org packages, google.golang.org packages, k8s.io packages; added go.uber.org/atomic MIT license entry.
Test Snapshots
tests/snapshots/TestCLICommands_Valid_Log_Level_in_Config_File.stderr.golden, tests/snapshots/TestCLICommands_atmos_--chdir_config_isolation.stdout.golden, tests/snapshots/TestCLICommands_atmos_describe_*.golden, tests/snapshots/TestCLICommands_describe_component_*.golden
Updated case-map key logging to include templates.settings.env, disabled pager output, removed debug pager errors, added deprecated field markers to component schema snapshots, minor whitespace adjustments.

Sequence Diagram

sequenceDiagram
    participant Config as Config Loader
    participant Viper as Viper (Case Handling)
    participant Templates as Template Processor
    participant Mapstructure as Mapstructure Decode
    participant OS as OS Environment
    participant Gomplate as Gomplate Datasource

    Config->>Config: Load raw settings map
    Config->>Viper: Apply case-sensitive preservation<br/>for templates.settings.env
    Templates->>Templates: Extract env from raw map<br/>(before decode)
    Templates->>Mapstructure: Decode settings
    Note over Mapstructure: Drops env field
    Templates->>Templates: Re-apply extracted env<br/>after decode
    Templates->>OS: setEnvVarsWithRestore<br/>(apply env vars)
    OS-->>Gomplate: Environment variables available
    Gomplate->>Gomplate: Access AWS credentials
    Templates->>OS: Cleanup & restore<br/>original env state
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Suggested reviewers

  • osterman
  • milldr
  • mcalhoun
🚥 Pre-merge checks | ✅ 5 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning Dependencies and mock updates support the env propagation fix. License and snapshot updates are secondary maintenance tasks. A terraform interface change (WorkspaceSelect) appears unrelated to gomplate datasource env propagation. Review the WorkspaceSelect interface change in executor.go and mock—clarify its relationship to the main fix or separate it into a follow-up PR.
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: fixing propagation of templates.settings.env to gomplate datasources, which directly addresses the core issue.
Linked Issues check ✅ Passed All code changes directly address issue #2083: environment variables are extracted before mapstructure drops them, case-sensitive handling restored for templates.settings.env, and OS env cleanup prevents cross-component leakage.
Docstring Coverage ✅ Passed Docstring coverage is 94.12% which is sufficient. The required threshold is 80.00%.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into main

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch aknysh/fix-template-datasources-auth-1

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (6)
docs/fixes/2026-02-16-gomplate-datasource-env-vars.md (1)

151-155: Add a language identifier to the fenced block.
This quiets MD040 while keeping the rendered output unchanged.

Suggested tweak
-```
+```text
 ok  github.com/cloudposse/atmos/internal/exec  1.207s
 ok  github.com/cloudposse/atmos/pkg/config     3.984s
 ok  github.com/cloudposse/atmos/pkg/schema     0.273s

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against the current code and only fix it if needed.

In @docs/fixes/2026-02-16-gomplate-datasource-env-vars.md around lines 151 -
155, The fenced code block showing the test output (the triple-backtick block
containing the three "ok github.com/..." lines) lacks a language identifier and
triggers MD040; update that block to use a labeled fence (e.g., text) so the markdown linter is satisfied while keeping the rendered output identical—modify the existing triple-backtick block in docs/fixes/2026-02-16-gomplate-datasource-env-vars.md to text at the start
and close with ``` at the end.


</details>

</blockquote></details>
<details>
<summary>internal/exec/template_utils.go (3)</summary><blockquote>

`362-386`: **Duplicated env-type-switch logic with `extractEnvFromRawMap`.**

The switch on `envRaw.(type)` at lines 366-375 mirrors the same pattern inside `extractEnvFromRawMap` (lines 121-132). Consider extracting a small `convertRawEnvToStringMap(envRaw any) map[string]string` helper to DRY this up.

<details>
<summary>Sketch</summary>

```go
func convertRawEnvToStringMap(envRaw any) map[string]string {
	result := make(map[string]string)
	switch env := envRaw.(type) {
	case map[string]any:
		for k, v := range env {
			if s, ok := v.(string); ok {
				result[k] = s
			}
		}
	case map[string]string:
		return env
	}
	if len(result) == 0 {
		return nil
	}
	return result
}
```

Then both `extractEnvFromRawMap` and the inline block here can delegate to it.

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@internal/exec/template_utils.go` around lines 362 - 386, The inline
type-switch that converts envRaw into resultEnv duplicates logic already in
extractEnvFromRawMap; create a small helper convertRawEnvToStringMap(envRaw any)
map[string]string (or similar) that performs the switch and returns nil on
empty, then replace the inline switch around
resultMapSettingsTemplatesSettings["env"] with a call to
convertRawEnvToStringMap and use its return to populate resultEnv before
decoding into templateSettings, and update extractEnvFromRawMap to delegate to
the new helper so both sites reuse the same conversion logic.
```

</details>

---

`147-177`: **`setEnvVarsWithRestore` — solid save/restore pattern.**

Two minor observations:

1. Lines 169 and 171: `os.Setenv` / `os.Unsetenv` errors in the cleanup closure are silently discarded. These rarely fail in practice, but if one does, the env ends up in a partially restored state with no signal. Consider at minimum logging a warning.

2. On error at line 161-163, the function returns `nil` for the cleanup function. Callers must nil-check before calling cleanup, or they'll panic. Currently the single call site (line 290-294) returns early on error so it never calls cleanup — but a future caller could trip on this. Returning a no-op `func(){}` instead of `nil` would be safer.

<details>
<summary>Suggested defensive return</summary>

```diff
 	for k, v := range envVars {
 		if err := os.Setenv(k, v); err != nil {
-			return nil, err
+			// Best-effort rollback of already-set vars.
+			for rk, orig := range saved {
+				if orig.existed {
+					os.Setenv(rk, orig.value)
+				} else {
+					os.Unsetenv(rk)
+				}
+			}
+			return func() {}, fmt.Errorf("setting env var %s: %w", k, err)
 		}
 	}
```

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@internal/exec/template_utils.go` around lines 147 - 177, The
setEnvVarsWithRestore function currently discards os.Setenv/os.Unsetenv errors
inside the cleanup closure and returns nil for the cleanup on a failed initial
Setenv; update the cleanup closure in setEnvVarsWithRestore to handle and at
minimum log warnings when os.Setenv or os.Unsetenv returns an error (referencing
the cleanup closure and saved map/savedEnvVar) so failures during restore are
visible, and change the function to always return a non-nil cleanup function
(e.g., a no-op func(){} when returning early on an os.Setenv error) instead of
nil so callers of setEnvVarsWithRestore can safely call the returned cleanup
without a nil-check.
```

</details>

---

`288-295`: **`defer cleanup()` inside the evaluation loop accumulates defers.**

Each loop iteration pushes a new deferred cleanup. With the typical 1–3 evaluations this is fine — LIFO ordering ensures correct restoration. Just be aware that if `evaluations` ever becomes large, this would stack many defers unnecessarily.

If you want to tighten this later, you could call `cleanup()` explicitly at the end of each iteration and only `defer` a final safety net, but the current approach is correct.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@internal/exec/template_utils.go` around lines 288 - 295, The loop currently
calls setEnvVarsWithRestore(templateSettings.Env) and uses defer cleanup() which
accumulates deferred calls per iteration; change this to call cleanup()
explicitly at the end of each iteration (instead of deferring) to avoid building
up many defers—i.e., replace defer cleanup() with a local call to cleanup() just
before the iteration continues/exits; if you want a safety net keep a single
top-level defer that runs only once outside the loop, but ensure
setEnvVarsWithRestore, cleanup(), and the evaluation loop are adjusted so each
iteration restores its env immediately.
```

</details>

</blockquote></details>
<details>
<summary>internal/exec/template_utils_env_test.go (2)</summary><blockquote>

`176-224`: **Prefer `t.Setenv` over bare `os.Unsetenv` for env var setup in tests.**

Lines 178, 229, 335, 416, 467 all use `os.Unsetenv` without registering cleanup with the test framework. If `TEST_GOMPLATE_AWS_PROFILE` (or the others) happened to be set in the ambient environment, the test would silently destroy that value with no restore on panic/failure.

A safer pattern:

```go
// Ensures the var is tracked by the test framework for restore,
// then immediately unset it for the test precondition.
t.Setenv("TEST_GOMPLATE_AWS_PROFILE", "")
os.Unsetenv("TEST_GOMPLATE_AWS_PROFILE")
```

This applies to all the `os.Unsetenv` calls at the top of `TestProcessTmplWithDatasources_*` functions. Based on learnings: "tests that manipulate environment variables should use testing.T.Setenv for automatic setup/teardown instead of os.Setenv/Unsetenv."

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@internal/exec/template_utils_env_test.go` around lines 176 - 224, Replace the
bare os.Unsetenv call at the top of
TestProcessTmplWithDatasources_EnvVarsFromConfig (and the other
TestProcessTmplWithDatasources_* tests mentioned) with a t.Setenv-backed pattern
so the test framework will restore any preexisting value on teardown;
specifically, call t.Setenv("TEST_GOMPLATE_AWS_PROFILE", "") to register the
variable with the test and then call os.Unsetenv("TEST_GOMPLATE_AWS_PROFILE") to
ensure the test precondition, leaving the rest of the test (e.g.,
ProcessTmplWithDatasources invocation and assertions) unchanged.
```

</details>

---

`136-172`: **`TestSetEnvVarsWithRestore` — good coverage of set-and-restore semantics.**

One small note: Line 142 uses `os.Unsetenv("TEST_NEW_VAR")` directly. Consider using the `t.Setenv` + `os.Unsetenv` pattern to ensure the test framework tracks the variable's original state, preventing leakage if the test panics before cleanup runs.

That said, this is acceptable per project conventions for test env manipulation. Based on learnings: "In test files for the atmos project, it's acceptable to ignore errors from os.Setenv/Unsetenv operations during test environment setup and teardown."

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@internal/exec/template_utils_env_test.go` around lines 136 - 172, The test
TestSetEnvVarsWithRestore currently calls os.Unsetenv("TEST_NEW_VAR") directly
which can bypass the test harness tracking; update the setup to use t.Setenv to
register the original state and then call os.Unsetenv (or rely on t.Setenv with
empty value) so the testing framework records/restores the environment properly
before calling setEnvVarsWithRestore; locate the test function
TestSetEnvVarsWithRestore and adjust the setup lines around the existing
t.Setenv("TEST_EXISTING_VAR", "original_value") and os.Unsetenv("TEST_NEW_VAR")
to use t.Setenv for the new variable (or pair t.Setenv with os.Unsetenv) to
avoid leaking env state if the test panics.
```

</details>

</blockquote></details>

</blockquote></details>

<details>
<summary>🤖 Prompt for all review comments with AI agents</summary>

Verify each finding against the current code and only fix it if needed.

Inline comments:
In @NOTICE:

  • Around line 24-27: The NOTICE file was modified manually and is out of sync
    with the generator; run the provided script ./scripts/generate-notice.sh to
    regenerate NOTICE and commit the generated output instead of editing NOTICE by
    hand, ensuring the updated NOTICE matches CI expectations (reference:
    ./scripts/generate-notice.sh and NOTICE).

In
@tests/snapshots/TestCLICommands_describe_component_provenance_advanced.stdout.golden:

  • Line 41: The snapshot file was edited manually; revert the manual changes to
    the golden (restore from VCS) and regenerate the snapshot by rerunning the test
    that owns this snapshot (the TestCLICommands test) with the snapshot
    regeneration flag (e.g., run the test command with -regenerate-snapshots as in
    the review comment) so the updated output for
    describe_component_provenance_advanced is produced automatically; ensure you run
    the specific test (TestCLICommands/test_name) to update tests/snapshots/**
    rather than editing the file by hand.

In
@tests/snapshots/TestCLICommands_describe_component_with_provenance_and_stack.stdout.golden:

  • Line 41: The snapshot file
    tests/snapshots/TestCLICommands_describe_component_with_provenance_and_stack.stdout.golden
    was modified manually; instead regenerate it by re-running the test that
    produces this golden (the
    TestCLICommands_describe_component_with_provenance_and_stack test) with the
    snapshot regeneration flag so the snapshot is updated automatically; run the
    test using the project’s test harness (e.g., go test ./tests -run
    'TestCLICommands/describ e_component_with_provenance_and_stack'
    -regenerate-snapshots or the equivalent command used in CI) and commit the
    resulting regenerated snapshot rather than editing the golden file by hand.

Nitpick comments:
In @docs/fixes/2026-02-16-gomplate-datasource-env-vars.md:

  • Around line 151-155: The fenced code block showing the test output (the
    triple-backtick block containing the three "ok github.com/..." lines) lacks a
    language identifier and triggers MD040; update that block to use a labeled fence
    (e.g., text) so the markdown linter is satisfied while keeping the rendered output identical—modify the existing triple-backtick block in docs/fixes/2026-02-16-gomplate-datasource-env-vars.md to text at the start
    and close with ``` at the end.

In @internal/exec/template_utils_env_test.go:

  • Around line 176-224: Replace the bare os.Unsetenv call at the top of
    TestProcessTmplWithDatasources_EnvVarsFromConfig (and the other
    TestProcessTmplWithDatasources_* tests mentioned) with a t.Setenv-backed pattern
    so the test framework will restore any preexisting value on teardown;
    specifically, call t.Setenv("TEST_GOMPLATE_AWS_PROFILE", "") to register the
    variable with the test and then call os.Unsetenv("TEST_GOMPLATE_AWS_PROFILE") to
    ensure the test precondition, leaving the rest of the test (e.g.,
    ProcessTmplWithDatasources invocation and assertions) unchanged.
  • Around line 136-172: The test TestSetEnvVarsWithRestore currently calls
    os.Unsetenv("TEST_NEW_VAR") directly which can bypass the test harness tracking;
    update the setup to use t.Setenv to register the original state and then call
    os.Unsetenv (or rely on t.Setenv with empty value) so the testing framework
    records/restores the environment properly before calling setEnvVarsWithRestore;
    locate the test function TestSetEnvVarsWithRestore and adjust the setup lines
    around the existing t.Setenv("TEST_EXISTING_VAR", "original_value") and
    os.Unsetenv("TEST_NEW_VAR") to use t.Setenv for the new variable (or pair
    t.Setenv with os.Unsetenv) to avoid leaking env state if the test panics.

In @internal/exec/template_utils.go:

  • Around line 362-386: The inline type-switch that converts envRaw into
    resultEnv duplicates logic already in extractEnvFromRawMap; create a small
    helper convertRawEnvToStringMap(envRaw any) map[string]string (or similar) that
    performs the switch and returns nil on empty, then replace the inline switch
    around resultMapSettingsTemplatesSettings["env"] with a call to
    convertRawEnvToStringMap and use its return to populate resultEnv before
    decoding into templateSettings, and update extractEnvFromRawMap to delegate to
    the new helper so both sites reuse the same conversion logic.
  • Around line 147-177: The setEnvVarsWithRestore function currently discards
    os.Setenv/os.Unsetenv errors inside the cleanup closure and returns nil for the
    cleanup on a failed initial Setenv; update the cleanup closure in
    setEnvVarsWithRestore to handle and at minimum log warnings when os.Setenv or
    os.Unsetenv returns an error (referencing the cleanup closure and saved
    map/savedEnvVar) so failures during restore are visible, and change the function
    to always return a non-nil cleanup function (e.g., a no-op func(){} when
    returning early on an os.Setenv error) instead of nil so callers of
    setEnvVarsWithRestore can safely call the returned cleanup without a nil-check.
  • Around line 288-295: The loop currently calls
    setEnvVarsWithRestore(templateSettings.Env) and uses defer cleanup() which
    accumulates deferred calls per iteration; change this to call cleanup()
    explicitly at the end of each iteration (instead of deferring) to avoid building
    up many defers—i.e., replace defer cleanup() with a local call to cleanup() just
    before the iteration continues/exits; if you want a safety net keep a single
    top-level defer that runs only once outside the loop, but ensure
    setEnvVarsWithRestore, cleanup(), and the evaluation loop are adjusted so each
    iteration restores its env immediately.

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

@codecov
Copy link
Copy Markdown

codecov bot commented Feb 17, 2026

Codecov Report

❌ Patch coverage is 82.50000% with 14 lines in your changes missing coverage. Please review.
✅ Project coverage is 76.22%. Comparing base (6e88571) to head (e256102).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
internal/exec/template_utils.go 88.23% 4 Missing and 4 partials ⚠️
internal/exec/describe_stacks.go 33.33% 2 Missing and 2 partials ⚠️
internal/exec/utils.go 0.00% 1 Missing and 1 partial ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #2084      +/-   ##
==========================================
+ Coverage   76.19%   76.22%   +0.03%     
==========================================
  Files         824      824              
  Lines       77546    77622      +76     
==========================================
+ Hits        59085    59167      +82     
+ Misses      14760    14754       -6     
  Partials     3701     3701              
Flag Coverage Δ
unittests 76.22% <82.50%> (+0.03%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
pkg/config/load.go 80.99% <100.00%> (+0.38%) ⬆️
pkg/schema/schema.go 87.70% <100.00%> (+0.20%) ⬆️
pkg/terraform/output/executor.go 91.79% <ø> (ø)
internal/exec/utils.go 82.22% <0.00%> (-0.28%) ⬇️
internal/exec/describe_stacks.go 73.17% <33.33%> (-0.37%) ⬇️
internal/exec/template_utils.go 70.83% <88.23%> (+7.33%) ⬆️

... and 4 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

coderabbitai[bot]
coderabbitai bot previously approved these changes Feb 17, 2026
Add integration tests exercising the full ExecuteDescribeStacks and
InitCliConfig paths with templates.settings.env configured. This covers
the env extraction at describe_stacks.go caller sites, the load.go case
restoration, and verifies case-sensitive key preservation through Viper.

Uses filepath.Join for cross-platform path handling in test fixtures.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (5)
pkg/schema/schema_test.go (1)

184-221: Prefer a table-driven structure for the new scenarios.

These two subtests are better expressed as a single table-driven case list for consistency.

As per coding guidelines: “Use table-driven tests for testing multiple scenarios in Go”.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/schema/schema_test.go` around lines 184 - 221, Combine the two t.Run
blocks into a single table-driven test that iterates over cases (with fields
like name, CaseMaps cm, AtmosConfiguration templates.Settings.Env input, and
expected map) and within the loop call t.Run(tc.name, func(t *testing.T) {
result := atmosConfig.GetCaseSensitiveMap("templates.settings.env");
assert.Equal(t, tc.expected, result) }); locate where AtmosConfiguration,
Templates, TemplatesSettings, GetCaseSensitiveMap and casemap.New are used in
pkg/schema/schema_test.go and replace the two explicit subtests with this
table-driven structure to cover both the no-CaseMaps and case-restoration
scenarios.
internal/exec/template_utils.go (2)

362-386: Inline env extraction duplicates extractEnvFromRawMap logic.

The type-switch extraction here (lines 364-376) mirrors extractEnvFromRawMap but operates on a slightly different input shape. Consider extracting a shared helper that takes the raw env value directly, to reduce duplication.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/exec/template_utils.go` around lines 362 - 386, The env extraction
in the block that inspects resultMapSettingsTemplatesSettings duplicates logic
in extractEnvFromRawMap; refactor by adding or reusing a helper that accepts the
raw env value (the envRaw extracted from resultMapSettingsTemplatesSettings) and
returns a map[string]string, then replace the type-switch with a call to that
helper, assign the returned map to resultEnv, and preserve the existing
mapstructure.Decode(templateSettings) and the subsequent restore to
templateSettings.Env so behavior remains identical.

288-295: defer cleanup() inside the evaluation loop accumulates deferred calls.

Each loop iteration appends a new deferred cleanup. With evaluations > 1, this creates N cleanup functions when only 1 is needed. It works correctly due to LIFO ordering (the first cleanup, which saved the true originals, runs last), but it's wasteful.

Consider moving the env setup before the loop, or guarding with a sync.Once-style flag so it's only set once.

♻️ Suggested: move env setup before the loop
+	// Set environment variables for template processing and restore originals when done.
+	if len(templateSettings.Env) > 0 {
+		cleanup, envErr := setEnvVarsWithRestore(templateSettings.Env)
+		if envErr != nil {
+			return "", envErr
+		}
+		defer cleanup()
+	}
+
 	for i := 0; i < evaluations; i++ {
 		log.Trace("ProcessTmplWithDatasources", logKeyTemplate, tmplName, "evaluation", i+1)
 		// ...
-		// Set environment variables for template processing and restore originals when done.
-		if len(templateSettings.Env) > 0 {
-			cleanup, envErr := setEnvVarsWithRestore(templateSettings.Env)
-			if envErr != nil {
-				return "", envErr
-			}
-			defer cleanup()
-		}

Note: if templateSettings.Env can change mid-loop (via the re-decode at lines 362-386), you'd need to call setEnvVarsWithRestore again inside the loop only when env actually changes.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/exec/template_utils.go` around lines 288 - 295, The code calls
setEnvVarsWithRestore(templateSettings.Env) with defer cleanup() inside the
evaluation loop, which accumulates deferred cleanups; move the env setup so it's
executed only once per call: invoke setEnvVarsWithRestore before entering the
evaluations loop (or protect it with a sync.Once/flag) and defer the single
cleanup after that setup; if templateSettings.Env may be re-decoded/changed
later in the loop, detect the change and call setEnvVarsWithRestore/cleanup only
when Env differs (replacing the previous env and running its cleanup before
setting the new one). Ensure you update references to cleanup,
setEnvVarsWithRestore, and templateSettings.Env accordingly.
internal/exec/template_utils_env_test.go (1)

176-224: os.Unsetenv calls in ProcessTmplWithDatasources tests.

Lines 178, 229, 335, 416, 467 all use os.Unsetenv for setup. Same recommendation as the integration test file — consider t.Setenv("VAR", "") + os.Unsetenv("VAR") for automatic restore on panic. Low risk since these are test-only vars.

Based on learnings: "In the cloudposse/atmos codebase, tests should prefer t.Setenv for environment variable setup/teardown instead of os.Setenv/Unsetenv to ensure test-scoped isolation."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/exec/template_utils_env_test.go` around lines 176 - 224, Replace the
manual os.Unsetenv setup/teardown in
TestProcessTmplWithDatasources_EnvVarsFromConfig (and the other
ProcessTmplWithDatasources tests referenced) with testing.T's scoped helper to
ensure automatic restore: call t.Setenv("TEST_GOMPLATE_AWS_PROFILE", "") at the
start to ensure the var is empty for the test and remove the manual os.Unsetenv
cleanup; keep using ProcessTmplWithDatasources as before but rely on t.Setenv
for isolation and remove/assert adjustments that expect manual restoration.
internal/exec/template_env_integration_test.go (1)

21-22: Prefer t.Setenv for env cleanup safety.

Lines 21-22 use os.Unsetenv directly. If these vars happen to exist in the parent env and the test panics, they won't be restored. Using t.Setenv("TEST_TMPL_ENV_VAR", "") followed by os.Unsetenv(...) ensures automatic restore on test cleanup.

That said, since these are test-specific vars unlikely to exist, the practical risk is low.

Based on learnings: "In the cloudposse/atmos repo, tests that manipulate environment variables should use testing.T.Setenv for automatic setup/teardown instead of os.Setenv/Unsetenv."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/exec/template_env_integration_test.go` around lines 21 - 22, Replace
direct calls to os.Unsetenv("TEST_TMPL_ENV_VAR") and
os.Unsetenv("TEST_TMPL_STACK_VAR") in the test with testing.T's Setenv so the
original environment is restored on test cleanup; locate the test function in
internal/exec/template_env_integration_test.go (where t is the *testing.T used)
and call t.Setenv("TEST_TMPL_ENV_VAR", "") and t.Setenv("TEST_TMPL_STACK_VAR",
"") instead of Unsetenv to ensure safe setup/teardown.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/fixes/2026-02-16-gomplate-datasource-env-vars.md`:
- Around line 151-155: The fenced code block currently has no language tag (it
starts with ``` and contains the "ok  github.com/cloudposse/..." lines); update
the fence to include a language such as text or console by changing the opening
``` to ```text (or ```console) so the block becomes ```text followed by the
three "ok ..." lines and the closing ``` to satisfy markdownlint.

---

Duplicate comments:
In `@NOTICE`:
- Around line 24-27: The NOTICE file was modified by hand; revert manual edits
and regenerate NOTICE using the repository's NOTICE generator script so
CI/license tracking remains consistent; locate the NOTICE file and run the
project's NOTICE generation tool (the generator script used in this repo) to
produce the correct content rather than editing NOTICE directly.

In
`@tests/snapshots/TestCLICommands_describe_component_provenance_advanced.stdout.golden`:
- Line 41: The snapshot was manually edited; regenerate it instead of touching
the golden file: run the test case
TestCLICommands/describe_component_provenance_advanced with the
-regenerate-snapshots flag (e.g. go test ./tests -run
'TestCLICommands/describe_component_provenance_advanced' -regenerate-snapshots)
to recreate
tests/snapshots/TestCLICommands_describe_component_provenance_advanced.stdout.golden
and revert your manual change to restore authoritative snapshot handling.

In
`@tests/snapshots/TestCLICommands_describe_component_with_provenance_and_stack.stdout.golden`:
- Line 41: The snapshot was edited manually; instead regenerate it by running
the test suite for the failing test
(TestCLICommands_describe_component_with_provenance_and_stack) with the
-regenerate-snapshots flag so the golden file is updated automatically (e.g.,
run the go test command shown in the review with -regenerate-snapshots) and
commit the regenerated snapshot instead of making manual edits.

---

Nitpick comments:
In `@internal/exec/template_env_integration_test.go`:
- Around line 21-22: Replace direct calls to os.Unsetenv("TEST_TMPL_ENV_VAR")
and os.Unsetenv("TEST_TMPL_STACK_VAR") in the test with testing.T's Setenv so
the original environment is restored on test cleanup; locate the test function
in internal/exec/template_env_integration_test.go (where t is the *testing.T
used) and call t.Setenv("TEST_TMPL_ENV_VAR", "") and
t.Setenv("TEST_TMPL_STACK_VAR", "") instead of Unsetenv to ensure safe
setup/teardown.

In `@internal/exec/template_utils_env_test.go`:
- Around line 176-224: Replace the manual os.Unsetenv setup/teardown in
TestProcessTmplWithDatasources_EnvVarsFromConfig (and the other
ProcessTmplWithDatasources tests referenced) with testing.T's scoped helper to
ensure automatic restore: call t.Setenv("TEST_GOMPLATE_AWS_PROFILE", "") at the
start to ensure the var is empty for the test and remove the manual os.Unsetenv
cleanup; keep using ProcessTmplWithDatasources as before but rely on t.Setenv
for isolation and remove/assert adjustments that expect manual restoration.

In `@internal/exec/template_utils.go`:
- Around line 362-386: The env extraction in the block that inspects
resultMapSettingsTemplatesSettings duplicates logic in extractEnvFromRawMap;
refactor by adding or reusing a helper that accepts the raw env value (the
envRaw extracted from resultMapSettingsTemplatesSettings) and returns a
map[string]string, then replace the type-switch with a call to that helper,
assign the returned map to resultEnv, and preserve the existing
mapstructure.Decode(templateSettings) and the subsequent restore to
templateSettings.Env so behavior remains identical.
- Around line 288-295: The code calls
setEnvVarsWithRestore(templateSettings.Env) with defer cleanup() inside the
evaluation loop, which accumulates deferred cleanups; move the env setup so it's
executed only once per call: invoke setEnvVarsWithRestore before entering the
evaluations loop (or protect it with a sync.Once/flag) and defer the single
cleanup after that setup; if templateSettings.Env may be re-decoded/changed
later in the loop, detect the change and call setEnvVarsWithRestore/cleanup only
when Env differs (replacing the previous env and running its cleanup before
setting the new one). Ensure you update references to cleanup,
setEnvVarsWithRestore, and templateSettings.Env accordingly.

In `@pkg/schema/schema_test.go`:
- Around line 184-221: Combine the two t.Run blocks into a single table-driven
test that iterates over cases (with fields like name, CaseMaps cm,
AtmosConfiguration templates.Settings.Env input, and expected map) and within
the loop call t.Run(tc.name, func(t *testing.T) { result :=
atmosConfig.GetCaseSensitiveMap("templates.settings.env"); assert.Equal(t,
tc.expected, result) }); locate where AtmosConfiguration, Templates,
TemplatesSettings, GetCaseSensitiveMap and casemap.New are used in
pkg/schema/schema_test.go and replace the two explicit subtests with this
table-driven structure to cover both the no-CaseMaps and case-restoration
scenarios.

)

- Convert schema_test.go subtests to table-driven structure
- Extract convertRawEnvToStringMap shared helper to reduce duplication
- Move env setup before evaluation loop to avoid accumulating deferred cleanups
- Use t.Setenv for automatic restore on test cleanup in all env tests
- Add language identifier to fenced code block in fix doc

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
osterman
osterman previously approved these changes Feb 17, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (3)
internal/exec/template_utils_env_test.go (2)

416-511: Evaluation loop and case-sensitivity tests round out the coverage nicely. The eval loop test with Evaluations: 2 and embedded settings.templates.settings.env in the template output validates the re-extraction path in the inner loop. The case-sensitivity test directly targets Bug 2 (Viper lowercasing).

One note: many inline comments across this file lack trailing periods (e.g., lines 138, 141, 152, 156, etc.). The coding guidelines enforce godot — ending comments with periods. Low priority for a test file, but worth a pass.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/exec/template_utils_env_test.go` around lines 416 - 511, Update
inline comments in internal/exec/template_utils_env_test.go to follow the godot
rule by ensuring every sentence-style comment ends with a trailing period;
specifically audit comments inside and around the
TestProcessTmplWithDatasources_EnvVarsInEvaluationLoop and
TestProcessTmplWithDatasources_EnvVarsCaseSensitive tests (and other inline
comments earlier in the file) and add a period to any that are missing to
satisfy the coding guidelines.

136-172: Solid coverage of the set-and-restore cycle. The t.Setenv + os.Unsetenv pattern correctly registers cleanup while starting from a clean state. One minor note: TEST_NEW_VAR on line 142 isn't registered with t.Setenv, so if the test panics before cleanup() runs, it could leak. Consider adding t.Setenv for it too.

Suggested safeguard
-		// Ensure a var that doesn't exist.
-		os.Unsetenv("TEST_NEW_VAR")
+		// Ensure a var that doesn't exist (register for cleanup first).
+		t.Setenv("TEST_NEW_VAR", "")
+		os.Unsetenv("TEST_NEW_VAR")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/exec/template_utils_env_test.go` around lines 136 - 172, The test
may leak an environment variable if it panics before cleanup because
TEST_NEW_VAR isn't registered with t.Setenv; to fix, register TEST_NEW_VAR with
t.Setenv before removing it so the testing framework will restore previous state
on test exit (i.e., call t.Setenv("TEST_NEW_VAR", "") prior to
os.Unsetenv("TEST_NEW_VAR")), keeping the rest of TestSetEnvVarsWithRestore and
the setEnvVarsWithRestore usage unchanged.
internal/exec/template_utils.go (1)

160-188: Partial env set on error leaves vars unrestored.

If os.Setenv fails partway through the second loop (line 172), previously-set vars from earlier iterations won't be rolled back since nil is returned as the cleanup function.

Practically, os.Setenv rarely fails (empty key on Windows), so this is low risk. Still, returning the cleanup for already-set vars would be more robust.

Suggested hardening
 	for k, v := range envVars {
 		if err := os.Setenv(k, v); err != nil {
-			return nil, err
+			// Return cleanup for vars already set before the failure.
+			cleanup := func() {
+				for key, original := range saved {
+					if original.existed {
+						os.Setenv(key, original.value)
+					} else {
+						os.Unsetenv(key)
+					}
+				}
+			}
+			return cleanup, err
 		}
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/exec/template_utils.go` around lines 160 - 188, The function
setEnvVarsWithRestore currently returns nil cleanup if os.Setenv fails partway,
leaving earlier-set vars unrestored; modify it so a cleanup function is always
returned even on error: either construct the cleanup before the second loop
(using the saved map) or, upon encountering an os.Setenv error inside the loop
in setEnvVarsWithRestore, immediately build and return a cleanup closure that
restores/unsets only the vars that were set up to that point along with the
error; ensure references to the saved map and the keys set so far (the loop
variables in setEnvVarsWithRestore) are used to perform the rollback and return
(cleanup, err) instead of (nil, err).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/fixes/2026-02-16-gomplate-datasource-env-vars.md`:
- Line 98: The function signature for setEnvVarsWithRestore has a spacing typo
in its return type; change the declared return from "func ()" to "func()" so the
signature reads setEnvVarsWithRestore(envVars map[string]string) (func(), error)
— update the declaration of setEnvVarsWithRestore accordingly.
- Around line 103-108: Add a trailing period to the last bullet in section 3 of
the docs so the sentence ending "Also extracts env from the re-decoded template
settings within the evaluation loop" becomes a complete sentence; locate the
bullet that references templateSettings.Env, atmosConfig.Templates.Settings.Env,
settingsSection.Templates.Settings.Env and setEnvVarsWithRestore and append a
period to the end.

In `@internal/exec/template_utils.go`:
- Around line 374-389: The evaluation loop currently os.Setenv()'s keys from
resultEnv (via convertRawEnvToStringMap) but those new keys aren't included in
the deferred cleanup from setEnvVarsWithRestore/cleanup(), causing an env leak;
modify the code around templateSettings.Env assignment (the block that decodes
into templateSettings and then sets templateSettings.Env and os.Setenv) to track
which keys in resultEnv were newly introduced (not present in the original
templateSettings.Env or the restore snapshot returned by setEnvVarsWithRestore),
and extend the cleanup behavior to either restore previous values for
overwritten keys and remove keys that were newly added; use the same restoration
mechanism used by setEnvVarsWithRestore (or return an augmented cleanup closure)
so the deferred cleanup removes any extra keys introduced in the loop and
restores overwritten values.

---

Nitpick comments:
In `@internal/exec/template_utils_env_test.go`:
- Around line 416-511: Update inline comments in
internal/exec/template_utils_env_test.go to follow the godot rule by ensuring
every sentence-style comment ends with a trailing period; specifically audit
comments inside and around the
TestProcessTmplWithDatasources_EnvVarsInEvaluationLoop and
TestProcessTmplWithDatasources_EnvVarsCaseSensitive tests (and other inline
comments earlier in the file) and add a period to any that are missing to
satisfy the coding guidelines.
- Around line 136-172: The test may leak an environment variable if it panics
before cleanup because TEST_NEW_VAR isn't registered with t.Setenv; to fix,
register TEST_NEW_VAR with t.Setenv before removing it so the testing framework
will restore previous state on test exit (i.e., call t.Setenv("TEST_NEW_VAR",
"") prior to os.Unsetenv("TEST_NEW_VAR")), keeping the rest of
TestSetEnvVarsWithRestore and the setEnvVarsWithRestore usage unchanged.

In `@internal/exec/template_utils.go`:
- Around line 160-188: The function setEnvVarsWithRestore currently returns nil
cleanup if os.Setenv fails partway, leaving earlier-set vars unrestored; modify
it so a cleanup function is always returned even on error: either construct the
cleanup before the second loop (using the saved map) or, upon encountering an
os.Setenv error inside the loop in setEnvVarsWithRestore, immediately build and
return a cleanup closure that restores/unsets only the vars that were set up to
that point along with the error; ensure references to the saved map and the keys
set so far (the loop variables in setEnvVarsWithRestore) are used to perform the
rollback and return (cleanup, err) instead of (nil, err).

- Return cleanup function on partial os.Setenv failure in setEnvVarsWithRestore
- Track and clean up extra env keys introduced by evaluation loop
- Register TEST_NEW_VAR with t.Setenv for panic safety
- Fix typos in fix doc (func() spacing, trailing period)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@aknysh aknysh merged commit 631cc03 into main Feb 17, 2026
61 of 63 checks passed
@aknysh aknysh deleted the aknysh/fix-template-datasources-auth-1 branch February 17, 2026 17:00
@github-actions
Copy link
Copy Markdown

These changes were released in v1.206.3-rc.1.

@github-actions
Copy link
Copy Markdown

These changes were released in v1.207.0.

@github-actions
Copy link
Copy Markdown

These changes were released in v1.208.0-rc.0.

@github-actions
Copy link
Copy Markdown

These changes were released in v1.208.0-test.15.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

patch A minor, backward compatible change size/xl Extra large size PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Gomplate datasources cannot access AWS credentials from template environment variables or Atmos auth

2 participants