@@ -77,6 +77,8 @@ type Update struct {
7777 // CLI (`gh`) to be installed and authenticated in the local environment.
7878 OpenGhIssue bool
7979
80+ UseGhModels string
81+
8082 // GitConfig holds per-invocation Git settings applied to every `git` command via
8183 // `git -c key=value`.
8284 //
@@ -271,6 +273,49 @@ func (opts *Update) Update() error {
271273 return nil
272274}
273275
276+ // isGeneratedKB returns true for Kubebuilder-generated artifacts.
277+ func isGeneratedKB (path string ) bool {
278+ return strings .Contains (path , "/zz_generated." ) ||
279+ strings .HasPrefix (path , "config/crd/bases/" ) ||
280+ strings .HasSuffix (path , "/bindata.go" )
281+ }
282+
283+ // listConflictFiles finds files that contain conflict markers and
284+ // returns them split into SOURCE vs GENERATED.
285+ func listConflictFiles () (src []string , gen []string ) {
286+ out , _ := exec .Command ("sh" , "-c" , `git grep -nE '^(<<<<<<< |>>>>>>> |=======$)' || true` ).Output ()
287+ seen := map [string ]bool {}
288+ lines := strings .Split (string (out ), "\n " )
289+ for _ , ln := range lines {
290+ if ln == "" {
291+ continue
292+ }
293+ // path:line:rest...
294+ parts := strings .SplitN (ln , ":" , 2 )
295+ if len (parts ) == 0 {
296+ continue
297+ }
298+ path := strings .TrimSpace (parts [0 ])
299+ if path == "" || seen [path ] {
300+ continue
301+ }
302+ seen [path ] = true
303+ if isGeneratedKB (path ) {
304+ gen = append (gen , path )
305+ } else {
306+ src = append (src , path )
307+ }
308+ }
309+ return
310+ }
311+
312+ func bulletList (items []string ) string {
313+ if len (items ) == 0 {
314+ return "<none>"
315+ }
316+ return "- " + strings .Join (items , "\n - " )
317+ }
318+
274319func (opts * Update ) openGitHubIssue (hasConflicts bool ) error {
275320 log .Info ("Creating GitHub Issue to track the need to update the project" )
276321 out := opts .getOutputBranchName ()
@@ -314,6 +359,56 @@ func (opts *Update) openGitHubIssue(hasConflicts bool) error {
314359 )
315360 }
316361
362+ if hasConflicts && len (opts .UseGhModels ) > 0 {
363+ srcFiles , genFiles := listConflictFiles ()
364+ releaseURL := fmt .Sprintf ("https://github.com/kubernetes-sigs/kubebuilder/releases/tag/%s" , opts .ToVersion )
365+
366+ var ctx strings.Builder
367+ ctx .WriteString ("PROJECT: Created with Kubebuilder\n " )
368+ fmt .Fprintf (& ctx , "Upgrade: from %s to %s\n " , opts .FromVersion , opts .ToVersion )
369+ fmt .Fprintf (& ctx , "Release notes: %s\n \n " , releaseURL )
370+ ctx .WriteString (`RULES (concise):
371+ - [GENERATED] api/**/zz_generated.*.go → this one you solve by running: make generate
372+ - [GENERATED] config/crd/bases/*.yaml → fix types in api/*_types.go, then run: make manifests
373+ - After regeneration run: make fmt vet lint-fix
374+ - [SOURCE] (api/*_types.go, controllers/*, webhooks/*): keep local fields/markers; adopt new scaffold markers/tags; fix imports/API changes.
375+ - Never hand-edit generated files; resolve in sources then regenerate.
376+ OUTPUT: Short bullet list of recommendations grouped by file, then the final command sequence.
377+ ` + "\n " )
378+
379+ fmt .Fprintf (& ctx , "\n Conflicted [SOURCE] files:\n %s\n " , bulletList (srcFiles ))
380+ fmt .Fprintf (& ctx , "\n Conflicted [GENERATED] files:\n %s\n " , bulletList (genFiles ))
381+
382+ prompt := `You are a senior Go/K8s engineer which is upgrading a project build with Kubebulder. Using the stdin context (Kubebuilder rules, versions, conflicted files):
383+ - For each [GENERATED] file: say "this one you solve by running make generate" (zz_generated) or "fix types then make manifests" (CRDs).
384+ - For [SOURCE]: give 1–3 specific keep/edit/remove steps.
385+ - End with: make manifests generate fmt vet lint-fix (and suggest make install if CRDs/webhooks changed).
386+ - Add: "To know more about this change see the release notes: <link>". Keep it concise.
387+ - Use a friendly, professional tone.
388+ - Output in markdown format
389+ - Do not mention the number of files, only list them.
390+ - Do not add info that is not helpful and keep it short
391+ - For go mod changes, suggested the upper versions and recommend: "run go mod tidy"`
392+
393+ // 3) Call the model with stdin context
394+ var summary string
395+ modelCmd := exec .Command ("gh" , "models" , "run" , "openai/gpt-4o" , prompt )
396+ modelCmd .Stdin = strings .NewReader (ctx .String ())
397+ modelOut , err := modelCmd .CombinedOutput ()
398+ if err != nil {
399+ log .Warn ("gh models run failed" , "error" , err , "output" , string (modelOut ))
400+ } else {
401+ summary = strings .TrimSpace (string (modelOut ))
402+ }
403+
404+ // 4) Append AI summary to the issue body
405+ if len (summary ) > 0 {
406+ body += "\n \n ## TL'DR: (AI-generate) Conflicts Resolution:\n \n " + summary + "\n "
407+ } else {
408+ log .Warn ("gh models run failed" , "output" , string (modelOut ))
409+ }
410+ }
411+
317412 issueCmd := exec .Command ("gh" , "issue" , "create" ,
318413 "--title" , title ,
319414 "--body" , body ,
0 commit comments