compose/pkg/e2e/env_file_test.go
Guillaume Lours 7bc2630b65 fix(config): resolve service environment when computing --hash
Containers are created from a project whose service environment is
resolved (env_file merged into environment), but since v2.22.0 `config
--hash` skipped that resolution, so hashes diverged from the
com.docker.compose.config-hash label for services using env_file.

Resolve the environment of the hashed services only, so a broken
env_file or platforms on an unrelated service still doesn't prevent
hashing, and honor --no-env-resolution as an escape hatch.

Fixes #14001

Signed-off-by: Guillaume Lours <glours@users.noreply.github.com>
2026-08-11 08:37:13 +02:00

88 lines
3.7 KiB
Go

/*
Copyright 2020 Docker Compose CLI authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package e2e
import (
"strings"
"testing"
"gotest.tools/v3/assert"
"gotest.tools/v3/icmd"
)
func TestRawEnvFile(t *testing.T) {
c := NewParallelCLI(t)
defer c.cleanupWithDown(t, "dotenv")
res := c.RunDockerComposeCmd(t, "-f", "./fixtures/dotenv/raw.yaml", "run", "test")
assert.Equal(t, strings.TrimSpace(res.Stdout()), "'{\"key\": \"value\"}'")
}
func TestUnusedMissingEnvFile(t *testing.T) {
c := NewParallelCLI(t)
defer c.cleanupWithDown(t, "unused_dotenv")
c.RunDockerComposeCmd(t, "-f", "./fixtures/env_file/compose.yaml", "up", "-d", "serviceA")
// Runtime operations should work even with missing env file
c.RunDockerComposeCmd(t, "-f", "./fixtures/env_file/compose.yaml", "ps")
c.RunDockerComposeCmd(t, "-f", "./fixtures/env_file/compose.yaml", "logs")
c.RunDockerComposeCmd(t, "-f", "./fixtures/env_file/compose.yaml", "exec", "serviceA", "echo", "hello")
// scale should work even with missing env file on a service not being scaled
c.RunDockerComposeCmd(t, "-f", "./fixtures/env_file/compose.yaml", "scale", "serviceA=2")
// but scaling the service with the missing env file must still fail
res := c.RunDockerComposeCmdNoCheck(t, "-f", "./fixtures/env_file/compose.yaml", "scale", "serviceB=1")
res.Assert(t, icmd.Expected{ExitCode: 1, Err: "env file /doesnotexist/.env not found"})
// config --hash should work for services not referencing the missing env file
c.RunDockerComposeCmd(t, "-f", "./fixtures/env_file/compose.yaml", "config", "--hash", "serviceA")
// but hashing the service with the missing env file must fail, as up would
res = c.RunDockerComposeCmdNoCheck(t, "-f", "./fixtures/env_file/compose.yaml", "config", "--hash", "serviceB")
res.Assert(t, icmd.Expected{ExitCode: 1, Err: "env file /doesnotexist/.env not found"})
// unless env_file resolution is explicitly disabled
c.RunDockerComposeCmd(t, "-f", "./fixtures/env_file/compose.yaml", "config", "--no-env-resolution", "--hash", "serviceB")
// and so must the wildcard, as it includes serviceB
res = c.RunDockerComposeCmdNoCheck(t, "-f", "./fixtures/env_file/compose.yaml", "config", "--hash", "*")
res.Assert(t, icmd.Expected{ExitCode: 1, Err: "env file /doesnotexist/.env not found"})
// shell completion should list services even with missing env file.
// ComposeStandalonePath fails the test outside standalone mode, so only the
// plugin form can be the default here.
completeCmd := []string{DockerExecutableName, "__complete", "compose"}
if composeStandaloneMode {
completeCmd = []string{ComposeStandalonePath(t), "__complete"}
}
res = c.RunCmd(t, append(completeCmd, "-f", "./fixtures/env_file/compose.yaml", "exec", "")...)
res.Assert(t, icmd.Expected{Out: "serviceA"})
res.Assert(t, icmd.Expected{Out: "serviceB"})
c.RunDockerComposeCmd(t, "-f", "./fixtures/env_file/compose.yaml", "down")
}
func TestRunEnvFile(t *testing.T) {
c := NewParallelCLI(t)
const projectName = "run-dotenv"
defer c.cleanupWithDown(t, projectName)
res := c.RunDockerComposeCmd(t, "-p", projectName, "--project-directory", "./fixtures/env_file", "run", "--rm", "serviceC", "env")
res.Assert(t, icmd.Expected{Out: "FOO=BAR"})
}