Repository: compose-spec/compose-go
Pull request #618 (2024-04-11 02:28:20)

refs issue:11706 Resolving the Alias value of Alias type node
**Changes:**

1.  If the node is of `Alias` kind, we need to preserve the current path and process the Alias value separately.
2. By doing this, the `!overwrite ` tag won't be ignored.

**Testing:**

1. Created setup that mentioned on this issue: https://github.com/docker/compose/issues/11706
2. Results:

containers are spawned successfully:

<img width="792" alt="2024-04-11 (1)" src="https://github.com/compose-spec/compose-go/assets/57858357/33ca0471-606d-4d3e-8c3a-46ebeaba0f77">


Here, we can see in the configs section for `y` that there is only one entry for `configs`

<img width="971" alt="2024-04-11" src="https://github.com/compose-spec/compose-go/assets/57858357/28a9dd77-672e-45b6-9ff1-fbb12b21528f">

arankeparth: Please review somebody
arankeparth: @ndeloof please review.
arankeparth: @ndeloof how to get this merged?
ndeloof: @arankeparth let's wait for a second review, then I think we can merge

arankeparth: @glours please review.
arankeparth: @milas can you please review this?
ndeloof: @arankeparth please don't push pressure on the team, will be reviewed once we get bandwidth to review
arankeparth: > @arankeparth please don't push pressure on the team, will be reviewed once we get bandwidth to review

Sorry for that.

Patch:
diff --git a/loader/reset.go b/loader/reset.go
index 3c615ef..2b7f04c 100644
--- a/loader/reset.go
+++ b/loader/reset.go
@@ -19,6 +19,7 @@ package loader
 import (
 	"fmt"
 	"strconv"
+	"strings"
 
 	"github.com/compose-spec/compose-go/v2/tree"
 	"gopkg.in/yaml.v3"
@@ -40,6 +41,15 @@ func (p *ResetProcessor) UnmarshalYAML(value *yaml.Node) error {
 
 // resolveReset detects `!reset` tag being set on yaml nodes and record position in the yaml tree
 func (p *ResetProcessor) resolveReset(node *yaml.Node, path tree.Path) (*yaml.Node, error) {
+	// If the path contains "<<", removing the "<<" element and merging the path
+	if strings.Contains(path.String(), ".<<") {
+		path = tree.NewPath(strings.Replace(path.String(), ".<<", "", 1))
+	}
+	// If the node is an alias, We need to process the alias field in order to consider the !override and !reset tags
+	if node.Kind == yaml.AliasNode {
+		return p.resolveReset(node.Alias, path)
+	}
+
 	if node.Tag == "!reset" {
 		p.paths = append(p.paths, path)
 		return nil, nil


Test patch:
diff --git a/loader/loader_yaml_test.go b/loader/loader_yaml_test.go
index 75ae2e8..0f084f3 100644
--- a/loader/loader_yaml_test.go
+++ b/loader/loader_yaml_test.go
@@ -55,3 +55,52 @@ services:
 		},
 	})
 }
+
+func TestParseYAMLFilesMergeOverride(t *testing.T) {
+	model, err := loadYamlModel(context.TODO(), types.ConfigDetails{
+		ConfigFiles: []types.ConfigFile{
+			{Filename: "override.yaml",
+				Content: []byte(`
+services:
+  base:
+    configs:
+      - source: credentials
+        target: /credentials/file1
+  x: &x
+    extends:
+      base
+    configs: !override
+      - source: credentials
+        target: /literally-anywhere-else
+
+  y:
+    <<: *x
+
+configs:
+  credentials:
+    content: |
+      dummy value
+`)},
+		}}, &Options{}, &cycleTracker{}, nil)
+	assert.NilError(t, err)
+	assert.DeepEqual(t, model, map[string]interface{}{
+		"configs": map[string]interface{}{"credentials": map[string]interface{}{"content": string("dummy value\n")}},
+		"services": map[string]interface{}{
+			"base": map[string]interface{}{
+				"configs": []interface{}{
+					map[string]interface{}{"source": string("credentials"), "target": string("/credentials/file1")},
+				},
+			},
+			"x": map[string]interface{}{
+				"configs": []interface{}{
+					map[string]interface{}{"source": string("credentials"), "target": string("/literally-anywhere-else")},
+				},
+			},
+			"y": map[string]interface{}{
+				"configs": []interface{}{
+					map[string]interface{}{"source": string("credentials"), "target": string("/literally-anywhere-else")},
+				},
+			},
+		},
+	})
+}