@@ -28,11 +28,12 @@ import (
2828type ResetProcessor struct {
2929 target interface {}
3030 paths []tree.Path
31- visitedNodes map [* yaml.Node ]string
31+ visitedNodes map [* yaml.Node ][] string
3232}
3333
3434// UnmarshalYAML implement yaml.Unmarshaler
3535func (p * ResetProcessor ) UnmarshalYAML (value * yaml.Node ) error {
36+ p .visitedNodes = make (map [* yaml.Node ][]string )
3637 resolved , err := p .resolveReset (value , tree .NewPath ())
3738 p .visitedNodes = nil
3839 if err != nil {
@@ -144,20 +145,46 @@ func (p *ResetProcessor) applyNullOverrides(target any, path tree.Path) error {
144145}
145146
146147func (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 ()
150150
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
156155 }
157- }
158156
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+ }
161161
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 )
162172 return nil
163173}
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