Fix rawsetenv nil-pointer guard for inherit-from-shell env vars

When a service declares an env var without a value (e.g. `- KEY` or
`KEY:`), MappingWithEquals stores it as a nil *string. The previous
condition `existing != nil && ...` skipped the warning for this case,
allowing silent overwrites. Change to `existing == nil || ...` so the
warning fires for both nil (shell-inherit) and value-mismatch cases.

Add e2e tests for both list-style (`- KEY`) and map-style (`KEY:`)
YAML forms to lock in the behavior.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Yohta Kimura <38206553+rajyan@users.noreply.github.com>
This commit is contained in:
Yohta Kimura 2026-06-23 10:29:09 +09:00 committed by Guillaume Lours
parent cdaeeac6ed
commit 43922d55b0
4 changed files with 67 additions and 1 deletions

View file

@ -94,7 +94,7 @@ func (s *composeService) runPlugin(ctx context.Context, project *types.Project,
s.Environment[prefix+key] = &val
}
for key, val := range variables.raw {
if existing, ok := s.Environment[key]; ok && existing != nil && *existing != val {
if existing, ok := s.Environment[key]; ok && (existing == nil || *existing != val) {
logrus.Warnf("provider %q overrides environment variable %q in service %q", service.Name, key, name)
}
s.Environment[key] = &val

View file

@ -0,0 +1,15 @@
services:
test:
image: alpine
command: env
environment:
CLOUD_REGION:
depends_on:
- secrets
secrets:
provider:
type: example-provider
options:
name: secrets
type: test1
size: 1

View file

@ -0,0 +1,15 @@
services:
test:
image: alpine
command: env
environment:
- CLOUD_REGION
depends_on:
- secrets
secrets:
provider:
type: example-provider
options:
name: secrets
type: test1
size: 1

View file

@ -119,6 +119,42 @@ func TestProviderRawSetEnvOverridesUserEnv(t *testing.T) {
assert.Check(t, strings.Contains(res.Combined(), "overrides environment variable"), res.Combined())
}
func TestProviderRawSetEnvOverridesInheritedEnv(t *testing.T) {
provider, err := findExecutable("example-provider")
assert.NilError(t, err)
path := fmt.Sprintf("%s%s%s", os.Getenv("PATH"), string(os.PathListSeparator), filepath.Dir(provider))
c := NewParallelCLI(t, WithEnv("PATH="+path))
const projectName = "rawsetenv-inherit"
t.Cleanup(func() {
c.cleanupWithDown(t, projectName)
})
res := c.RunDockerComposeCmd(t, "-f", "fixtures/providers/rawsetenv-inherit.yaml", "--project-name", projectName, "up")
res.Assert(t, icmd.Success)
env := getEnv(res.Combined())
assert.Check(t, slices.Contains(env, "CLOUD_REGION=us-east-1"), env)
assert.Check(t, strings.Contains(res.Combined(), "overrides environment variable"), res.Combined())
}
func TestProviderRawSetEnvOverridesInheritedEnvMapForm(t *testing.T) {
provider, err := findExecutable("example-provider")
assert.NilError(t, err)
path := fmt.Sprintf("%s%s%s", os.Getenv("PATH"), string(os.PathListSeparator), filepath.Dir(provider))
c := NewParallelCLI(t, WithEnv("PATH="+path))
const projectName = "rawsetenv-inherit-map"
t.Cleanup(func() {
c.cleanupWithDown(t, projectName)
})
res := c.RunDockerComposeCmd(t, "-f", "fixtures/providers/rawsetenv-inherit-map.yaml", "--project-name", projectName, "up")
res.Assert(t, icmd.Success)
env := getEnv(res.Combined())
assert.Check(t, slices.Contains(env, "CLOUD_REGION=us-east-1"), env)
assert.Check(t, strings.Contains(res.Combined(), "overrides environment variable"), res.Combined())
}
func getEnv(out string) []string {
var env []string
scanner := bufio.NewScanner(strings.NewReader(out))