nebius/SWE-rebench-V2-PRs huggingface dataset:
Repository: compose-spec/compose-go
Pull request #56 (2020-07-06 13:44:56)

Fix project name generated after working dir:  take absolute path
Otherwise we end up with an empty name in case of “.” Working dir


Patch:
diff --git a/cli/options.go b/cli/options.go
index 8600dd3..baaa885 100644
--- a/cli/options.go
+++ b/cli/options.go
@@ -127,6 +127,10 @@ func ProjectFromOptions(options *ProjectOptions) (*types.Project, error) {
 	if err != nil {
 		return nil, err
 	}
+	absWorkingDir, err := filepath.Abs(workingDir)
+	if err != nil {
+		return nil, err
+	}
 
 	return loader.Load(types.ConfigDetails{
 		ConfigFiles: configs,
@@ -139,7 +143,7 @@ func ProjectFromOptions(options *ProjectOptions) (*types.Project, error) {
 			opts.Name = nameFromEnv
 		} else {
 			opts.Name = regexp.MustCompile(`[^a-z0-9\\-_]+`).
-				ReplaceAllString(strings.ToLower(filepath.Base(workingDir)), "")
+				ReplaceAllString(strings.ToLower(filepath.Base(absWorkingDir)), "")
 		}
 	})
 }


Test patch:
diff --git a/cli/options_test.go b/cli/options_test.go
index d90574a..9cc8aac 100644
--- a/cli/options_test.go
+++ b/cli/options_test.go
@@ -31,6 +31,13 @@ func TestProjectName(t *testing.T) {
 	assert.NilError(t, err)
 	assert.Equal(t, p.Name, "my_project")
 
+	p, err = ProjectFromOptions(&ProjectOptions{
+		WorkingDir:  ".",
+		ConfigPaths: []string{"testdata/simple/compose.yaml"},
+	})
+	assert.NilError(t, err)
+	assert.Equal(t, p.Name, "cli")
+
 	p, err = ProjectFromOptions(&ProjectOptions{
 		ConfigPaths: []string{"testdata/simple/compose.yaml"},
 	})
Repository: compose-spec/compose-go
Pull request #74 (2020-09-07 08:09:55)

support x-* extension in transformed types
Some types get special handling during the loading process to manage short vs long syntax
when we process so, we need to invoke `groupXFieldsIntoExtensions` to populate the `Extensions` field from all `x-*` elements.


Patch:
diff --git a/loader/loader.go b/loader/loader.go
index c7ef137..e174626 100644
--- a/loader/loader.go
+++ b/loader/loader.go
@@ -746,7 +746,7 @@ var transformServicePort TransformerFunc = func(data interface{}) (interface{},
 					ports = append(ports, v)
 				}
 			case map[string]interface{}:
-				ports = append(ports, value)
+				ports = append(ports, groupXFieldsIntoExtensions(value))
 			default:
 				return data, errors.Errorf("invalid type %T for port", value)
 			}
@@ -762,7 +762,7 @@ var transformStringSourceMap TransformerFunc = func(data interface{}) (interface
 	case string:
 		return map[string]interface{}{"source": value}, nil
 	case map[string]interface{}:
-		return data, nil
+		return groupXFieldsIntoExtensions(data.(map[string]interface{})), nil
 	default:
 		return data, errors.Errorf("invalid type %T for secret", value)
 	}
@@ -773,7 +773,7 @@ var transformBuildConfig TransformerFunc = func(data interface{}) (interface{},
 	case string:
 		return map[string]interface{}{"context": value}, nil
 	case map[string]interface{}:
-		return data, nil
+		return groupXFieldsIntoExtensions(data.(map[string]interface{})), nil
 	default:
 		return data, errors.Errorf("invalid type %T for service build", value)
 	}
@@ -792,7 +792,7 @@ var transformDependsOnConfig TransformerFunc = func(data interface{}) (interface
 		}
 		return transformed, nil
 	case map[string]interface{}:
-		return data, nil
+		return groupXFieldsIntoExtensions(data.(map[string]interface{})), nil
 	default:
 		return data, errors.Errorf("invalid type %T for service depends_on", value)
 	}
