Skip to content

Commit 9856802

Browse files
tensorworkerndeloof
authored andcommitted
fix: expand tilde in --env-file paths to user home directory
When using --env-file=~/.env, the tilde was not expanded to the user's home directory. Instead, it was treated as a literal character and resolved relative to the current working directory, resulting in errors like "couldn't find env file: /current/dir/~/.env". This adds an ExpandUser function that expands ~ to the home directory before converting relative paths to absolute paths. Fixes #13508 Signed-off-by: tensorworker <tensorworker@proton.me>
1 parent 63ae7eb commit 9856802

File tree

3 files changed

+112
-0
lines changed

3 files changed

+112
-0
lines changed

cmd/compose/compose.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ import (
4646

4747
"github.com/docker/compose/v5/cmd/display"
4848
"github.com/docker/compose/v5/cmd/formatter"
49+
"github.com/docker/compose/v5/internal/paths"
4950
"github.com/docker/compose/v5/internal/tracing"
5051
"github.com/docker/compose/v5/pkg/api"
5152
"github.com/docker/compose/v5/pkg/compose"
@@ -550,12 +551,15 @@ func RootCommand(dockerCli command.Cli, backendOptions *BackendOptions) *cobra.C
550551
fmt.Fprint(os.Stderr, aec.Apply("option '--workdir' is DEPRECATED at root level! Please use '--project-directory' instead.\n", aec.RedF))
551552
}
552553
for i, file := range opts.EnvFiles {
554+
file = paths.ExpandUser(file)
553555
if !filepath.IsAbs(file) {
554556
file, err := filepath.Abs(file)
555557
if err != nil {
556558
return err
557559
}
558560
opts.EnvFiles[i] = file
561+
} else {
562+
opts.EnvFiles[i] = file
559563
}
560564
}
561565

internal/paths/paths.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,30 @@ import (
2222
"strings"
2323
)
2424

25+
// ExpandUser expands a leading tilde (~) in a path to the user's home directory.
26+
// If the path doesn't start with ~, it is returned unchanged.
27+
// If the home directory cannot be determined, the original path is returned.
28+
func ExpandUser(path string) string {
29+
if path == "" {
30+
return path
31+
}
32+
if path[0] != '~' {
33+
return path
34+
}
35+
if len(path) > 1 && path[1] != '/' && path[1] != filepath.Separator {
36+
// ~otheruser/... syntax is not supported
37+
return path
38+
}
39+
home, err := os.UserHomeDir()
40+
if err != nil {
41+
return path
42+
}
43+
if len(path) == 1 {
44+
return home
45+
}
46+
return filepath.Join(home, path[2:])
47+
}
48+
2549
func IsChild(dir string, file string) bool {
2650
if dir == "" {
2751
return false

internal/paths/paths_test.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*
2+
Copyright 2020 Docker Compose CLI authors
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package paths
18+
19+
import (
20+
"os"
21+
"path/filepath"
22+
"testing"
23+
24+
"gotest.tools/v3/assert"
25+
)
26+
27+
func TestExpandUser(t *testing.T) {
28+
home, err := os.UserHomeDir()
29+
assert.NilError(t, err)
30+
31+
tests := []struct {
32+
name string
33+
input string
34+
expected string
35+
}{
36+
{
37+
name: "empty string",
38+
input: "",
39+
expected: "",
40+
},
41+
{
42+
name: "tilde only",
43+
input: "~",
44+
expected: home,
45+
},
46+
{
47+
name: "tilde with slash",
48+
input: "~/.env",
49+
expected: filepath.Join(home, ".env"),
50+
},
51+
{
52+
name: "tilde with subdir",
53+
input: "~/subdir/.env",
54+
expected: filepath.Join(home, "subdir", ".env"),
55+
},
56+
{
57+
name: "absolute path unchanged",
58+
input: "/absolute/path/.env",
59+
expected: "/absolute/path/.env",
60+
},
61+
{
62+
name: "relative path unchanged",
63+
input: "relative/path/.env",
64+
expected: "relative/path/.env",
65+
},
66+
{
67+
name: "tilde in middle unchanged",
68+
input: "/path/~/file",
69+
expected: "/path/~/file",
70+
},
71+
{
72+
name: "tilde other user unchanged",
73+
input: "~otheruser/.env",
74+
expected: "~otheruser/.env",
75+
},
76+
}
77+
78+
for _, tt := range tests {
79+
t.Run(tt.name, func(t *testing.T) {
80+
result := ExpandUser(tt.input)
81+
assert.Equal(t, result, tt.expected)
82+
})
83+
}
84+
}

0 commit comments

Comments
 (0)