Repository: docker/compose-cli
Pull request #1216 (2021-02-02 10:46:37)

Introduce --profile option
**What I did**
Added support for --profiles according to compose-spec

**Related issue**

**(not mandatory) A picture of a cute animal, if possible in relation with what you did**
![image](https://user-images.githubusercontent.com/132757/106589419-4ab18580-654c-11eb-911c-76501e361a3a.png)




Patch:
diff --git a/cli/cmd/compose/build.go b/cli/cmd/compose/build.go
index 2b1b16d0..29bc29f6 100644
--- a/cli/cmd/compose/build.go
+++ b/cli/cmd/compose/build.go
@@ -50,16 +50,12 @@ func runBuild(ctx context.Context, opts buildOptions, services []string) error {
 		return err
 	}
 
-	_, err = progress.Run(ctx, func(ctx context.Context) (string, error) {
-		project, err := opts.toProject()
-		if err != nil {
-			return "", err
-		}
+	project, err := opts.toProject(services)
+	if err != nil {
+		return err
+	}
 
-		err = filter(project, services)
-		if err != nil {
-			return "", err
-		}
+	_, err = progress.Run(ctx, func(ctx context.Context) (string, error) {
 		return "", c.ComposeService().Build(ctx, project)
 	})
 	return err
diff --git a/cli/cmd/compose/compose.go b/cli/cmd/compose/compose.go
index 065a0b6d..c10d81ee 100644
--- a/cli/cmd/compose/compose.go
+++ b/cli/cmd/compose/compose.go
@@ -29,12 +29,14 @@ import (
 
 type projectOptions struct {
 	ProjectName string
+	Profiles    []string
 	ConfigPaths []string
 	WorkingDir  string
 	EnvFile     string
 }
 
 func (o *projectOptions) addProjectFlags(f *pflag.FlagSet) {
+	f.StringArrayVar(&o.Profiles, "profile", []string{}, "Specify a profile to enable")
 	f.StringVarP(&o.ProjectName, "project-name", "p", "", "Project name")
 	f.StringArrayVarP(&o.ConfigPaths, "file", "f", []string{}, "Compose configuration files")
 	f.StringVar(&o.EnvFile, "env-file", "", "Specify an alternate environment file.")
@@ -47,14 +49,14 @@ func (o *projectOptions) toProjectName() (string, error) {
 		return o.ProjectName, nil
 	}
 
-	project, err := o.toProject()
+	project, err := o.toProject(nil)
 	if err != nil {
 		return "", err
 	}
 	return project.Name, nil
 }
 
-func (o *projectOptions) toProject() (*types.Project, error) {
+func (o *projectOptions) toProject(services []string) (*types.Project, error) {
 	options, err := o.toProjectOptions()
 	if err != nil {
 		return nil, err
@@ -64,7 +66,19 @@ func (o *projectOptions) toProject() (*types.Project, error) {
 	if err != nil {
 		return nil, err
 	}
-	return project, nil
+
+	if len(services) != 0 {
+		s, err := project.GetServices(services)
+		if err != nil {
+			return nil, err
+		}
+		o.Profiles = append(o.Profiles, s.GetProfiles()...)
+	}
+
+	project.ApplyProfiles(o.Profiles)
+
+	err = project.ForServices(services)
+	return project, err
 }
 
 func (o *projectOptions) toProjectOptions() (*cli.ProjectOptions, error) {
@@ -114,40 +128,3 @@ func Command(contextType string) *cobra.Command {
 	opts.addProjectFlags(command.PersistentFlags())
 	return command
 }
-
-//
-func filter(project *types.Project, services []string) error {
-	if len(services) == 0 {
-		// All services
-		return nil
-	}
-
-	names := map[string]bool{}
-	err := addServiceNames(project, services, names)
-	if err != nil {
-		return err
-	}
-	var filtered types.Services
-	for _, s := range project.Services {
-		if _, ok := names[s.Name]; ok {
-			filtered = append(filtered, s)
-		}
-	}
-	project.Services = filtered
-	return nil
-}
-
-func addServiceNames(project *types.Project, services []string, names map[string]bool) error {
-	for _, name := range services {
-		names[name] = true
-		service, err := project.GetService(name)
-		if err != nil {
-			return err
-		}
-		err = addServiceNames(project, service.GetDependencies(), names)
-		if err != nil {
-			return err
-		}
-	}
-	return nil
-}
diff --git a/cli/cmd/compose/convert.go b/cli/cmd/compose/convert.go
index 99b26b55..16800d76 100644
--- a/cli/cmd/compose/convert.go
+++ b/cli/cmd/compose/convert.go
@@ -37,10 +37,11 @@ func convertCommand(p *projectOptions) *cobra.Command {
 		projectOptions: p,
 	}
 	convertCmd := &cobra.Command{
-		Use:   "convert",
-		Short: "Converts the compose file to a cloud format (default: cloudformation)",
+		Aliases: []string{"config"},
+		Use:     "convert SERVICES",
+		Short:   "Converts the compose file to platform's canonical format",
 		RunE: func(cmd *cobra.Command, args []string) error {
-			return runConvert(cmd.Context(), opts)
+			return runConvert(cmd.Context(), opts, args)
 		},
 	}
 	flags := convertCmd.Flags()
@@ -49,14 +50,14 @@ func convertCommand(p *projectOptions) *cobra.Command {
 	return convertCmd
 }
 
-func runConvert(ctx context.Context, opts convertOptions) error {
+func runConvert(ctx context.Context, opts convertOptions, services []string) error {
 	var json []byte
 	c, err := client.NewWithDefaultLocalBackend(ctx)
 	if err != nil {
 		return err
 	}
 
-	project, err := opts.toProject()
+	project, err := opts.toProject(services)
 	if err != nil {
 		return err
 	}
diff --git a/cli/cmd/compose/down.go b/cli/cmd/compose/down.go
index e36bfd89..ef48e71a 100644
--- a/cli/cmd/compose/down.go
+++ b/cli/cmd/compose/down.go
@@ -57,7 +57,7 @@ func runDown(ctx context.Context, opts downOptions) error {
 		name := opts.ProjectName
 		var project *types.Project
 		if opts.ProjectName == "" {
-			p, err := opts.toProject()
+			p, err := opts.toProject(nil)
 			if err != nil {
 				return "", err
 			}
diff --git a/cli/cmd/compose/pull.go b/cli/cmd/compose/pull.go
index b519ec1e..3617058e 100644
--- a/cli/cmd/compose/pull.go
+++ b/cli/cmd/compose/pull.go
@@ -50,16 +50,12 @@ func runPull(ctx context.Context, opts pullOptions, services []string) error {
 		return err
 	}
 
-	_, err = progress.Run(ctx, func(ctx context.Context) (string, error) {
-		project, err := opts.toProject()
-		if err != nil {
-			return "", err
-		}
+	project, err := opts.toProject(services)
+	if err != nil {
+		return err
+	}
 
-		err = filter(project, services)
-		if err != nil {
-			return "", err
-		}
+	_, err = progress.Run(ctx, func(ctx context.Context) (string, error) {
 		return "", c.ComposeService().Pull(ctx, project)
 	})
 	return err
diff --git a/cli/cmd/compose/push.go b/cli/cmd/compose/push.go
index c36a8d38..407f8836 100644
--- a/cli/cmd/compose/push.go
+++ b/cli/cmd/compose/push.go
@@ -50,16 +50,12 @@ func runPush(ctx context.Context, opts pushOptions, services []string) error {
 		return err
 	}
 
-	_, err = progress.Run(ctx, func(ctx context.Context) (string, error) {
-		project, err := opts.toProject()
-		if err != nil {
-			return "", err
-		}
+	project, err := opts.toProject(services)
+	if err != nil {
+		return err
+	}
 
-		err = filter(project, services)
-		if err != nil {
-			return "", err
-		}
+	_, err = progress.Run(ctx, func(ctx context.Context) (string, error) {
 		return "", c.ComposeService().Push(ctx, project)
 	})
 	return err
diff --git a/cli/cmd/compose/start.go b/cli/cmd/compose/start.go
index 75f1c666..f2290c3e 100644
--- a/cli/cmd/compose/start.go
+++ b/cli/cmd/compose/start.go
@@ -54,12 +54,7 @@ func runStart(ctx context.Context, opts startOptions, services []string) error {
 		return err
 	}
 
-	project, err := opts.toProject()
-	if err != nil {
-		return err
-	}
-
-	err = filter(project, services)
+	project, err := opts.toProject(services)
 	if err != nil {
 		return err
 	}
diff --git a/cli/cmd/compose/stop.go b/cli/cmd/compose/stop.go
index 55e91f37..f3bb29d3 100644
--- a/cli/cmd/compose/stop.go
+++ b/cli/cmd/compose/stop.go
@@ -49,16 +49,12 @@ func runStop(ctx context.Context, opts stopOptions, services []string) error {
 		return err
 	}
 
-	_, err = progress.Run(ctx, func(ctx context.Context) (string, error) {
-		project, err := opts.toProject()
-		if err != nil {
-			return "", err
-		}
+	project, err := opts.toProject(services)
+	if err != nil {
+		return err
+	}
 
-		err = filter(project, services)
-		if err != nil {
-			return "", err
-		}
+	_, err = progress.Run(ctx, func(ctx context.Context) (string, error) {
 		return "", c.ComposeService().Stop(ctx, project)
 	})
 	return err
diff --git a/cli/cmd/compose/up.go b/cli/cmd/compose/up.go
index fd51e977..358860d6 100644
--- a/cli/cmd/compose/up.go
+++ b/cli/cmd/compose/up.go
@@ -161,10 +161,11 @@ func setup(ctx context.Context, opts composeOptions, services []string) (*client
 		return nil, nil, err
 	}
 
-	project, err := opts.toProject()
+	project, err := opts.toProject(services)
 	if err != nil {
 		return nil, nil, err
 	}
+
 	if opts.DomainName != "" {
 		// arbitrarily set the domain name on the first service ; ACI backend will expose the entire project
 		project.Services[0].DomainName = opts.DomainName
@@ -175,9 +176,5 @@ func setup(ctx context.Context, opts composeOptions, services []string) (*client
 		}
 	}
 
-	err = filter(project, services)
-	if err != nil {
-		return nil, nil, err
-	}
 	return c, project, nil
 }
diff --git a/go.mod b/go.mod
index 6c7010b5..52fbfe8f 100644
--- a/go.mod
+++ b/go.mod
@@ -17,7 +17,7 @@ require (
 	github.com/awslabs/goformation/v4 v4.15.6
 	github.com/buger/goterm v0.0.0-20200322175922-2f3e71b85129
 	github.com/cnabio/cnab-to-oci v0.3.1-beta1
-	github.com/compose-spec/compose-go v0.0.0-20210128135116-292e3df05d93
+	github.com/compose-spec/compose-go v0.0.0-20210202093933-d648aac758f9
 	github.com/containerd/console v1.0.1
 	github.com/containerd/containerd v1.4.3
 	github.com/containerd/continuity v0.0.0-20200928162600-f2cc35102c2a // indirect
diff --git a/go.sum b/go.sum
index c1564321..2480a1a7 100644
--- a/go.sum
+++ b/go.sum
@@ -307,8 +307,8 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX
 github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
 github.com/codahale/hdrhistogram v0.0.0-20160425231609-f8ad88b59a58/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
 github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
-github.com/compose-spec/compose-go v0.0.0-20210128135116-292e3df05d93 h1:An81C14E6chlgVNYFzl38nNhVD71aKhUXGosteUW2a4=
-github.com/compose-spec/compose-go v0.0.0-20210128135116-292e3df05d93/go.mod h1:s53PhZODE+L0pFAsTcueMKrZE7q1F1Qtm+d4s8xJjwE=
+github.com/compose-spec/compose-go v0.0.0-20210202093933-d648aac758f9 h1:S5VA8b4LXPe9jZsOXy5KNC/2iMMcllWqW2/rOBTwnxQ=
+github.com/compose-spec/compose-go v0.0.0-20210202093933-d648aac758f9/go.mod h1:DmI+QjIXA6A/DLEanqkNEl1+Bn/ZSp9FWEuAGC8haHA=
 github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko=
 github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM=
 github.com/containerd/cgroups v0.0.0-20200710171044-318312a37340 h1:9atoWyI9RtXFwf7UDbme/6M8Ud0rFrx+Q3ZWgSnsxtw=
@@ -1018,8 +1018,8 @@ github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0Qu
 github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 github.com/mitchellh/mapstructure v1.3.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
-github.com/mitchellh/mapstructure v1.4.0 h1:7ks8ZkOP5/ujthUsT07rNv+nkLXCQWKNHuwzOAesEks=
-github.com/mitchellh/mapstructure v1.4.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
+github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
+github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
 github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A=
 github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY=
 github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
diff --git a/local/compose/attach.go b/local/compose/attach.go
index 9cccf3c5..814986d4 100644
--- a/local/compose/attach.go
+++ b/local/compose/attach.go
@@ -42,6 +42,7 @@ func (s *composeService) attach(ctx context.Context, project *types.Project, con
 	if err != nil {
 		return nil, err
 	}
+	containers = Containers(containers).filter(isService(project.ServiceNames()...))
 
 	var names []string
 	for _, c := range containers {


Test patch:
diff --git a/cli/cmd/compose/compose_test.go b/cli/cmd/compose/compose_test.go
index 49308d70..dfa68cf4 100644
--- a/cli/cmd/compose/compose_test.go
+++ b/cli/cmd/compose/compose_test.go
@@ -24,7 +24,7 @@ import (
 )
 
 func TestFilterServices(t *testing.T) {
-	p := types.Project{
+	p := &types.Project{
 		Services: []types.ServiceConfig{
 			{
 				Name:  "foo",
@@ -42,7 +42,7 @@ func TestFilterServices(t *testing.T) {
 			},
 		},
 	}
-	err := filter(&p, []string{"bar"})
+	err := p.ForServices([]string{"bar"})
 	assert.NilError(t, err)
 
 	assert.Equal(t, len(p.Services), 2)