Repository: compose-spec/compose-go
Pull request #321 (2022-11-24 12:44:16)

merge exposes, heathchecks.test
closes https://github.com/docker/compose/issues/9756


Patch:
diff --git a/loader/merge.go b/loader/merge.go
index bf10cf7..ce7c1dc 100644
--- a/loader/merge.go
+++ b/loader/merge.go
@@ -119,6 +119,9 @@ func _merge(baseService *types.ServiceConfig, overrideService *types.ServiceConf
 	if overrideService.Command != nil {
 		baseService.Command = overrideService.Command
 	}
+	if overrideService.HealthCheck != nil {
+		baseService.HealthCheck.Test = overrideService.HealthCheck.Test
+	}
 	if overrideService.Entrypoint != nil {
 		baseService.Entrypoint = overrideService.Entrypoint
 	}
@@ -127,9 +130,26 @@ func _merge(baseService *types.ServiceConfig, overrideService *types.ServiceConf
 	} else {
 		baseService.Environment = overrideService.Environment
 	}
+	baseService.Expose = unique(baseService.Expose)
 	return baseService, nil
 }
 
+func unique(slice []string) []string {
+	if slice == nil {
+		return nil
+	}
+	uniqMap := make(map[string]struct{})
+	for _, v := range slice {
+		uniqMap[v] = struct{}{}
+	}
+
+	uniqSlice := make([]string, 0, len(uniqMap))
+	for v := range uniqMap {
+		uniqSlice = append(uniqSlice, v)
+	}
+	return uniqSlice
+}
+
 func toServiceSecretConfigsMap(s interface{}) (map[interface{}]interface{}, error) {
 	secrets, ok := s.([]types.ServiceSecretConfig)
 	if !ok {
@@ -299,7 +319,7 @@ func mergeLoggingConfig(dst, src reflect.Value) error {
 	return nil
 }
 
-//nolint: unparam
+// nolint: unparam
 func mergeUlimitsConfig(dst, src reflect.Value) error {
 	if src.Interface() != reflect.Zero(reflect.TypeOf(src.Interface())).Interface() {
 		dst.Elem().Set(src.Elem())
@@ -307,7 +327,7 @@ func mergeUlimitsConfig(dst, src reflect.Value) error {
 	return nil
 }
 
-//nolint: unparam
+// nolint: unparam
 func mergeServiceNetworkConfig(dst, src reflect.Value) error {
 	if src.Interface() != reflect.Zero(reflect.TypeOf(src.Interface())).Interface() {
 		dst.Elem().FieldByName("Aliases").Set(src.Elem().FieldByName("Aliases"))


Test patch:
diff --git a/loader/merge_test.go b/loader/merge_test.go
index 329b084..4f173b1 100644
--- a/loader/merge_test.go
+++ b/loader/merge_test.go
@@ -884,6 +884,9 @@ func TestLoadMultipleConfigs(t *testing.T) {
 					"8080:80",
 					"9090:90",
 				},
+				"expose": []interface{}{
+					"8080",
+				},
 				"labels": []interface{}{
 					"foo=bar",
 				},
@@ -916,6 +919,9 @@ func TestLoadMultipleConfigs(t *testing.T) {
 						"published": 8080,
 					},
 				},
+				"expose": []interface{}{
+					"8080",
+				},
 				"labels": map[string]interface{}{
 					"foo": "baz",
 				},
@@ -963,6 +969,7 @@ func TestLoadMultipleConfigs(t *testing.T) {
 						"password": strPtr("secret"),
 					},
 				},
+				Expose: []string{"8080"},
 				Ports: []types.ServicePortConfig{
 					{
 						Mode:      "ingress",
@@ -1223,6 +1230,36 @@ func TestMergeCommands(t *testing.T) {
 	assert.DeepEqual(t, merged.Services[0].Command, types.ShellCommand{"/bin/ash", "-c", "echo 'world'"})
 }
 
+func TestMergeHealthCheck(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",
+						"healthcheck": map[string]interface{}{
+							"test": []interface{}{"CMD", "original"},
+						},
+					},
+				},
+			}},
+			{Filename: "override.yml", Config: map[string]interface{}{
+				"services": map[string]interface{}{
+					"foo": map[string]interface{}{
+						"image": "alpine",
+						"healthcheck": map[string]interface{}{
+							"test": []interface{}{"CMD", "override"},
+						},
+					},
+				},
+			}},
+		},
+	}
+	merged, err := loadTestProject(configDetails)
+	assert.NilError(t, err)
+	assert.DeepEqual(t, merged.Services[0].HealthCheck.Test, types.HealthCheckTest{"CMD", "override"})
+}
+
 func TestMergeEnvironments(t *testing.T) {
 	configDetails := types.ConfigDetails{
 		ConfigFiles: []types.ConfigFile{