Skip to content
This repository was archived by the owner on Jul 18, 2025. It is now read-only.

Commit 981fb65

Browse files
authored
Merge pull request #343 from dustinrc/multi-composefile-envvar
Parse multifile COMPOSE_FILE envvar, w/ side effect
2 parents 380641e + 16a53f5 commit 981fb65

File tree

2 files changed

+95
-1
lines changed

2 files changed

+95
-1
lines changed

cli/command/command.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package command
22

33
import (
44
"os"
5+
"strings"
56

67
"github.com/docker/libcompose/cli/app"
78
"github.com/docker/libcompose/project"
@@ -10,7 +11,14 @@ import (
1011

1112
// Populate updates the specified project context based on command line arguments and subcommands.
1213
func Populate(context *project.Context, c *cli.Context) {
13-
context.ComposeFiles = c.GlobalStringSlice("file")
14+
// urfave/cli does not distinguish whether the first string in the slice comes from the envvar
15+
// or is from a flag. Worse off, it appends the flag values to the envvar value instead of
16+
// overriding it. To ensure the multifile envvar case is always handled, the first string
17+
// must always be split. It gives a more consistent behavior, then, to split each string in
18+
// the slice.
19+
for _, v := range c.GlobalStringSlice("file") {
20+
context.ComposeFiles = append(context.ComposeFiles, strings.Split(v, string(os.PathListSeparator))...)
21+
}
1422

1523
if len(context.ComposeFiles) == 0 {
1624
context.ComposeFiles = []string{"docker-compose.yml"}

cli/docker/app/factory_test.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package app
33
import (
44
"flag"
55
"io/ioutil"
6+
"os"
67
"path/filepath"
78
"testing"
89

@@ -55,3 +56,88 @@ func TestProjectFactoryProjectNameIsNormalized(t *testing.T) {
5556
}
5657
}
5758
}
59+
60+
func TestProjectFactoryFileArgMayContainMultipleFiles(t *testing.T) {
61+
sep := string(os.PathListSeparator)
62+
fileCases := []struct {
63+
requested []string
64+
available []string
65+
expected []string
66+
}{
67+
{
68+
requested: []string{},
69+
available: []string{"docker-compose.yml"},
70+
expected: []string{"docker-compose.yml"},
71+
},
72+
{
73+
requested: []string{},
74+
available: []string{"docker-compose.yml", "docker-compose.override.yml"},
75+
expected: []string{"docker-compose.yml", "docker-compose.override.yml"},
76+
},
77+
{
78+
requested: []string{"one.yml"},
79+
available: []string{"one.yml"},
80+
expected: []string{"one.yml"},
81+
},
82+
{
83+
requested: []string{"one.yml"},
84+
available: []string{"docker-compose.yml", "one.yml"},
85+
expected: []string{"one.yml"},
86+
},
87+
{
88+
requested: []string{"one.yml", "two.yml", "three.yml"},
89+
available: []string{"one.yml", "two.yml", "three.yml"},
90+
expected: []string{"one.yml", "two.yml", "three.yml"},
91+
},
92+
{
93+
requested: []string{"one.yml" + sep + "two.yml" + sep + "three.yml"},
94+
available: []string{"one.yml", "two.yml", "three.yml"},
95+
expected: []string{"one.yml", "two.yml", "three.yml"},
96+
},
97+
{
98+
requested: []string{"one.yml" + sep + "two.yml", "three.yml" + sep + "four.yml"},
99+
available: []string{"one.yml", "two.yml", "three.yml", "four.yml"},
100+
expected: []string{"one.yml", "two.yml", "three.yml", "four.yml"},
101+
},
102+
{
103+
requested: []string{"one.yml", "two.yml" + sep + "three.yml"},
104+
available: []string{"one.yml", "two.yml", "three.yml"},
105+
expected: []string{"one.yml", "two.yml", "three.yml"},
106+
},
107+
}
108+
109+
for _, fileCase := range fileCases {
110+
tmpDir, err := ioutil.TempDir("", "project-factory-test")
111+
if err != nil {
112+
t.Fatal(err)
113+
}
114+
defer os.RemoveAll(tmpDir)
115+
if err = os.Chdir(tmpDir); err != nil {
116+
t.Fatal(err)
117+
}
118+
119+
for _, file := range fileCase.available {
120+
ioutil.WriteFile(file, []byte(`hello:
121+
image: busybox`), 0700)
122+
}
123+
globalSet := flag.NewFlagSet("test", 0)
124+
// Set the project-name flag
125+
globalSet.String("project-name", "example", "doc")
126+
// Set the compose file flag
127+
fcr := cli.StringSlice(fileCase.requested)
128+
globalSet.Var(&fcr, "file", "doc")
129+
c := cli.NewContext(nil, globalSet, nil)
130+
factory := &ProjectFactory{}
131+
p, err := factory.Create(c)
132+
if err != nil {
133+
t.Fatal(err)
134+
}
135+
136+
for i, v := range p.(*project.Project).Files {
137+
if v != fileCase.expected[i] {
138+
t.Fatalf("requested %s, available %s, expected %s, got %s",
139+
fileCase.requested, fileCase.available, fileCase.expected, p.(*project.Project).Files)
140+
}
141+
}
142+
}
143+
}

0 commit comments

Comments
 (0)