Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 11 additions & 9 deletions cfgx.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,14 +100,22 @@ func GenerateFromFile(opts *GenerateOptions) error {
return fmt.Errorf("failed to read input file %s: %w", opts.InputFile, err)
}

// Parse TOML to apply environment variable overrides if enabled
// Set default mode if not specified
mode := opts.Mode
if mode == "" {
mode = "static"
}
Comment on lines +103 to +107
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

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

GenerateFromFile’s behavior (including whether env overrides are applied) depends on the exact opts.Mode string matching "getter". Consider normalizing/validating opts.Mode here (e.g., trim + strings.ToLower and reject unknown values) so typos/casing differences don’t accidentally fall back to static-mode behavior and reintroduce secret-baking during generation.

Copilot uses AI. Check for mistakes.

// Parse TOML to apply environment variable overrides if enabled.
// In getter mode, env vars are resolved at runtime via os.Getenv() calls
// in the generated code, so applying them at generation time would
// incorrectly bake runtime values (e.g. secrets) into the source as defaults.
var configData map[string]any
if err := toml.Unmarshal(data, &configData); err != nil {
return fmt.Errorf("failed to parse TOML: %w", err)
}

// Apply environment variable overrides
if opts.EnableEnv {
if opts.EnableEnv && mode != "getter" {
if err := envoverride.Apply(configData); err != nil {
return fmt.Errorf("failed to apply environment overrides: %w", err)
}
Expand Down Expand Up @@ -137,12 +145,6 @@ func GenerateFromFile(opts *GenerateOptions) error {
maxFileSize = DefaultMaxFileSize
}

// Set default mode if not specified
mode := opts.Mode
if mode == "" {
mode = "static"
}

// Generate code
generated, err := GenerateWithOptions(data, packageName, opts.EnableEnv, inputDir, maxFileSize, mode)
if err != nil {
Expand Down
47 changes: 47 additions & 0 deletions cfgx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,53 @@ max_conns = 10
require.NotContains(t, outputStr, `":8080"`, "original server.addr should have been overridden")
}

func TestGenerateFromFile_GetterModeIgnoresEnvOverrides(t *testing.T) {
tmpDir := t.TempDir()
inputFile := filepath.Join(tmpDir, "config.toml")
outputFile := filepath.Join(tmpDir, "config.go")

tomlData := []byte(`
[server]
addr = ":8080"

[secrets]
api_key = "set-from-env"
`)

err := os.WriteFile(inputFile, tomlData, 0644)
require.NoError(t, err)

// Set env var that would override the TOML value at generation time
os.Setenv("CONFIG_SECRETS_API_KEY", "sk-real-secret-key-12345")
defer os.Unsetenv("CONFIG_SECRETS_API_KEY")
Comment on lines +92 to +93
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

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

Use testing.T.Setenv (or capture/restore the prior value) instead of os.Setenv + defer os.Unsetenv. Unsetting can clobber an existing value in the parent environment and makes this test less isolated, especially if tests are run in parallel or the env var is already set on the machine/CI runner.

Copilot uses AI. Check for mistakes.

opts := &GenerateOptions{
InputFile: inputFile,
OutputFile: outputFile,
PackageName: "config",
EnableEnv: true,
Mode: "getter",
}

err = GenerateFromFile(opts)
require.NoError(t, err, "GenerateFromFile() should not error")

output, err := os.ReadFile(outputFile)
require.NoError(t, err)

outputStr := string(output)

// The generated code should use the TOML default, NOT the env var value
require.Contains(t, outputStr, `"set-from-env"`,
"getter mode should use TOML default, not env var value")
require.NotContains(t, outputStr, "sk-real-secret-key-12345",
"getter mode must not bake env var values into generated code")

// It should still have the os.Getenv call for runtime override
require.Contains(t, outputStr, `os.Getenv("CONFIG_SECRETS_API_KEY")`,
"getter mode should still generate runtime env var lookups")
Comment on lines +103 to +119
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

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

This test asserts on generated source text but doesn't verify the generated getter-mode file compiles (unlike the other GenerateFromFile tests below). Adding a quick go build on outputFile would catch missing imports / syntax issues and make the regression test stronger.

Copilot uses AI. Check for mistakes.
}

func TestGenerateFromFile(t *testing.T) {
// Create a temporary TOML file
tmpDir := t.TempDir()
Expand Down
Loading