Skip to content

Commit 74631e0

Browse files
(alpha update): Add command to generate AI info
1 parent f56402f commit 74631e0

File tree

3 files changed

+111
-0
lines changed

3 files changed

+111
-0
lines changed

pkg/cli/alpha/internal/update/update.go

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
274319
func (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, "\nConflicted [SOURCE] files:\n%s\n", bulletList(srcFiles))
380+
fmt.Fprintf(&ctx, "\nConflicted [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,

pkg/cli/alpha/internal/update/validate.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,23 @@ func (opts *Update) Validate() error {
5757
}
5858
}
5959

60+
if len(opts.UseGhModels) > 0 && !isGhModelsExtensionInstalled() {
61+
return fmt.Errorf("gh-models extension is not installed. To install the extension, run: " +
62+
"gh extension install https://github.com/github/gh-models")
63+
}
64+
6065
return nil
6166
}
6267

68+
// isGhModelsExtensionInstalled checks if the gh-models extension is installed
69+
func isGhModelsExtensionInstalled() bool {
70+
cmd := exec.Command("gh", "extension", "list")
71+
if _, err := cmd.Output(); err != nil {
72+
return false
73+
}
74+
return true
75+
}
76+
6377
// validateGitRepo verifies if the current directory is a valid Git repository and checks for uncommitted changes.
6478
func (opts *Update) validateGitRepo() error {
6579
log.Info("Checking if is a git repository")

pkg/cli/alpha/update.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,8 @@ Defaults:
145145
"Push the output branch to the remote repository after the update.")
146146
updateCmd.Flags().BoolVar(&opts.OpenGhIssue, "open-gh-issue", false,
147147
"Create a GitHub issue with a pre-filled checklist and compare link after the update completes (requires `gh`).")
148+
updateCmd.Flags().BoolVar(&opts.UseGhModels, "--use-gh-models", false,
149+
"Create a GitHub issue with a pre-filled checklist and compare link after the update completes (requires `gh`).")
148150
updateCmd.Flags().StringArrayVar(
149151
&gitCfg,
150152
"git-config",

0 commit comments

Comments
 (0)