@@ -28,11 +28,12 @@ import (
28
28
type ResetProcessor struct {
29
29
target interface {}
30
30
paths []tree.Path
31
- visitedNodes map [* yaml.Node ]string
31
+ visitedNodes map [* yaml.Node ][] string
32
32
}
33
33
34
34
// UnmarshalYAML implement yaml.Unmarshaler
35
35
func (p * ResetProcessor ) UnmarshalYAML (value * yaml.Node ) error {
36
+ p .visitedNodes = make (map [* yaml.Node ][]string )
36
37
resolved , err := p .resolveReset (value , tree .NewPath ())
37
38
p .visitedNodes = nil
38
39
if err != nil {
@@ -144,20 +145,46 @@ func (p *ResetProcessor) applyNullOverrides(target any, path tree.Path) error {
144
145
}
145
146
146
147
func (p * ResetProcessor ) checkForCycle (node * yaml.Node , path tree.Path ) error {
147
- if p .visitedNodes == nil {
148
- p .visitedNodes = make (map [* yaml.Node ]string )
149
- }
148
+ paths := p .visitedNodes [node ]
149
+ pathStr := path .String ()
150
150
151
- // Check for cycle by seeing if the node has already been visited at this path
152
- if previousPath , found := p .visitedNodes [node ]; found {
153
- // If the current node has been visited, we have a cycle if the previous path is a prefix
154
- if strings .HasPrefix (path .String (), strings .TrimRight (previousPath , "<<" )) {
155
- return fmt .Errorf ("cycle detected at path: %s" , previousPath )
151
+ for _ , prevPath := range paths {
152
+ // If we're visiting the exact same path, it's not a cycle
153
+ if pathStr == prevPath {
154
+ continue
156
155
}
157
- }
158
156
159
- // Mark the current node as visited
160
- p .visitedNodes [node ] = path .String ()
157
+ // If either path is using a merge key, it's legitimate YAML merging
158
+ if strings .Contains (prevPath , "<<" ) || strings .Contains (pathStr , "<<" ) {
159
+ continue
160
+ }
161
161
162
+ // Only consider it a cycle if one path is contained within the other
163
+ // and they're not in different service definitions
164
+ if (strings .HasPrefix (pathStr , prevPath + "." ) ||
165
+ strings .HasPrefix (prevPath , pathStr + "." )) &&
166
+ ! areInDifferentServices (pathStr , prevPath ) {
167
+ return fmt .Errorf ("cycle detected: node at path %s references node at path %s" , pathStr , prevPath )
168
+ }
169
+ }
170
+
171
+ p .visitedNodes [node ] = append (paths , pathStr )
162
172
return nil
163
173
}
174
+
175
+ // areInDifferentServices checks if two paths are in different service definitions
176
+ func areInDifferentServices (path1 , path2 string ) bool {
177
+ // Split paths into components
178
+ parts1 := strings .Split (path1 , "." )
179
+ parts2 := strings .Split (path2 , "." )
180
+
181
+ // Look for the services component and compare the service names
182
+ for i := 0 ; i < len (parts1 ) && i < len (parts2 ); i ++ {
183
+ if parts1 [i ] == "services" && i + 1 < len (parts1 ) &&
184
+ parts2 [i ] == "services" && i + 1 < len (parts2 ) {
185
+ // If they're different services, it's not a cycle
186
+ return parts1 [i + 1 ] != parts2 [i + 1 ]
187
+ }
188
+ }
189
+ return false
190
+ }
0 commit comments