@@ -803,7 +803,7 @@ var transformServiceVolumeConfig TransformerFunc = func(data interface{}) (inter
 	case string:
 		return ParseVolume(value)
 	case map[string]interface{}:
-		return data, nil
+		return groupXFieldsIntoExtensions(data.(map[string]interface{})), nil
 	default:
 		return data, errors.Errorf("invalid type %T for service volume", value)
 	}


Test patch:
diff --git a/loader/loader_test.go b/loader/loader_test.go
index 86ef8bb..fec44c0 100644
--- a/loader/loader_test.go
+++ b/loader/loader_test.go
@@ -548,6 +548,7 @@ services:
       - "34567"
       - target: $theint
         published: $theint
+        x-foo-bar: true
     ulimits:
       nproc: $theint
       nofile:
@@ -637,7 +638,7 @@ networks:
 				Ports: []types.ServicePortConfig{
 					{Target: 555, Mode: "ingress", Protocol: "tcp"},
 					{Target: 34567, Mode: "ingress", Protocol: "tcp"},
-					{Target: 555, Published: 555},
+					{Target: 555, Published: 555, Extensions: map[string]interface{}{"x-foo-bar": true}},
 				},
 				Ulimits: map[string]*types.UlimitsConfig{
 					"nproc":  {Single: 555},
Repository: compose-spec/compose-go
Pull request #85 (2020-11-26 17:11:26)

Fix project named derived from working dir : same as docker-compose
Keep same transformation as in docker-compose: https://github.com/docker/compose/blob/master/compose/cli/command.py#L176

Esp. Allow “-“ in project name

Signed-off-by: Guillaume Tardif <guillaume.tardif@docker.com>


Patch:
diff --git a/cli/options.go b/cli/options.go
index dfecf98..03a5711 100644
--- a/cli/options.go
+++ b/cli/options.go
@@ -177,7 +177,7 @@ func ProjectFromOptions(options *ProjectOptions) (*types.Project, error) {
 		} else if nameFromEnv, ok := os.LookupEnv(ComposeProjectName); ok {
 			opts.Name = nameFromEnv
 		} else {
-			opts.Name = regexp.MustCompile(`[^a-z0-9\\-_]+`).
+			opts.Name = regexp.MustCompile(`[^-_a-z0-9]+`).
 				ReplaceAllString(strings.ToLower(filepath.Base(absWorkingDir)), "")
 		}
 	}


Test patch:
diff --git a/cli/options_test.go b/cli/options_test.go
index 6d7656e..48bfa56 100644
--- a/cli/options_test.go
+++ b/cli/options_test.go
@@ -110,6 +110,16 @@ func TestProjectWithDiscardEnvFile(t *testing.T) {
 	assert.Assert(t, service.EnvFile == nil)
 }
 
