Skip to content

Commit 655e99a

Browse files
committed
changes in env vars
1 parent 2d528b1 commit 655e99a

File tree

2 files changed

+141
-11
lines changed

2 files changed

+141
-11
lines changed

cmd/thv-proxyrunner/app/run.go

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -295,17 +295,11 @@ func runCmdFunc(cmd *cobra.Command, args []string) error {
295295

296296
var imageMetadata *registry.ImageMetadata
297297

298-
// Parse environment variables from slice to map only if env flag was set
299-
var envVarsMap map[string]string
300-
if cmd.Flags().Changed("env") {
301-
var err error
302-
envVarsMap, err = environment.ParseEnvironmentVariables(runFlags.runEnv)
303-
if err != nil {
304-
return fmt.Errorf("failed to parse environment variables: %v", err)
305-
}
306-
} else {
307-
// Use empty map if no env flags were set
308-
envVarsMap = make(map[string]string)
298+
// Parse environment variables from CLI flags (always parse, even if empty)
299+
// This ensures CLI env vars can override file-based env vars if provided
300+
envVarsMap, err := environment.ParseEnvironmentVariables(runFlags.runEnv)
301+
if err != nil {
302+
return fmt.Errorf("failed to parse environment variables: %v", err)
309303
}
310304

311305
// Start with basic options

cmd/thv-proxyrunner/app/run_test.go

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ import (
88
"github.com/stretchr/testify/assert"
99
"github.com/stretchr/testify/require"
1010

11+
"github.com/stacklok/toolhive/pkg/environment"
1112
"github.com/stacklok/toolhive/pkg/runner"
13+
"github.com/stacklok/toolhive/pkg/transport/types"
1214
)
1315

1416
func TestRunCmdFlagParsing(t *testing.T) {
@@ -353,3 +355,137 @@ func TestTryLoadConfigFromFile(t *testing.T) {
353355
assert.Contains(t, err.Error(), "failed to parse JSON")
354356
})
355357
}
358+
359+
func TestRunCmdEnvironmentVariableOverrideBehavior(t *testing.T) {
360+
t.Parallel()
361+
362+
t.Run("no CLI env vars preserves file-based env vars", func(t *testing.T) {
363+
t.Parallel()
364+
365+
// Create a config file with environment variables
366+
tmpDir := t.TempDir()
367+
configPath := tmpDir + "/runconfig.json"
368+
369+
configContent := `{
370+
"schema_version": "v1",
371+
"name": "env-test-server",
372+
"image": "test:env",
373+
"transport": "stdio",
374+
"env_vars": {
375+
"FILE_VAR": "file_value",
376+
"COMMON_VAR": "from_file"
377+
}
378+
}`
379+
380+
err := os.WriteFile(configPath, []byte(configContent), 0644)
381+
require.NoError(t, err, "Should be able to create config file")
382+
383+
// Test function that loads config from our specific path
384+
testLoadConfigFromPath := func(path string) (*runner.RunConfig, error) {
385+
if _, err := os.Stat(path); err != nil {
386+
return nil, nil
387+
}
388+
389+
file, err := os.Open(path) // #nosec G304 - test path
390+
if err != nil {
391+
return nil, fmt.Errorf("found config file at %s but failed to open: %w", path, err)
392+
}
393+
defer file.Close()
394+
395+
return runner.ReadJSON(file)
396+
}
397+
398+
config, err := testLoadConfigFromPath(configPath)
399+
require.NoError(t, err, "Should successfully load config from file")
400+
require.NotNil(t, config, "Should return config when file exists")
401+
402+
// Verify file-based environment variables are present
403+
assert.Equal(t, "file_value", config.EnvVars["FILE_VAR"])
404+
assert.Equal(t, "from_file", config.EnvVars["COMMON_VAR"])
405+
406+
// Create a mock command with no --env flags set
407+
cmd := NewRunCmd()
408+
require.NotNil(t, cmd)
409+
addRunFlags(cmd, &proxyRunFlags{})
410+
411+
// Parse empty args (no --env flags)
412+
err = cmd.ParseFlags([]string{"--transport", "stdio", "--name", "test-server"})
413+
require.NoError(t, err)
414+
415+
// Verify that env flags were not changed
416+
assert.False(t, cmd.Flags().Changed("env"), "env flag should not be changed")
417+
})
418+
419+
t.Run("CLI env vars override file-based env vars", func(t *testing.T) {
420+
t.Parallel()
421+
422+
// Create a config file with environment variables
423+
tmpDir := t.TempDir()
424+
configPath := tmpDir + "/runconfig.json"
425+
426+
configContent := `{
427+
"schema_version": "v1",
428+
"name": "env-test-server",
429+
"image": "test:env",
430+
"transport": "stdio",
431+
"env_vars": {
432+
"FILE_VAR": "file_value",
433+
"COMMON_VAR": "from_file"
434+
}
435+
}`
436+
437+
err := os.WriteFile(configPath, []byte(configContent), 0644)
438+
require.NoError(t, err, "Should be able to create config file")
439+
440+
// Create a mock command with --env flags set
441+
cmd := NewRunCmd()
442+
require.NotNil(t, cmd)
443+
flags := &proxyRunFlags{}
444+
addRunFlags(cmd, flags)
445+
446+
// Parse args with --env flags that should override file-based env vars
447+
err = cmd.ParseFlags([]string{
448+
"--transport", "stdio",
449+
"--name", "test-server",
450+
"--env", "CLI_VAR=cli_value",
451+
"--env", "COMMON_VAR=from_cli",
452+
})
453+
require.NoError(t, err)
454+
455+
// Verify that env flag was changed and values are correct
456+
assert.True(t, cmd.Flags().Changed("env"), "env flag should be changed")
457+
assert.Contains(t, flags.runEnv, "CLI_VAR=cli_value")
458+
assert.Contains(t, flags.runEnv, "COMMON_VAR=from_cli")
459+
})
460+
461+
t.Run("empty CLI env vars do not interfere with file-based env vars", func(t *testing.T) {
462+
t.Parallel()
463+
464+
// Test that ParseEnvironmentVariables with empty slice returns empty map
465+
envVarsMap, err := environment.ParseEnvironmentVariables([]string{})
466+
require.NoError(t, err, "ParseEnvironmentVariables should handle empty slice")
467+
assert.Equal(t, map[string]string{}, envVarsMap, "Empty slice should return empty map")
468+
assert.Len(t, envVarsMap, 0, "Empty map should have zero length")
469+
470+
// Create a RunConfig with some existing env vars (simulating file-based config)
471+
config := &runner.RunConfig{
472+
Transport: types.TransportTypeStdio,
473+
TargetPort: 8080,
474+
EnvVars: map[string]string{
475+
"FILE_VAR": "file_value",
476+
"COMMON_VAR": "from_file",
477+
},
478+
}
479+
480+
// Apply empty environment variables (simulating no CLI --env flags)
481+
resultConfig, err := config.WithEnvironmentVariables(envVarsMap)
482+
require.NoError(t, err, "WithEnvironmentVariables should handle empty map")
483+
484+
// Verify that original env vars are preserved
485+
assert.Equal(t, "file_value", resultConfig.EnvVars["FILE_VAR"], "File-based env var should be preserved")
486+
assert.Equal(t, "from_file", resultConfig.EnvVars["COMMON_VAR"], "File-based env var should be preserved")
487+
488+
// Verify transport-specific env vars are also set (these are always added)
489+
assert.Equal(t, "stdio", resultConfig.EnvVars["MCP_TRANSPORT"], "Transport env var should be set")
490+
})
491+
}

0 commit comments

Comments
 (0)