nebius/SWE-rebench-V2-PRs huggingface dataset:
Repository: compose-spec/compose-go
Pull request #151 (2021-06-28 08:46:10)

fix `ports: []` fail to validate schema
`[]` must be converted into an empty list, not `nil` otherwise schema validation fail


Patch:
diff --git a/loader/loader.go b/loader/loader.go
index 97ce633..a9a70e7 100644
--- a/loader/loader.go
+++ b/loader/loader.go
@@ -391,7 +391,7 @@ func convertToStringKeysRecursive(value interface{}, keyPrefix string) (interfac
 		return dict, nil
 	}
 	if list, ok := value.([]interface{}); ok {
-		var convertedList []interface{}
+		convertedList := []interface{}{}
 		for index, entry := range list {
 			newKeyPrefix := fmt.Sprintf("%s[%d]", keyPrefix, index)
 			convertedEntry, err := convertToStringKeysRecursive(entry, newKeyPrefix)


Test patch:
diff --git a/loader/loader_test.go b/loader/loader_test.go
index 61db73c..5e00f2d 100644
--- a/loader/loader_test.go
+++ b/loader/loader_test.go
@@ -1743,3 +1743,13 @@ services:
 	assert.NilError(t, err)
 	assert.Equal(t, "always", svc.PullPolicy)
 }
+
+func TestEmptyList(t *testing.T) {
+	_, err := loadYAML(`
+services:
+  test:
+    image: nginx:latest
+    ports: []
+`)
+	assert.NilError(t, err)
+}
Repository: compose-spec/compose-go
Pull request #174 (2021-08-31 15:13:20)

allow services to use `x-...` as names
x-* is reserved by the compose specification for model extension, but we don't expect any extension within the top-level `services` element, and should allow users to define services with name `x-foo`

close https://github.com/docker/compose-cli/issues/2050


Patch:
diff --git a/loader/loader.go b/loader/loader.go
index d868c72..8207108 100644
--- a/loader/loader.go
+++ b/loader/loader.go
@@ -261,7 +261,7 @@ func loadSections(filename string, config map[string]interface{}, configDetails
 		return nil, err
 	}
 
-	cfg.Networks, err = LoadNetworks(getSection(config, "networks"), configDetails.Version)
+	cfg.Networks, err = LoadNetworks(getSection(config, "networks"))
 	if err != nil {
 		return nil, err
 	}
@@ -426,6 +426,14 @@ func formatInvalidKeyError(keyPrefix string, key interface{}) error {
 func LoadServices(filename string, servicesDict map[string]interface{}, workingDir string, lookupEnv template.Mapping, opts *Options) ([]types.ServiceConfig, error) {
 	var services []types.ServiceConfig
 
+	x, ok := servicesDict["extensions"]
+	if ok {
+		// as a top-level attribute, "services" doesn't support extensions, and a service can be named `x-foo`
+		for k, v := range x.(map[string]interface{}) {
+			servicesDict[k] = v
+		}
+	}
+
 	for name := range servicesDict {
 		serviceConfig, err := loadServiceWithExtends(filename, name, servicesDict, workingDir, lookupEnv, opts, &cycleTracker{})
 		if err != nil {
@@ -620,7 +628,7 @@ func transformUlimits(data interface{}) (interface{}, error) {
 
 // LoadNetworks produces a NetworkConfig map from a compose file Dict
 // the source Dict is not validated if directly used. Use Load() to enable validation
-func LoadNetworks(source map[string]interface{}, version string) (map[string]types.NetworkConfig, error) {
+func LoadNetworks(source map[string]interface{}) (map[string]types.NetworkConfig, error) {
 	networks := make(map[string]types.NetworkConfig)
 	err := Transform(source, &networks)
 	if err != nil {


Test patch:
diff --git a/loader/loader_test.go b/loader/loader_test.go
index 2197a1f..50bd31f 100644
--- a/loader/loader_test.go
+++ b/loader/loader_test.go
@@ -1362,7 +1362,7 @@ func TestLoadNetworksWarnOnDeprecatedExternalNameVersion35(t *testing.T) {
 			},
 		},
 	}
-	networks, err := LoadNetworks(source, "3.5")
+	networks, err := LoadNetworks(source)
 	assert.NilError(t, err)
 	expected := map[string]types.NetworkConfig{
 		"foo": {
@@ -1386,7 +1386,7 @@ func TestLoadNetworksWarnOnDeprecatedExternalName(t *testing.T) {
 			},
 		},
 	}
-	networks, err := LoadNetworks(source, "3.4")
+	networks, err := LoadNetworks(source)
 	assert.NilError(t, err)
 	expected := map[string]types.NetworkConfig{
 		"foo": {
Repository: compose-spec/compose-go
Pull request #176 (2021-09-01 08:03:25)

don't resolve `FOO=`, value is empty string
variable set as `FOO=` should not be resolved based on project environment, but set to empty string


This is a fix https://github.com/docker/compose-cli/issues/1989


Patch:
diff --git a/cli/options.go b/cli/options.go
index 54dedbd..7fde886 100644
--- a/cli/options.go
+++ b/cli/options.go
@@ -314,7 +314,7 @@ func ProjectFromOptions(options *ProjectOptions) (*types.Project, error) {
 		} else if nameFromEnv, ok := options.Environment[ComposeProjectName]; ok && nameFromEnv != "" {
 			opts.Name = nameFromEnv
 		} else {
-			opts.Name = regexp.MustCompile(`(?m)[a-z]+[-_a-z0-9]*`).FindString(strings.ToLower(filepath.Base(absWorkingDir)))
+			opts.Name = regexp.MustCompile(`(?m)[a-z]+[-_a-z0-9]*[a-z0-9]+`).FindString(strings.ToLower(filepath.Base(absWorkingDir)))
 		}
 		opts.Name = strings.ToLower(opts.Name)
 	}
diff --git a/types/types.go b/types/types.go
index 37238dd..ffaf607 100644
--- a/types/types.go
+++ b/types/types.go
@@ -352,7 +352,7 @@ func (e MappingWithEquals) OverrideBy(other MappingWithEquals) MappingWithEquals
 // Resolve update a MappingWithEquals for keys without value (`key`, but not `key=`)
 func (e MappingWithEquals) Resolve(lookupFn func(string) (string, bool)) MappingWithEquals {
 	for k, v := range e {
-		if v == nil || *v == "" {
+		if v == nil {
 			if value, ok := lookupFn(k); ok {
 				e[k] = &value
 			}


Test patch:
diff --git a/loader/loader_test.go b/loader/loader_test.go
index 2197a1f..fb56208 100644
--- a/loader/loader_test.go
+++ b/loader/loader_test.go
@@ -446,26 +446,29 @@ services:
     environment:
       FOO: "1"
       BAR: 2
-      BAZ: 2.5
-      QUX:
-      QUUX:
+      GA: 2.5
+      BU: ""
+      ZO:
+      MEU:
   list-env:
     image: busybox
     environment:
       - FOO=1
       - BAR=2
-      - BAZ=2.5
-      - QUX=
-      - QUUX
-`, map[string]string{"QUX": "qux"})
+      - GA=2.5
+      - BU=
+      - ZO
+      - MEU
+`, map[string]string{"MEU": "Shadoks"})
 	assert.NilError(t, err)
 
 	expected := types.MappingWithEquals{
-		"FOO":  strPtr("1"),
-		"BAR":  strPtr("2"),
-		"BAZ":  strPtr("2.5"),
-		"QUX":  strPtr("qux"),
-		"QUUX": nil,
+		"FOO": strPtr("1"),
+		"BAR": strPtr("2"),
+		"GA":  strPtr("2.5"),
+		"BU":  strPtr(""),
+		"ZO":  nil,
+		"MEU": strPtr("Shadoks"),
 	}
 
 	assert.Check(t, is.Equal(2, len(config.Services)))
Repository: compose-spec/compose-go
Pull request #179 (2021-09-09 11:57:54)

fix merging ports
fix port merge logic: align with docker-compose to use composite key `(target,publish,ip,protocol)` 
see https://github.com/docker/compose/blob/c81046aac0ce1360f1bab64368f9a318c593d82a/compose/config/types.py#L419

close https://github.com/docker/compose/issues/8557
close https://github.com/docker/compose/issues/8586


Patch:
diff --git a/loader/merge.go b/loader/merge.go
index 78af4cf..bac9dd7 100644
--- a/loader/merge.go
+++ b/loader/merge.go
@@ -147,12 +147,21 @@ func toServicePortConfigsMap(s interface{}) (map[interface{}]interface{}, error)
 		return nil, errors.Errorf("not a servicePortConfig slice: %v", s)
 	}
 	m := map[interface{}]interface{}{}
+	type port struct {
+		target    uint32
+		published uint32
+		ip        string
+		protocol  string
+	}
+
 	for _, p := range ports {
-		k := p.Published
-		if k == 0 {
-			k = p.Target
+		mergeKey := port{
+			target:    p.Target,
+			published: p.Published,
+			ip:        p.HostIP,
+			protocol:  p.Protocol,
 		}
-		m[k] = p
+		m[mergeKey] = p
 	}
 	return m, nil
 }


Test patch:
diff --git a/loader/merge_test.go b/loader/merge_test.go
index 7bc0177..035eeba 100644
--- a/loader/merge_test.go
+++ b/loader/merge_test.go
@@ -286,24 +286,30 @@ func TestLoadMultipleServicePorts(t *testing.T) {
 			},
 		},
 		{
-			name: "override_same_published",
+			name: "override_distinct_protocols",
 			portBase: map[string]interface{}{
 				"ports": []interface{}{
-					"8080:80",
+					"8080:80/tcp",
 				},
 			},
 			portOverride: map[string]interface{}{
 				"ports": []interface{}{
-					"8080:81",
+					"8080:80/udp",
 				},
 			},
 			expected: []types.ServicePortConfig{
 				{
 					Mode:      "ingress",
 					Published: 8080,
-					Target:    81,
+					Target:    80,
 					Protocol:  "tcp",
 				},
+				{
+					Mode:      "ingress",
+					Published: 8080,
+					Target:    80,
+					Protocol:  "udp",
+				},
 			},
 		},
 		{
@@ -947,6 +953,12 @@ func TestLoadMultipleConfigs(t *testing.T) {
 					},
 				},
 				Ports: []types.ServicePortConfig{
+					{
+						Mode:      "ingress",
+						Target:    80,
+						Published: 8080,
+						Protocol:  "tcp",
+					},
 					{
 						Target:    81,
 						Published: 8080,
Repository: compose-spec/compose-go
Pull request #187 (2021-09-16 13:20:29)

unbraced variable must not apply advanced substitutions
close https://github.com/docker/compose/issues/8601

aligns with https://github.com/docker/compose/blob/2011bc3cea1c78eccd0fa75d01a6b638d6bfb7c2/compose/config/interpolation.py#L96-L98


Patch:
diff --git a/template/template.go b/template/template.go
index d5ad0ae..26e9b36 100644
--- a/template/template.go
+++ b/template/template.go
@@ -25,11 +25,12 @@ import (
 )
 
 var delimiter = "\\$"
-var substitution = "[_a-z][_a-z0-9]*(?::?[-?][^}]*)?"
+var substitutionNamed = "[_a-z][_a-z0-9]*"
+var substitutionBraced = "[_a-z][_a-z0-9]*(?::?[-?][^}]*)?"
 
 var patternString = fmt.Sprintf(
 	"%s(?i:(?P<escaped>%s)|(?P<named>%s)|{(?P<braced>%s)}|(?P<invalid>))",
-	delimiter, delimiter, substitution, substitution,
+	delimiter, delimiter, substitutionNamed, substitutionBraced,
 )
 
 var defaultPattern = regexp.MustCompile(patternString)
@@ -74,9 +75,11 @@ func SubstituteWith(template string, mapping Mapping, pattern *regexp.Regexp, su
 			return escaped
 		}
 
+		braced := false
 		substitution := groups["named"]
 		if substitution == "" {
 			substitution = groups["braced"]
+			braced = true
 		}
 
 		if substitution == "" {
@@ -84,19 +87,21 @@ func SubstituteWith(template string, mapping Mapping, pattern *regexp.Regexp, su
 			return ""
 		}
 
-		for _, f := range subsFuncs {
-			var (
-				value   string
-				applied bool
-			)
-			value, applied, err = f(substitution, mapping)
-			if err != nil {
-				return ""
-			}
-			if !applied {
-				continue
+		if braced {
+			for _, f := range subsFuncs {
+				var (
+					value   string
+					applied bool
+				)
+				value, applied, err = f(substitution, mapping)
+				if err != nil {
+					return ""
+				}
+				if !applied {
+					continue
+				}
+				return value
 			}
-			return value
 		}
 
 		value, ok := mapping(substitution)


Test patch:
diff --git a/template/template_test.go b/template/template_test.go
index 3831f23..3c6b61e 100644
--- a/template/template_test.go
+++ b/template/template_test.go
@@ -64,6 +64,13 @@ func TestInvalid(t *testing.T) {
 	}
 }
 
+// see https://github.com/docker/compose/issues/8601
+func TestNonBraced(t *testing.T) {
+	substituted, err := Substitute("$FOO-bar", defaultMapping)
+	assert.NilError(t, err)
+	assert.Equal(t, substituted, "first-bar")
+}
+
 func TestNoValueNoDefault(t *testing.T) {
 	for _, template := range []string{"This ${missing} var", "This ${BAR} var"} {
 		result, err := Substitute(template, defaultMapping)
Repository: compose-spec/compose-go
Pull request #195 (2021-10-11 11:52:27)

Let Dockerfile be a relative path
https://github.com/docker/compose/issues/8760


Patch:
diff --git a/loader/loader.go b/loader/loader.go
index 57505ee..4f32aa4 100644
--- a/loader/loader.go
+++ b/loader/loader.go
@@ -450,12 +450,7 @@ func loadServiceWithExtends(filename, name string, servicesDict map[string]inter
 		return nil, err
 	}
 
-	target, ok := servicesDict[name]
-	if !ok {
-		return nil, fmt.Errorf("cannot extend service %q in %s: service not found", name, filename)
-	}
-
-	serviceConfig, err := LoadService(name, target.(map[string]interface{}), workingDir, lookupEnv, opts.ResolvePaths)
+	serviceConfig, err := LoadService(name, servicesDict[name].(map[string]interface{}), workingDir, lookupEnv, opts.ResolvePaths)
 	if err != nil {
 		return nil, err
 	}
diff --git a/loader/normalize.go b/loader/normalize.go
index 3f465b3..23851ae 100644
--- a/loader/normalize.go
+++ b/loader/normalize.go
@@ -41,6 +41,10 @@ func normalize(project *types.Project, resolvePaths bool) error {
 	}
 	project.ComposeFiles = absComposeFiles
 
+	if project.Networks == nil {
+		project.Networks = make(map[string]types.NetworkConfig)
+	}
+
 	// If not declared explicitly, Compose model involves an implicit "default" network
 	if _, ok := project.Networks["default"]; !ok {
 		project.Networks["default"] = types.NetworkConfig{}
@@ -74,7 +78,6 @@ func normalize(project *types.Project, resolvePaths bool) error {
 			if _, err := os.Stat(localContext); err == nil {
 				if resolvePaths {
 					s.Build.Context = localContext
-					s.Build.Dockerfile = absPath(localContext, s.Build.Dockerfile)
 				}
 			} else {
 				// might be a remote http/git context. Unfortunately supported "remote" syntax is highly ambiguous


Test patch:
diff --git a/loader/normalize_test.go b/loader/normalize_test.go
index dcc5858..a2e3f68 100644
--- a/loader/normalize_test.go
+++ b/loader/normalize_test.go
@@ -17,6 +17,7 @@
 package loader
 
 import (
+	"fmt"
 	"os"
 	"path/filepath"
 	"testing"
@@ -87,6 +88,41 @@ networks:
 	assert.DeepEqual(t, expected, string(marshal))
 }
 
+func TestNormalizeResolvePathsBuildContextPaths(t *testing.T) {
+	wd, _ := os.Getwd()
+	project := types.Project{
+		Name:       "myProject",
+		WorkingDir: wd,
+		Services: []types.ServiceConfig{
+			{
+				Name: "foo",
+				Build: &types.BuildConfig{
+					Context:    "./testdata",
+					Dockerfile: "Dockerfile-sample",
+				},
+				Scale: 1,
+			},
+		},
+	}
+
+	expected := fmt.Sprintf(`services:
+  foo:
+    build:
+      context: %s
+      dockerfile: Dockerfile-sample
+    networks:
+      default: null
+networks:
+  default:
+    name: myProject_default
+`, filepath.Join(wd, "testdata"))
+	err := normalize(&project, true)
+	assert.NilError(t, err)
+	marshal, err := yaml.Marshal(project)
+	assert.NilError(t, err)
+	assert.DeepEqual(t, expected, string(marshal))
+}
+
 func TestNormalizeAbsolutePaths(t *testing.T) {
 	project := types.Project{
 		Name:         "myProject",
Repository: compose-spec/compose-go
Pull request #205 (2021-11-22 07:47:13)

merge environments
use MappingWithEquals#Override to handle configuration merges

close https://github.com/docker/compose/issues/8917


Patch:
diff --git a/loader/merge.go b/loader/merge.go
index 0c7a6dd..40405e3 100644
--- a/loader/merge.go
+++ b/loader/merge.go
@@ -114,6 +114,11 @@ func _merge(baseService *types.ServiceConfig, overrideService *types.ServiceConf
 	if overrideService.Entrypoint != nil {
 		baseService.Entrypoint = overrideService.Entrypoint
 	}
+	if baseService.Environment != nil {
+		baseService.Environment.OverrideBy(overrideService.Environment)
+	} else {
+		baseService.Environment = overrideService.Environment
+	}
 	return baseService, nil
 }
 


Test patch:
diff --git a/loader/merge_test.go b/loader/merge_test.go
index 42f5f38..b96fd33 100644
--- a/loader/merge_test.go
+++ b/loader/merge_test.go
@@ -1224,3 +1224,37 @@ func TestMergeCommands(t *testing.T) {
 	assert.NilError(t, err)
 	assert.DeepEqual(t, merged.Services[0].Command, types.ShellCommand{"/bin/ash", "-c", "echo 'world'"})
 }
+
+func TestMergeEnvironments(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",
+						"environment": map[string]interface{}{
+							"NAME":  "BASE",
+							"VALUE": "BASE",
+						},
+					},
+				},
+			}},
+			{Filename: "override.yml", Config: map[string]interface{}{
+				"services": map[string]interface{}{
+					"foo": map[string]interface{}{
+						"image": "alpine",
+						"environment": map[string]interface{}{
+							"NAME":  "DEV",
+							"VALUE": nil,
+						},
+					},
+				},
+			}},
+		},
+	}
+	merged, err := loadTestProject(configDetails)
+	assert.NilError(t, err)
+	env := merged.Services[0].Environment
+	assert.Assert(t, *env["NAME"] == "DEV")
+	assert.Assert(t, env["VALUE"] == nil)
+}
Repository: compose-spec/compose-go
Pull request #218 (2022-01-12 17:38:19)

Do not set undefined inherited variables
Prior to Docker-Compose v2, inherited variables that were undefined on the host would not be defined in the container. This returns the original behavior.

This resolves https://github.com/docker/compose/issues/9031


Patch:
diff --git a/dotenv/parser.go b/dotenv/parser.go
index e657c8d..228655a 100644
--- a/dotenv/parser.go
+++ b/dotenv/parser.go
@@ -41,9 +41,9 @@ func parseBytes(src []byte, out map[string]string, lookupFn LookupFn) error {
 			value, ok := lookupFn(key)
 			if ok {
 				out[key] = value
-				cutset = left
-				continue
 			}
+			cutset = left
+			continue
 		}
 
 		value, left, err := extractVarValue(left, out, lookupFn)


Test patch:
diff --git a/dotenv/godotenv_test.go b/dotenv/godotenv_test.go
index 7fa718f..0034fa5 100644
--- a/dotenv/godotenv_test.go
+++ b/dotenv/godotenv_test.go
@@ -575,8 +575,8 @@ func TestInheritedEnvVariablSingleVar(t *testing.T) {
 
 func TestInheritedEnvVariableNotFound(t *testing.T) {
 	envMap, err := Read("fixtures/inherited-not-found.env")
-	if envMap["VARIABLE_NOT_FOUND"] != "" || err != nil {
-		t.Errorf("Expected 'VARIABLE_NOT_FOUND' to be '' with no errors")
+	if _, ok := envMap["VARIABLE_NOT_FOUND"]; ok || err != nil {
+		t.Errorf("Expected 'VARIABLE_NOT_FOUND' to be undefined with no errors")
 	}
 }
 
@@ -589,8 +589,8 @@ func TestInheritedEnvVariableNotFoundWithLookup(t *testing.T) {
 		}
 		return envVar, ok
 	}, "fixtures/inherited-not-found.env")
-	if envMap["VARIABLE_NOT_FOUND"] != "" || err != nil {
-		t.Errorf("Expected 'VARIABLE_NOT_FOUND' to be '' with no errors")
+	if _, ok := envMap["VARIABLE_NOT_FOUND"]; ok || err != nil {
+		t.Errorf("Expected 'VARIABLE_NOT_FOUND' to be undefined with no errors")
 	}
 	_, ok := notFoundMap["VARIABLE_NOT_FOUND"]
 	if !ok {
Repository: compose-spec/compose-go
Pull request #242 (2022-03-20 17:13:10)

Fix required env vars ignored except the last one
During the variable substitution, if a missing variable was followed
by an existing one, the empty "err" variable overwrote the previous
non-empty variable.

Unlike with Docker Compose v1, the first found error did not guarantee
that an actual error would be thrown.

Example which would have run before the fix even though var1 is not defined:

```yaml
services:
  bash:
    image: bash:5.0.18-alpine3.15
    environment:
      var12: "_ ${var1:?Error1} _ ${var2:?Error2} _ "
    command:
      - env
```

```bash
# var2=1 docker compose up
```

This change also means that, if multiple variables are missing in one string, the first one will be reported by Docker Compose

Signed-off-by: Ákos Takács <takacs.akos@it-sziget.hu>


Patch:
diff --git a/template/template.go b/template/template.go
index 1e497ab..7056a13 100644
--- a/template/template.go
+++ b/template/template.go
@@ -62,6 +62,7 @@ type SubstituteFunc func(string, Mapping) (string, bool, error)
 // It accepts additional substitute function.
 func SubstituteWith(template string, mapping Mapping, pattern *regexp.Regexp, subsFuncs ...SubstituteFunc) (string, error) {
 	var outerErr error
+	var returnErr error
 
 	result := pattern.ReplaceAllStringFunc(template, func(substring string) string {
 		_, subsFunc := getSubstitutionFunctionForTemplate(substring)
@@ -91,6 +92,9 @@ func SubstituteWith(template string, mapping Mapping, pattern *regexp.Regexp, su
 
 		if substitution == "" {
 			outerErr = &InvalidTemplateError{Template: template}
+			if returnErr == nil {
+				returnErr = outerErr
+			}
 			return ""
 		}
 
@@ -101,6 +105,9 @@ func SubstituteWith(template string, mapping Mapping, pattern *regexp.Regexp, su
 			)
 			value, applied, outerErr = subsFunc(substitution, mapping)
 			if outerErr != nil {
+				if returnErr == nil {
+					returnErr = outerErr
+				}
 				return ""
 			}
 			if applied {
@@ -119,7 +126,7 @@ func SubstituteWith(template string, mapping Mapping, pattern *regexp.Regexp, su
 		return value
 	})
 
-	return result, outerErr
+	return result, returnErr
 }
 
 func getSubstitutionFunctionForTemplate(template string) (string, SubstituteFunc) {


Test patch:
diff --git a/interpolation/interpolation_test.go b/interpolation/interpolation_test.go
index b67eee4..8502731 100644
--- a/interpolation/interpolation_test.go
+++ b/interpolation/interpolation_test.go
@@ -114,6 +114,7 @@ func TestValidUnexistentInterpolation(t *testing.T) {
 		{test: "{{{ ${FOO:?foo_} }}}", errMsg: "foo_"},
 		{test: "{{{ ${FOO:?foo-bar-value} }}}", errMsg: "foo-bar-value"},
 		{test: "{{{ ${FOO:?foo} ${BAR:-DEFAULT_VALUE} }}}", errMsg: "foo"},
+		{test: "${FOO:?foo} ${BAR:?bar}", errMsg: "foo"},
 		{test: "{{{ ${BAR} }}}", expected: "{{{  }}}"},
 		{test: "${FOO:?baz} }}}", errMsg: "baz"},
 		{test: "${FOO?baz} }}}", errMsg: "baz"},
Repository: compose-spec/compose-go
Pull request #256 (2022-05-02 15:41:22)

Add support of tags in the build section
implements https://github.com/compose-spec/compose-spec/pull/255


Patch:
diff --git a/loader/full-example.yml b/loader/full-example.yml
index 0a77f3a..8cd8331 100644
--- a/loader/full-example.yml
+++ b/loader/full-example.yml
@@ -22,6 +22,9 @@ services:
           uid: '103'
           gid: '103'
           mode: 0440
+      tags:
+        - foo:v1.0.0
+        - docker.io/username/foo:my-other-tag
 
 
     cap_add:
diff --git a/schema/compose-spec.json b/schema/compose-spec.json
index b3485be..a41e056 100644
--- a/schema/compose-spec.json
+++ b/schema/compose-spec.json
@@ -102,7 +102,8 @@
                 "shm_size": {"type": ["integer", "string"]},
                 "extra_hosts": {"$ref": "#/definitions/list_or_dict"},
                 "isolation": {"type": "string"},
-                "secrets": {"$ref": "#/definitions/service_config_or_secret"}
+                "secrets": {"$ref": "#/definitions/service_config_or_secret"},
+                "tags": {"type": "array", "items": {"type": "string"}}
               },
               "additionalProperties": false,
               "patternProperties": {"^x-": {}}
diff --git a/types/types.go b/types/types.go
index 597a3db..7051bf0 100644
--- a/types/types.go
+++ b/types/types.go
@@ -305,6 +305,7 @@ type BuildConfig struct {
 	Network    string                `yaml:",omitempty" json:"network,omitempty"`
 	Target     string                `yaml:",omitempty" json:"target,omitempty"`
 	Secrets    []ServiceSecretConfig `yaml:",omitempty" json:"secrets,omitempty"`
+	Tags       StringList            `mapstructure:"tags" yaml:"tags,omitempty" json:"tags,omitempty"`
 
 	Extensions map[string]interface{} `yaml:",inline" json:"-"`
 }


Test patch:
diff --git a/loader/full-struct_test.go b/loader/full-struct_test.go
index 0161518..6438b36 100644
--- a/loader/full-struct_test.go
+++ b/loader/full-struct_test.go
@@ -70,6 +70,7 @@ func services(workingDir, homeDir string) []types.ServiceConfig {
 						Mode:   uint32Ptr(0440),
 					},
 				},
+				Tags: []string{"foo:v1.0.0", "docker.io/username/foo:my-other-tag"},
 			},
 			CapAdd:       []string{"ALL"},
 			CapDrop:      []string{"NET_ADMIN", "SYS_ADMIN"},
@@ -598,6 +599,9 @@ services:
         uid: "103"
         gid: "103"
         mode: 288
+      tags:
+      - foo:v1.0.0
+      - docker.io/username/foo:my-other-tag
     cap_add:
     - ALL
     cap_drop:
@@ -1127,6 +1131,10 @@ func fullExampleJSON(workingDir, homeDir string) string {
             "gid": "103",
             "mode": 288
           }
+        ],
+        "tags": [
+          "foo:v1.0.0",
+          "docker.io/username/foo:my-other-tag"
         ]
       },
       "cap_add": [