+func TestProjectNameFromWorkingDir(t *testing.T) {
+	opts, err := NewProjectOptions([]string{
+		"testdata/env-file/compose-with-env-file.yaml",
+	})
+	assert.NilError(t, err)
+	p, err := ProjectFromOptions(opts)
+	assert.NilError(t, err)
+	assert.Equal(t, p.Name, "env-file")
+}
+
 func TestEnvMap(t *testing.T) {
 	m := map[string]string{}
 	m["foo"] = "bar"
Repository: compose-spec/compose-go
Pull request #111 (2021-02-24 07:58:57)

always create a "default" network if not set by user
docker-compose creates a "default" network in addition to the declared ones
see https://github.com/docker/compose/blob/2a4aca7f54ca6982b8d3f0c236ce22d7f7e90a08/compose/network.py#L256-L257

This will fix https://github.com/docker/compose-cli/issues/1308


Patch:
diff --git a/loader/normalize.go b/loader/normalize.go
index 73e85b6..871ebfa 100644
--- a/loader/normalize.go
+++ b/loader/normalize.go
@@ -27,8 +27,8 @@ import (
 
 // normalize compose project by moving deprecated attributes to their canonical position and injecting implicit defaults
 func normalize(project *types.Project) error {
-	// If none defined, Compose model involves an implicit "default" network
-	if len(project.Networks) == 0 {
+	// If not declared explicitly, Compose model involves an implicit "default" network
+	if _, ok := project.Networks["default"]; !ok {
 		project.Networks["default"] = types.NetworkConfig{}
 	}
 


Test patch:
diff --git a/loader/normalize_test.go b/loader/normalize_test.go
index 817bcf6..a448ab6 100644
--- a/loader/normalize_test.go
+++ b/loader/normalize_test.go
@@ -41,6 +41,7 @@ func TestNormalizeNetworkNames(t *testing.T) {
 	expected := types.Project{
 		Name: "myProject",
 		Networks: types.Networks{
+			"default": {Name: "myProject_default"},
 			"myExternalnet": {
 				Name:     "myExternalnet",
 				External: types.External{External: true},
Repository: compose-spec/compose-go
Pull request #112 (2021-03-02 15:53:04)

introduce func to build Mapping from a set of KEY=VALUE strings


Patch:
diff --git a/types/types.go b/types/types.go
index 8bb7650..7bcfcee 100644
--- a/types/types.go
+++ b/types/types.go
@@ -262,6 +262,20 @@ type StringOrNumberList []string
 // For the key without value (`key`), the mapped value is set to nil.
 type MappingWithEquals map[string]*string
 
+// NewMappingWithEquals build a new Mapping from a set of KEY=VALUE strings
+func NewMappingWithEquals(values []string) MappingWithEquals {
+	mapping := MappingWithEquals{}
+	for _, env := range values {
+		tokens := strings.SplitN(env, "=", 2)
+		if len(tokens) > 1 {
+			mapping[tokens[0]] = &tokens[1]
+		} else {
+			mapping[env] = nil
+		}
+	}
+	return mapping
+}
+
 // OverrideBy update MappingWithEquals with values from another MappingWithEquals
 func (e MappingWithEquals) OverrideBy(other MappingWithEquals) MappingWithEquals {
 	for k, v := range other {
@@ -298,6 +312,22 @@ func (e MappingWithEquals) RemoveEmpty() MappingWithEquals {
 // mapped value is set to an empty string `""`.
 type Mapping map[string]string
 
+// NewMapping build a new Mapping from a set of KEY=VALUE strings
+func NewMapping(values []string) Mapping {
+	mapping := Mapping{}
+	for _, value := range values {
+		parts := strings.SplitN(value, "=", 2)
+		key := parts[0]
+		switch {
+		case len(parts) == 1:
+			mapping[key] = ""
+		default:
+			mapping[key] = parts[1]
+		}
+	}
+	return mapping
+}
+
 // Labels is a mapping type for labels
 type Labels map[string]string
 


Test patch:
diff --git a/types/types_test.go b/types/types_test.go
index 143ad8d..60d6864 100644
--- a/types/types_test.go
+++ b/types/types_test.go
@@ -235,3 +235,22 @@ func TestExtension(t *testing.T) {
 	assert.NilError(t, err)
 	assert.Check(t, ok == false)
 }
+
+func TestNewMapping(t *testing.T) {
+	m := NewMapping([]string{
+		"FOO=BAR",
+		"ZOT=",
+		"QIX",
+	})
+	mw := NewMappingWithEquals([]string{
+		"FOO=BAR",
+		"ZOT=",
+		"QIX",
+	})
+	assert.Check(t, m["FOO"] == "BAR")
+	assert.Check(t, m["ZOT"] == "")
+	assert.Check(t, m["QIX"] == "")
+	assert.Check(t, *mw["FOO"] == "BAR")
+	assert.Check(t, *mw["ZOT"] == "")
+	assert.Check(t, mw["QIX"] == nil)
+}
Repository: compose-spec/compose-go
Pull request #115 (2021-03-16 09:33:42)

resolve COMPOSE_PROJECT_NAME from configured environment sources
see https://github.com/docker/compose-cli/issues/1430

as sources for environment variable can be set with options (WithDotEnv, WithEnvFile, WithOsEnv) this also must be used to lookup COMPOSE_PROJECT_NAME assigning project name

includes updated test case


Patch:
diff --git a/cli/options.go b/cli/options.go
index 6aeee71..e2c083f 100644
--- a/cli/options.go
+++ b/cli/options.go
@@ -189,7 +189,7 @@ func ProjectFromOptions(options *ProjectOptions) (*types.Project, error) {
 	var nameLoadOpt = func(opts *loader.Options) {
 		if options.Name != "" {
 			opts.Name = options.Name
-		} else if nameFromEnv, ok := os.LookupEnv(ComposeProjectName); ok {
+		} else if nameFromEnv, ok := options.Environment[ComposeProjectName]; ok {
 			opts.Name = nameFromEnv
 		} else {
 			opts.Name = regexp.MustCompile(`[^-_a-z0-9]+`).


Test patch:
diff --git a/cli/options_test.go b/cli/options_test.go
index 4e99c6f..fb2dafd 100644
--- a/cli/options_test.go
+++ b/cli/options_test.go
@@ -50,6 +50,13 @@ func TestProjectName(t *testing.T) {
 	p, err = ProjectFromOptions(opts)
 	assert.NilError(t, err)
 	assert.Equal(t, p.Name, "my_project_from_env")
+
+	opts, err = NewProjectOptions([]string{"testdata/simple/compose.yaml"}, WithDotEnv)
+	assert.NilError(t, err)
+	p, err = ProjectFromOptions(opts)
+	assert.NilError(t, err)
+	assert.Equal(t, p.Name, "my_project_from_dot_env")
+
 }
 
 func TestProjectFromSetOfFiles(t *testing.T) {
diff --git a/cli/testdata/simple/.env b/cli/testdata/simple/.env
index 9f295a4..7997544 100644
--- a/cli/testdata/simple/.env
+++ b/cli/testdata/simple/.env
@@ -1,1 +1,2 @@
-PUBLIC_PORT=8000
\ No newline at end of file
+PUBLIC_PORT=8000
+COMPOSE_PROJECT_NAME=my_project_from_dot_env
\ No newline at end of file
Repository: compose-spec/compose-go
Pull request #127 (2021-04-22 07:17:26)

shm_size can be set as pain int or unitByte notation
shm_size can be set both as plain int or a string with byte notation

fix https://github.com/docker/compose-cli/issues/1565

gtardif: why this CI error that's not related to the current change ?? 

Patch:
diff --git a/compatibility/services.go b/compatibility/services.go
index 88f2fa7..39bca64 100644
--- a/compatibility/services.go
+++ b/compatibility/services.go
@@ -668,8 +668,8 @@ func (c *AllowList) CheckSecurityOpt(service *types.ServiceConfig) {
 }
 
 func (c *AllowList) CheckShmSize(service *types.ServiceConfig) {
-	if !c.supported("services.shm_size") && service.ShmSize != "" {
-		service.ShmSize = ""
+	if !c.supported("services.shm_size") && service.ShmSize != 0 {
+		service.ShmSize = 0
 		c.Unsupported("services.shm_size")
 	}
 }
diff --git a/types/types.go b/types/types.go
index 3d445c2..752911a 100644
--- a/types/types.go
+++ b/types/types.go
@@ -155,7 +155,7 @@ type ServiceConfig struct {
 	Scale           int                              `yaml:",omitempty" json:"scale,omitempty"`
 	Secrets         []ServiceSecretConfig            `yaml:",omitempty" json:"secrets,omitempty"`
 	SecurityOpt     []string                         `mapstructure:"security_opt" yaml:"security_opt,omitempty" json:"security_opt,omitempty"`
-	ShmSize         string                           `mapstructure:"shm_size" yaml:"shm_size,omitempty" json:"shm_size,omitempty"`
+	ShmSize         UnitBytes                        `mapstructure:"shm_size" yaml:"shm_size,omitempty" json:"shm_size,omitempty"`
 	StdinOpen       bool                             `mapstructure:"stdin_open" yaml:"stdin_open,omitempty" json:"stdin_open,omitempty"`
 	StopGracePeriod *Duration                        `mapstructure:"stop_grace_period" yaml:"stop_grace_period,omitempty" json:"stop_grace_period,omitempty"`
 	StopSignal      string                           `mapstructure:"stop_signal" yaml:"stop_signal,omitempty" json:"stop_signal,omitempty"`


Test patch:
diff --git a/loader/loader_test.go b/loader/loader_test.go
index 7917586..4ded0e0 100644
--- a/loader/loader_test.go
+++ b/loader/loader_test.go
@@ -556,6 +556,7 @@ services:
         soft: $theint
     privileged: $thebool
     read_only: $thebool
+    shm_size: 2gb
     stdin_open: ${thebool}
     tty: $thebool
     volumes:
@@ -647,6 +648,7 @@ networks:
 				},
 				Privileged: true,
 				ReadOnly:   true,
+				ShmSize:    types.UnitBytes(2 * 1024 * 1024 * 1024),
 				StdinOpen:  true,
 				Tty:        true,
 				Volumes: []types.ServiceVolumeConfig{
Repository: compose-spec/compose-go
Pull request #133 (2021-04-29 14:05:01)

whenever backed by a ShellCommand, service.command must NOT merge WithAppendSlice
service.Command is implemented as a `type ShellCommand []sring` and so mergo applies `WithAppendSlice` policy, which isn't relevant in this case.

close https://github.com/docker/compose-cli/issues/1601


Patch:
diff --git a/.dependabot/config.yml b/.dependabot/config.yml
new file mode 100644
index 0000000..fc63862
--- /dev/null
+++ b/.dependabot/config.yml
@@ -0,0 +1,5 @@
+version: 1
+update_configs:
+  - package_manager: "go:modules"
+    directory: "/"
+    update_schedule: "weekly"
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
deleted file mode 100644
index 1ed403b..0000000
--- a/.github/dependabot.yml
+++ /dev/null
@@ -1,15 +0,0 @@
-version: 2
-updates:
-- package-ecosystem: gomod
-  directory: "/"
-  schedule:
-    interval: weekly
-  open-pull-requests-limit: 10
-  ignore:
-  - dependency-name: github.com/sirupsen/logrus
-    versions:
-    - 1.8.0
-    - 1.8.1
-  - dependency-name: github.com/google/go-cmp
-    versions:
-    - 0.5.5
diff --git a/loader/merge.go b/loader/merge.go
index 9e13cd4..3743841 100644
--- a/loader/merge.go
+++ b/loader/merge.go
@@ -89,6 +89,9 @@ func mergeServices(base, override []types.ServiceConfig) ([]types.ServiceConfig,
 			if err := mergo.Merge(&baseService, &overrideService, mergo.WithAppendSlice, mergo.WithOverride, mergo.WithTransformers(serviceSpecials)); err != nil {
 				return base, errors.Wrapf(err, "cannot merge service %s", name)
 			}
+			if len(overrideService.Command) > 0 {
+				baseService.Command = overrideService.Command
+			}
 			baseServices[name] = baseService
 			continue
 		}


Test patch:
diff --git a/loader/merge_test.go b/loader/merge_test.go
index 07966cc..1e9ea04 100644
--- a/loader/merge_test.go
+++ b/loader/merge_test.go
@@ -1147,3 +1147,29 @@ func TestMergeTopLevelExtensions(t *testing.T) {
 		},
 	}, config)
 }
+
+func TestMergeCommands(t *testing.T) {
+	configDetails := types.ConfigDetails{
+		ConfigFiles: []types.ConfigFile{
+			{Filename: "base.yml", Config: map[string]interface{}{
+				"services": map[string]interface{}{
+					"foo": map[string]interface{}{
+						"image":   "alpine",
+						"command": "/bin/bash -c \"echo 'hello'\"",
+					},
+				},
+			}},
+			{Filename: "override.yml", Config: map[string]interface{}{
+				"services": map[string]interface{}{
+					"foo": map[string]interface{}{
+						"image":   "alpine",
+						"command": "/bin/ash -c \"echo 'world'\"",
+					},
+				},
+			}},
+		},
+	}
+	merged, err := loadTestProject(configDetails)
+	assert.NilError(t, err)
+	assert.DeepEqual(t, merged.Services[0].Command, types.ShellCommand{"/bin/ash", "-c", "echo 'world'"})
+}
Repository: compose-spec/compose-go
Pull request #140 (2021-05-17 07:20:49)

introduce WithConfigFileEnv to get config file paths from environment
`WithConfigFileEnv` passed to `NewProjectOptions` will load config file if `COMPOSE_FILE` is set in user's environment.

see https://github.com/docker/compose-cli/issues/1668


Patch:
diff --git a/cli/options.go b/cli/options.go
index 3aa2cfc..2c1d79c 100644
--- a/cli/options.go
+++ b/cli/options.go
@@ -82,6 +82,24 @@ func WithWorkingDirectory(wd string) ProjectOptionsFn {
 	}
 }
 
+// WithConfigFileEnv allow to set compose config file paths by COMPOSE_FILE environment variable
+func WithConfigFileEnv(o *ProjectOptions) error {
+	if len(o.ConfigPaths) > 0 {
+		return nil
+	}
+	sep := o.Environment[ComposeFileSeparator]
+	if sep == "" {
+		sep = string(os.PathListSeparator)
+	}
+	f, ok := o.Environment[ComposeFilePath]
+	if ok {
+		paths, err := absolutePaths(strings.Split(f, sep), o.WorkingDir)
+		o.ConfigPaths = paths
+		return err
+	}
+	return nil
+}
+
 // WithDefaultConfigPath searches for default config files from working directory
 func WithDefaultConfigPath(o *ProjectOptions) error {
 	if len(o.ConfigPaths) > 0 {
@@ -285,15 +303,6 @@ func getConfigPathsFromOptions(options *ProjectOptions) ([]string, error) {
 		return absolutePaths(options.ConfigPaths, pwd)
 	}
 
-	sep := options.Environment[ComposeFileSeparator]
-	if sep == "" {
-		sep = string(os.PathListSeparator)
-	}
-	f := options.Environment[ComposeFilePath]
-	if f != "" {
-		return absolutePaths(strings.Split(f, sep), pwd)
-	}
-
 	return nil, errors.New("no configuration file provided")
 }
 


Test patch:
diff --git a/cli/options_test.go b/cli/options_test.go
index 1b0e3f1..2142204 100644
--- a/cli/options_test.go
+++ b/cli/options_test.go
@@ -60,7 +60,7 @@ func TestProjectName(t *testing.T) {
 	})
 
 	t.Run("by .env", func(t *testing.T) {
-		opts, err := NewProjectOptions(nil, WithWorkingDirectory("testdata/env-file"), WithDotEnv)
+		opts, err := NewProjectOptions(nil, WithWorkingDirectory("testdata/env-file"), WithDotEnv, WithConfigFileEnv)
 		assert.NilError(t, err)
 		p, err := ProjectFromOptions(opts)
 		assert.NilError(t, err)
Repository: compose-spec/compose-go
Pull request #144 (2021-06-09 10:28:55)

Add restart policy constants
Signed-off-by: Timo Walter <twalter@walter-se.com>

This PR adds restart policy constants that will eliminate the string literals for the `Restart` values.


Patch:
diff --git a/types/types.go b/types/types.go
index 02c62a9..608b3a5 100644
--- a/types/types.go
+++ b/types/types.go
@@ -214,6 +214,17 @@ const (
 	PullPolicyBuild = "build"
 )
 
+const (
+	//RestartPolicyAlways always restart the container if it stops
+	RestartPolicyAlways = "always"
+	//RestartPolicyOnFailure restart the container if it exits due to an error
+	RestartPolicyOnFailure = "on-failure"
+	//RestartPolicyNo do not automatically restart the container
+	RestartPolicyNo = "no"
+	//RestartPolicyUnlessStopped always restart the container unless the container is stopped (manually or otherwise)
+	RestartPolicyUnlessStopped = "unless-stopped"
+)
+
 const (
 	// NetworkModeServicePrefix is the prefix for network_mode pointing to a service
 	NetworkModeServicePrefix = "service:"


Test patch:
diff --git a/loader/full-struct_test.go b/loader/full-struct_test.go
index af9807f..a4e0e14 100644
--- a/loader/full-struct_test.go
+++ b/loader/full-struct_test.go
@@ -123,7 +123,7 @@ func services(workingDir, homeDir string) []types.ServiceConfig {
 					},
 				},
 				RestartPolicy: &types.RestartPolicy{
-					Condition:   "on-failure",
+					Condition:   types.RestartPolicyOnFailure,
 					Delay:       durationPtr(5 * time.Second),
 					MaxAttempts: uint64Ptr(3),
 					Window:      durationPtr(2 * time.Minute),
@@ -357,7 +357,7 @@ func services(workingDir, homeDir string) []types.ServiceConfig {
 			},
 			Privileged: true,
 			ReadOnly:   true,
-			Restart:    "always",
+			Restart:    types.RestartPolicyAlways,
 			Secrets: []types.ServiceSecretConfig{
 				{
 					Source: "secret1",
Repository: compose-spec/compose-go
Pull request #149 (2021-06-23 08:51:35)

drop unnecessary resources from project model
This allows to remove any resource (network, volume, ...) that is not required by the active set of services, including the implicit "default" network when services are explicitly bound to declared networks


close https://github.com/docker/compose-cli/issues/1756

Proof of usability: https://github.com/ndeloof/compose-cli/runs/2893113262


Patch:
diff --git a/types/project.go b/types/project.go
index 43c9400..3a65eef 100644
--- a/types/project.go
+++ b/types/project.go
@@ -240,6 +240,55 @@ func (p *Project) ApplyProfiles(profiles []string) {
 	p.DisabledServices = disabled
 }
 
+// WithoutUnnecessaryResources drops networks/volumes/secrets/configs that are not referenced by active services
+func (p *Project) WithoutUnnecessaryResources() {
+	requiredNetworks := map[string]struct{}{}
+	requiredVolumes := map[string]struct{}{}
+	requiredSecrets := map[string]struct{}{}
+	requiredConfigs := map[string]struct{}{}
+	for _, s := range p.Services {
+		for k := range s.Networks {
+			requiredNetworks[k] = struct{}{}
+		}
+		for _, v := range s.Volumes {
+			if v.Type != VolumeTypeVolume || v.Source == "" {
+				continue
+			}
+			requiredVolumes[v.Source] = struct{}{}
+		}
+		for _, v := range s.Secrets {
+			requiredSecrets[v.Source] = struct{}{}
+		}
+		for _, v := range s.Configs {
+			requiredConfigs[v.Source] = struct{}{}
+		}
+	}
+
+	networks := Networks{}
+	for k := range requiredNetworks {
+		networks[k] = p.Networks[k]
+	}
+	p.Networks = networks
+
+	volumes := Volumes{}
+	for k := range requiredVolumes {
+		volumes[k] = p.Volumes[k]
+	}
+	p.Volumes = volumes
+
+	secrets := Secrets{}
+	for k := range requiredSecrets {
+		secrets[k] = p.Secrets[k]
+	}
+	p.Secrets = secrets
+
+	configs := Configs{}
+	for k := range requiredConfigs {
+		configs[k] = p.Configs[k]
+	}
+	p.Configs = configs
+}
+
 // ForServices restrict the project model to a subset of services
 func (p *Project) ForServices(names []string) error {
 	if len(names) == 0 {


Test patch:
diff --git a/types/project_test.go b/types/project_test.go
index b103731..203c7a0 100644
--- a/types/project_test.go
+++ b/types/project_test.go
@@ -35,6 +35,27 @@ func Test_ApplyProfiles(t *testing.T) {
 	assert.Equal(t, p.DisabledServices[0].Name, "service_3")
 }
 
+func Test_WithoutUnnecessaryResources(t *testing.T) {
+	p := makeProject()
+	p.Networks["unused"] = NetworkConfig{}
+	p.Volumes["unused"] = VolumeConfig{}
+	p.Secrets["unused"] = SecretConfig{}
+	p.Configs["unused"] = ConfigObjConfig{}
+	p.WithoutUnnecessaryResources()
+	if _, ok := p.Networks["unused"]; ok {
+		t.Fail()
+	}
+	if _, ok := p.Volumes["unused"]; ok {
+		t.Fail()
+	}
+	if _, ok := p.Secrets["unused"]; ok {
+		t.Fail()
+	}
+	if _, ok := p.Configs["unused"]; ok {
+		t.Fail()
+	}
+}
+
 func Test_NoProfiles(t *testing.T) {
 	p := makeProject()
 	p.ApplyProfiles(nil)
@@ -75,6 +96,10 @@ func makeProject() Project {
 				Name:     "service_3",
 				Profiles: []string{"bar"},
 			}),
+		Networks: Networks{},
+		Volumes:  Volumes{},
+		Secrets:  Secrets{},
+		Configs:  Configs{},
 	}
 }