@@ -17,6 +17,7 @@ limitations under the License.
1717package alphaupdate
1818
1919import (
20+ "bytes"
2021 "fmt"
2122 "io"
2223 "net/http"
@@ -101,9 +102,14 @@ var _ = Describe("kubebuilder", func() {
101102 })
102103
103104 It ("should update project from v4.5.2 to v4.6.0 without conflicts" , func () {
105+ By ("running alpha update from v4.5.2 to v4.6.0" )
104106 runAlphaUpdate (kbc .Dir , kbc , toVersion , false )
107+
108+ By ("checking that custom code is preserved" )
105109 validateCustomCodePreservation (kbc .Dir )
106- validateConflictMarkers (kbc .Dir , false )
110+
111+ By ("checking that no conflict markers are present in the project files" )
112+ Expect (hasConflictMarkers (kbc .Dir )).To (BeFalse ())
107113
108114 By ("checking that go module is upgraded" )
109115 validateCommonGoModule (kbc .Dir )
@@ -113,14 +119,52 @@ var _ = Describe("kubebuilder", func() {
113119 })
114120
115121 It ("should update project from v4.5.2 to v4.7.0 with --force flag and create conflict markers" , func () {
122+ By ("modifying original Makefile to use CONTROLLER_TOOLS_VERSION v0.17.3" )
123+ modifyMakefileControllerTools (kbc .Dir , "v0.17.3" )
124+
116125 By ("running alpha update to v4.7.0 with --force flag" )
117126 runAlphaUpdate (kbc .Dir , kbc , toVersionWithConflict , true )
118127
119- By ("checking that markers for conflicts are present " )
120- validateConflictMarkers (kbc .Dir , true )
128+ By ("checking that custom code is preserved " )
129+ validateCustomCodePreservation (kbc .Dir )
121130
122- By ("checking that go module is upgraded" )
131+ By ("checking that conflict markers are present in the project files" )
132+ Expect (hasConflictMarkers (kbc .Dir )).To (BeTrue ())
133+
134+ By ("checking that go module is upgraded to expected versions" )
123135 validateCommonGoModule (kbc .Dir )
136+
137+ By ("checking that Makefile is updated and has conflict between old and new versions in Makefile" )
138+ makefilePath := filepath .Join (kbc .Dir , "Makefile" )
139+ content , err := os .ReadFile (makefilePath )
140+ Expect (err ).NotTo (HaveOccurred (), "Failed to read Makefile after update" )
141+ makefileStr := string (content )
142+
143+ // Should update to the new version
144+ Expect (makefileStr ).To (ContainSubstring (`GOLANGCI_LINT_VERSION ?= v2.1.6` ))
145+
146+ // The original project was scaffolded with v0.17.2 (from v4.5.2).
147+ // The user manually updated it to v0.17.3.
148+ // The target upgrade version (v4.7.0) introduces v0.18.0.
149+ //
150+ // Because both the user's version (v0.17.3) and the scaffold version (v0.18.0) differ,
151+ // we expect Git to insert conflict markers around this line in the Makefile:
152+ //
153+ // <<<<<<< HEAD
154+ // CONTROLLER_TOOLS_VERSION ?= v0.18.0
155+ // =======
156+ // CONTROLLER_TOOLS_VERSION ?= v0.17.3
157+ // >>>>>>> tmp-original-*
158+ Expect (makefileStr ).To (ContainSubstring ("<<<<<<<" ),
159+ "Expected conflict marker <<<<<<< in Makefile" )
160+ Expect (makefileStr ).To (ContainSubstring ("=======" ),
161+ "Expected conflict separator ======= in Makefile" )
162+ Expect (makefileStr ).To (ContainSubstring (">>>>>>>" ),
163+ "Expected conflict marker >>>>>>> in Makefile" )
164+ Expect (makefileStr ).To (ContainSubstring ("CONTROLLER_TOOLS_VERSION ?= v0.17.3" ),
165+ "Expected original user version in conflict" )
166+ Expect (makefileStr ).To (ContainSubstring ("CONTROLLER_TOOLS_VERSION ?= v0.18.0" ),
167+ "Expected latest scaffold version in conflict" )
124168 })
125169
126170 It ("should stop when updating the project from v4.5.2 to v4.7.0 without the flag force" , func () {
@@ -130,6 +174,9 @@ var _ = Describe("kubebuilder", func() {
130174 By ("validating that merge stopped with conflicts requiring manual resolution" )
131175 validateConflictState (kbc .Dir )
132176
177+ By ("checking that custom code is preserved" )
178+ validateCustomCodePreservation (kbc .Dir )
179+
133180 By ("checking that go module is upgraded" )
134181 validateCommonGoModule (kbc .Dir )
135182 })
@@ -147,6 +194,28 @@ var _ = Describe("kubebuilder", func() {
147194 })
148195})
149196
197+ func modifyMakefileControllerTools (projectDir , newVersion string ) {
198+ makefilePath := filepath .Join (projectDir , "Makefile" )
199+ oldLine := "CONTROLLER_TOOLS_VERSION ?= v0.17.2"
200+ newLine := fmt .Sprintf ("CONTROLLER_TOOLS_VERSION ?= %s" , newVersion )
201+
202+ By ("replacing the controller-tools version in the Makefile" )
203+ Expect (util .ReplaceInFile (makefilePath , oldLine , newLine )).
204+ To (Succeed (), "Failed to update CONTROLLER_TOOLS_VERSION in Makefile" )
205+
206+ By ("committing the Makefile change to simulate user customization" )
207+ cmds := [][]string {
208+ {"git" , "add" , "Makefile" },
209+ {"git" , "commit" , "-m" , fmt .Sprintf ("User modified CONTROLLER_TOOLS_VERSION to %s" , newVersion )},
210+ }
211+ for _ , args := range cmds {
212+ cmd := exec .Command (args [0 ], args [1 :]... )
213+ cmd .Dir = projectDir
214+ output , err := cmd .CombinedOutput ()
215+ Expect (err ).NotTo (HaveOccurred (), fmt .Sprintf ("Git command failed: %s" , output ))
216+ }
217+ }
218+
150219func validateMakefileContent (projectDir string ) {
151220 makefilePath := filepath .Join (projectDir , "Makefile" )
152221 content , err := os .ReadFile (makefilePath )
@@ -283,34 +352,37 @@ func validateCustomCodePreservation(projectDir string) {
283352 Expect (string (controllerContent )).To (ContainSubstring (controllerImplementation ))
284353}
285354
286- func validateConflictMarkers (projectDir string , expectMarkers bool ) {
287- files := []string {
288- filepath .Join (projectDir , "api" , "v1" , "testoperator_types.go" ),
289- filepath .Join (projectDir , "internal" , "controller" , "testoperator_controller.go" ),
290- }
291- found := false
292- for _ , file := range files {
293- content , err := os .ReadFile (file )
294- if err != nil {
295- continue
355+ func hasConflictMarkers (projectDir string ) bool {
356+ hasMarker := false
357+
358+ err := filepath .Walk (projectDir , func (path string , info os.FileInfo , err error ) error {
359+ if err != nil || info .IsDir () {
360+ return nil
361+ }
362+
363+ content , readErr := os .ReadFile (path )
364+ if readErr != nil || bytes .Contains (content , []byte {0 }) {
365+ return nil // skip unreadable or binary files
296366 }
367+
297368 if strings .Contains (string (content ), "<<<<<<<" ) {
298- found = true
299- break
369+ hasMarker = true
370+ return fmt . Errorf ( "conflict marker found in %s" , path ) // short-circuit early
300371 }
372+ return nil
373+ })
374+
375+ if err != nil && hasMarker {
376+ return true
301377 }
302- if expectMarkers {
303- Expect (found ).To (BeTrue ())
304- } else {
305- Expect (found ).To (BeFalse ())
306- }
378+ return false
307379}
308380
309381func validateConflictState (projectDir string ) {
310382 By ("validating merge stopped with conflicts requiring manual resolution" )
311383
312384 // 1. Check file contents for conflict markers
313- validateConflictMarkers ( projectDir , true )
385+ Expect ( hasConflictMarkers ( projectDir )). To ( BeTrue () )
314386
315387 // 2. Check Git status for conflict-tracked files (UU = both modified)
316388 cmd := exec .Command ("git" , "status" , "--porcelain" )
0 commit comments