diff --git a/cli/options.go b/cli/options.go index 01cb590bd..f42c4872e 100644 --- a/cli/options.go +++ b/cli/options.go @@ -37,8 +37,6 @@ import ( // ProjectOptions provides common configuration for loading a project. type ProjectOptions struct { - ctx context.Context - // Name is a valid Compose project name to be used or empty. // // If empty, the project loader will automatically infer a reasonable @@ -334,14 +332,6 @@ func WithResolvedPaths(resolve bool) ProjectOptionsFn { } } -// WithContext sets the context used to load model and resources -func WithContext(ctx context.Context) ProjectOptionsFn { - return func(o *ProjectOptions) error { - o.ctx = ctx - return nil - } -} - // WithResourceLoader register support for ResourceLoader to manage remote resources func WithResourceLoader(r loader.ResourceLoader) ProjectOptionsFn { return func(o *ProjectOptions) error { @@ -381,7 +371,7 @@ var DefaultOverrideFileNames = []string{"compose.override.yml", "compose.overrid func (o ProjectOptions) GetWorkingDir() (string, error) { if o.WorkingDir != "" { - return o.WorkingDir, nil + return filepath.Abs(o.WorkingDir) } for _, path := range o.ConfigPaths { if path != "-" { @@ -395,9 +385,8 @@ func (o ProjectOptions) GetWorkingDir() (string, error) { return os.Getwd() } -// ProjectFromOptions load a compose project based on command line options -func ProjectFromOptions(options *ProjectOptions) (*types.Project, error) { - configPaths, err := getConfigPathsFromOptions(options) +func (o ProjectOptions) GeConfigFiles() ([]types.ConfigFile, error) { + configPaths, err := o.getConfigPaths() if err != nil { return nil, err } @@ -425,25 +414,25 @@ func ProjectFromOptions(options *ProjectOptions) (*types.Project, error) { Content: b, }) } + return configs, err +} - workingDir, err := options.GetWorkingDir() +// ProjectFromOptions load a compose project based on command line options +func ProjectFromOptions(ctx context.Context, options *ProjectOptions) (*types.Project, error) { + configs, err := options.GeConfigFiles() if err != nil { return nil, err } - absWorkingDir, err := filepath.Abs(workingDir) + + workingDir, err := options.GetWorkingDir() if err != nil { return nil, err } options.loadOptions = append(options.loadOptions, - withNamePrecedenceLoad(absWorkingDir, options), + withNamePrecedenceLoad(workingDir, options), withConvertWindowsPaths(options)) - ctx := options.ctx - if ctx == nil { - ctx = context.Background() - } - project, err := loader.LoadWithContext(ctx, types.ConfigDetails{ ConfigFiles: configs, WorkingDir: workingDir, @@ -453,7 +442,10 @@ func ProjectFromOptions(options *ProjectOptions) (*types.Project, error) { return nil, err } - project.ComposeFiles = configPaths + for _, config := range configs { + project.ComposeFiles = append(project.ComposeFiles, config.Filename) + } + return project, nil } @@ -480,10 +472,10 @@ func withConvertWindowsPaths(options *ProjectOptions) func(*loader.Options) { } } -// getConfigPathsFromOptions retrieves the config files for project based on project options -func getConfigPathsFromOptions(options *ProjectOptions) ([]string, error) { - if len(options.ConfigPaths) != 0 { - return absolutePaths(options.ConfigPaths) +// getConfigPaths retrieves the config files for project based on project options +func (o *ProjectOptions) getConfigPaths() ([]string, error) { + if len(o.ConfigPaths) != 0 { + return absolutePaths(o.ConfigPaths) } return nil, fmt.Errorf("no configuration file provided: %w", errdefs.ErrNotFound) } diff --git a/cli/options_test.go b/cli/options_test.go index 07565c305..31edf9b95 100644 --- a/cli/options_test.go +++ b/cli/options_test.go @@ -17,6 +17,7 @@ package cli import ( + "context" "fmt" "os" "path/filepath" @@ -33,7 +34,7 @@ func TestProjectName(t *testing.T) { t.Run("by name", func(t *testing.T) { opts, err := NewProjectOptions([]string{"testdata/simple/compose.yaml"}, WithName("my_project")) assert.NilError(t, err) - p, err := ProjectFromOptions(opts) + p, err := ProjectFromOptions(context.TODO(), opts) assert.NilError(t, err) assert.Equal(t, p.Name, "my_project") }) @@ -41,7 +42,7 @@ func TestProjectName(t *testing.T) { t.Run("by name start with number", func(t *testing.T) { opts, err := NewProjectOptions([]string{"testdata/simple/compose.yaml"}, WithName("42my_project_num")) assert.NilError(t, err) - p, err := ProjectFromOptions(opts) + p, err := ProjectFromOptions(context.TODO(), opts) assert.NilError(t, err) assert.Equal(t, p.Name, "42my_project_num") @@ -49,7 +50,7 @@ func TestProjectName(t *testing.T) { fmt.Sprintf("%s=%s", consts.ComposeProjectName, "42my_project_env"), })) assert.NilError(t, err) - p, err = ProjectFromOptions(opts) + p, err = ProjectFromOptions(context.TODO(), opts) assert.NilError(t, err) assert.Equal(t, p.Name, "42my_project_env") }) @@ -60,7 +61,7 @@ func TestProjectName(t *testing.T) { WithName(""), ) assert.NilError(t, err) - p, err := ProjectFromOptions(opts) + p, err := ProjectFromOptions(context.TODO(), opts) assert.NilError(t, err) assert.Equal(t, p.Name, "simple") }) @@ -72,7 +73,7 @@ func TestProjectName(t *testing.T) { WithWorkingDirectory("/path/to/proj"), ) assert.NilError(t, err) - p, err := ProjectFromOptions(opts) + p, err := ProjectFromOptions(context.TODO(), opts) assert.NilError(t, err) assert.Equal(t, p.Name, "proj") }) @@ -81,7 +82,7 @@ func TestProjectName(t *testing.T) { opts, err := NewProjectOptions([]string{"testdata/simple/compose.yaml"}, WithWorkingDirectory("/")) assert.NilError(t, err) - p, err := ProjectFromOptions(opts) + p, err := ProjectFromOptions(context.TODO(), opts) // root directory will resolve to an empty project name since there // IS no directory name! @@ -97,7 +98,7 @@ func TestProjectName(t *testing.T) { fmt.Sprintf("%s=%s", consts.ComposeProjectName, "-my_project"), })) assert.NilError(t, err) - p, err := ProjectFromOptions(opts) + p, err := ProjectFromOptions(context.TODO(), opts) assert.ErrorContains(t, err, `invalid project name "-my_project"`) assert.Assert(t, p == nil) }) @@ -110,7 +111,7 @@ func TestProjectName(t *testing.T) { fmt.Sprintf("%s=%s", consts.ComposeProjectName, "_my_project"), })) assert.NilError(t, err) - p, err := ProjectFromOptions(opts) + p, err := ProjectFromOptions(context.TODO(), opts) assert.ErrorContains(t, err, `invalid project name "_my_project"`) assert.Assert(t, p == nil) }) @@ -123,7 +124,7 @@ func TestProjectName(t *testing.T) { fmt.Sprintf("%s=%s", consts.ComposeProjectName, "www.my.project"), })) assert.NilError(t, err) - p, err := ProjectFromOptions(opts) + p, err := ProjectFromOptions(context.TODO(), opts) assert.ErrorContains(t, err, `invalid project name "www.my.project"`) assert.Assert(t, p == nil) }) @@ -136,7 +137,7 @@ func TestProjectName(t *testing.T) { fmt.Sprintf("%s=%s", consts.ComposeProjectName, "MY_PROJECT"), })) assert.NilError(t, err) - p, err := ProjectFromOptions(opts) + p, err := ProjectFromOptions(context.TODO(), opts) assert.ErrorContains(t, err, `invalid project name "MY_PROJECT"`) assert.Assert(t, p == nil) }) @@ -144,7 +145,7 @@ func TestProjectName(t *testing.T) { t.Run("by working dir", func(t *testing.T) { opts, err := NewProjectOptions([]string{"testdata/simple/compose.yaml"}, WithWorkingDirectory(".")) assert.NilError(t, err) - p, err := ProjectFromOptions(opts) + p, err := ProjectFromOptions(context.TODO(), opts) assert.NilError(t, err) assert.Equal(t, p.Name, "cli") }) @@ -152,7 +153,7 @@ func TestProjectName(t *testing.T) { t.Run("by compose file parent dir", func(t *testing.T) { opts, err := NewProjectOptions([]string{"testdata/simple/compose.yaml"}) assert.NilError(t, err) - p, err := ProjectFromOptions(opts) + p, err := ProjectFromOptions(context.TODO(), opts) assert.NilError(t, err) assert.Equal(t, p.Name, "simple") }) @@ -160,7 +161,7 @@ func TestProjectName(t *testing.T) { t.Run("by compose file parent dir special", func(t *testing.T) { opts, err := NewProjectOptions([]string{"testdata/UNNORMALIZED PATH/compose.yaml"}) assert.NilError(t, err) - p, err := ProjectFromOptions(opts) + p, err := ProjectFromOptions(context.TODO(), opts) assert.NilError(t, err) assert.Equal(t, p.Name, "unnormalizedpath") }) @@ -170,7 +171,7 @@ func TestProjectName(t *testing.T) { defer os.Unsetenv("COMPOSE_PROJECT_NAME") //nolint:errcheck opts, err := NewProjectOptions([]string{"testdata/simple/compose.yaml"}, WithOsEnv) assert.NilError(t, err) - p, err := ProjectFromOptions(opts) + p, err := ProjectFromOptions(context.TODO(), opts) assert.NilError(t, err) assert.Equal(t, p.Name, "my_project_from_env") }) @@ -184,7 +185,7 @@ func TestProjectName(t *testing.T) { opts, err := NewProjectOptions(nil, WithEnvFiles(), WithDotEnv, WithConfigFileEnv) assert.NilError(t, err) - p, err := ProjectFromOptions(opts) + p, err := ProjectFromOptions(context.TODO(), opts) assert.NilError(t, err) assert.Equal(t, p.Name, "my_project_from_dot_env") }) @@ -194,7 +195,7 @@ func TestProjectName(t *testing.T) { "TEST=expected", })) assert.NilError(t, err) - p, err := ProjectFromOptions(opts) + p, err := ProjectFromOptions(context.TODO(), opts) assert.NilError(t, err) assert.Equal(t, p.Name, "test-expected-test") }) @@ -206,7 +207,7 @@ func TestProjectFromSetOfFiles(t *testing.T) { "testdata/simple/compose-with-overrides.yaml", }, WithName("my_project")) assert.NilError(t, err) - p, err := ProjectFromOptions(opts) + p, err := ProjectFromOptions(context.TODO(), opts) assert.NilError(t, err) service, err := p.GetService("simple") assert.NilError(t, err) @@ -220,7 +221,7 @@ func TestProjectComposefilesFromSetOfFiles(t *testing.T) { WithDefaultConfigPath, ) assert.NilError(t, err) - p, err := ProjectFromOptions(opts) + p, err := ProjectFromOptions(context.TODO(), opts) assert.NilError(t, err) absPath, _ := filepath.Abs(filepath.Join("testdata", "simple", "compose.yaml")) assert.DeepEqual(t, p.ComposeFiles, []string{absPath}) @@ -232,7 +233,7 @@ func TestProjectComposefilesFromWorkingDir(t *testing.T) { "testdata/simple/compose-with-overrides.yaml", }, WithName("my_project")) assert.NilError(t, err) - p, err := ProjectFromOptions(opts) + p, err := ProjectFromOptions(context.TODO(), opts) assert.NilError(t, err) currentDir, _ := os.Getwd() assert.DeepEqual(t, p.ComposeFiles, []string{ @@ -252,7 +253,7 @@ func TestProjectWithDotEnv(t *testing.T) { "compose-with-variables.yaml", }, WithName("my_project"), WithEnvFiles(), WithDotEnv) assert.NilError(t, err) - p, err := ProjectFromOptions(opts) + p, err := ProjectFromOptions(context.TODO(), opts) assert.NilError(t, err) service, err := p.GetService("simple") assert.NilError(t, err) @@ -265,7 +266,7 @@ func TestProjectWithDiscardEnvFile(t *testing.T) { }, WithDiscardEnvFile) assert.NilError(t, err) - p, err := ProjectFromOptions(opts) + p, err := ProjectFromOptions(context.TODO(), opts) assert.NilError(t, err) service, err := p.GetService("simple") assert.NilError(t, err) @@ -282,7 +283,7 @@ func TestProjectWithMultipleEnvFile(t *testing.T) { WithDotEnv) assert.NilError(t, err) - p, err := ProjectFromOptions(opts) + p, err := ProjectFromOptions(context.TODO(), opts) assert.NilError(t, err) service, err := p.GetService("simple") assert.NilError(t, err) @@ -296,7 +297,7 @@ func TestProjectNameFromWorkingDir(t *testing.T) { "testdata/env-file/compose-with-env-file.yaml", }) assert.NilError(t, err) - p, err := ProjectFromOptions(opts) + p, err := ProjectFromOptions(context.TODO(), opts) assert.NilError(t, err) assert.Equal(t, p.Name, "env-file") } diff --git a/cmd/main.go b/cmd/main.go index d2fec0d9f..f73620a16 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -17,6 +17,7 @@ package main import ( + "context" "fmt" "os" @@ -47,7 +48,7 @@ Usage: compose-spec [OPTIONS] COMPOSE_FILE [COMPOSE_OVERRIDE_FILE]`) exitError("failed to configure project options", err) } - project, err := cli.ProjectFromOptions(options) + project, err := cli.ProjectFromOptions(context.Background(), options) if err != nil { exitError("failed to load project", err) } diff --git a/loader/loader_test.go b/loader/loader_test.go index 41cb7a889..617750237 100644 --- a/loader/loader_test.go +++ b/loader/loader_test.go @@ -70,7 +70,7 @@ func loadYAML(yaml string) (*types.Project, error) { } func loadYAMLWithEnv(yaml string, env map[string]string) (*types.Project, error) { - return Load(buildConfigDetails(yaml, env), func(options *Options) { + return LoadWithContext(context.TODO(), buildConfigDetails(yaml, env), func(options *Options) { options.SkipConsistencyCheck = true options.SkipNormalization = true options.ResolvePaths = true