Skip to content

Conversation

@austinamorusoyardstick
Copy link
Owner

This pull request introduces a new update-checking feature to the application, alongside various code quality improvements and minor fixes. The most notable changes include the addition of an UpdateChecker component, integration of this feature into the UI, and the introduction of a new keybinding for triggering update checks. Additionally, there are minor refactorings and fixes for consistency and readability.

New Feature: Update Checking

  • Added the UpdateChecker component to manage background update checks, including methods for checking updates, determining commits behind, and triggering immediate checks (app/update_checker.go).
  • Integrated the UpdateChecker into the home struct and initialized it in the newHome function (app/app.go) [1] [2] [3].
  • Added a new keybinding (U) to trigger immediate update checks and updated the keybinding configuration (keys/keys.go) [1] [2] [3] [4] [5] [6].
  • Enhanced the Menu component to display an update indicator when updates are available (ui/menu.go) [1] [2] [3].

Code Quality Improvements

  • Refactored anonymous User structs in session/git/pr_comments.go for better readability and maintainability [1] [2] [3].
  • Cleaned up unused imports and redundant lines across several files, such as ui/jest.go and ui/markdown.go [1] [2] [3] [4] [5] [6] [7].

Minor Fixes

  • Fixed alignment and formatting inconsistencies in session/storage.go, session/instance.go, and other files [1] [2].
  • Updated the build tag in test_suggestion.go for clarity.

These changes collectively improve the application's functionality, maintainability, and user experience.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary of Changes

Hello @austinamorusoyardstick, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

I've implemented a new update-checking feature for the application, which includes a dedicated UpdateChecker component, its integration into the user interface, and a new keybinding for manual checks. Alongside this new functionality, I've also focused on improving overall code quality by refactoring existing structures and performing extensive cleanup of formatting and unused code.

Highlights

  • New Update Checking Feature: I've added a new UpdateChecker component that handles both background and on-demand checks for application updates. This component is integrated into the main application logic.
  • UI Integration for Updates: The application's Menu component now visually indicates when an update is available, providing a clear signal to users.
  • New Keybinding for Updates: Users can now trigger an immediate update check using a new 'U' keybinding, enhancing control over the update process.
  • Code Structure Refinements: I've refactored anonymous User structs in session/git/pr_comments.go to named structs, which improves code readability and maintainability.
  • Whitespace and Import Cleanup: I've performed a comprehensive cleanup across numerous files, removing unused imports and fixing minor formatting inconsistencies, particularly extraneous tab characters.
  • Markdown Rendering Improvements: I've significantly enhanced the markdown rendering capabilities in ui/markdown.go, leading to a more accurate and visually appealing display of headers, lists, code blocks, tables, and inline formatting.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in issue comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments or fill out our survey to provide feedback.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@austinamorusoyardstick
Copy link
Owner Author

/gemini review

@austinamorusoyardstick austinamorusoyardstick merged commit 428c47c into main Jul 29, 2025
1 check passed
Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new update-checking feature, which is a great addition for keeping users informed. The implementation is well-integrated into the existing application structure. The PR also includes a significant number of code quality improvements across many files, mainly by removing extra whitespace and improving formatting, which enhances readability. My review focuses on a couple of areas in the new app/update_checker.go file to improve its robustness and adherence to Go best practices.

Comment on lines +214 to +227
count := 0
if _, err := strings.NewReader(strings.TrimSpace(string(output))).Read([]byte{}); err == nil {
// Parse the count
if n, _ := strings.CutSuffix(strings.TrimSpace(string(output)), "\n"); n != "" {
// Simple atoi implementation
for _, c := range n {
if c >= '0' && c <= '9' {
count = count*10 + int(c-'0')
}
}
}
}

return count, nil

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This custom implementation for parsing an integer from the command output is unnecessarily complex and less robust than the standard library's strconv.Atoi. The check for a non-empty string is also convoluted. Using strconv.Atoi simplifies the code, improves readability, and provides proper error handling. You'll need to add "strconv" to your imports.

	countStr := strings.TrimSpace(string(output))
	if countStr == "" {
		return 0, nil
	}

	count, err := strconv.Atoi(countStr)
	if err != nil {
		log.WarningLog.Printf("could not parse commit count from output '%s': %v", countStr, err)
		return 0, nil
	}

	return count, nil

Comment on lines +187 to +204
func getMainBranch(gitRoot string) string {
// Try to get the default branch from remote
cmd := exec.Command("sh", "-c", "git -C "+gitRoot+" remote show origin | sed -n '/HEAD branch/s/.*: //p'")
output, err := cmd.Output()
if err == nil && len(output) > 0 {
return strings.TrimSpace(string(output))
}

// Fallback to common defaults
for _, branch := range []string{"main", "master"} {
cmd := exec.Command("git", "-C", gitRoot, "rev-parse", "--verify", "origin/"+branch)
if err := cmd.Run(); err == nil {
return branch
}
}

return "main"
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The use of sh -c to determine the main branch name is not robust and can be platform-dependent. It also introduces a potential for shell injection if gitRoot were ever to come from an untrusted source. A more idiomatic and safer approach in Go is to execute commands directly without a shell wrapper. You could use git symbolic-ref refs/remotes/origin/HEAD to get the default branch name more reliably.

func getMainBranch(gitRoot string) string {
	// Try to get the default branch from remote using symbolic-ref
	cmd := exec.Command("git", "-C", gitRoot, "symbolic-ref", "refs/remotes/origin/HEAD")
	output, err := cmd.Output()
	if err == nil {
		// Output is typically 'refs/remotes/origin/main', we need to extract 'main'
		if parts := strings.Split(strings.TrimSpace(string(output)), "/"); len(parts) > 0 {
			return parts[len(parts)-1]
		}
	}

	// Fallback to common defaults
	for _, branch := range []string{"main", "master"} {
		cmd := exec.Command("git", "-C", gitRoot, "rev-parse", "--verify", "origin/"+branch)
		if err := cmd.Run(); err == nil {
			return branch
		}
	}

	return "main"
}

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a valuable update-checking feature and includes several code quality improvements. The core logic for the new feature in app/update_checker.go is a good start, but it relies heavily on shelling out to external commands, which can be fragile. I've provided specific suggestions to make these shell commands more robust and portable.

The most critical issue is in the new getCommitCount function, which contains a buggy parsing implementation that can fail silently. I've provided a much simpler and more correct version using the standard library.

The other changes are mostly stylistic cleanups and are well-executed, improving the overall readability of the codebase.

Comment on lines +207 to +227
func getCommitCount(gitRoot, ref string) (int, error) {
cmd := exec.Command("git", "-C", gitRoot, "rev-list", "--count", ref)
output, err := cmd.Output()
if err != nil {
return 0, err
}

count := 0
if _, err := strings.NewReader(strings.TrimSpace(string(output))).Read([]byte{}); err == nil {
// Parse the count
if n, _ := strings.CutSuffix(strings.TrimSpace(string(output)), "\n"); n != "" {
// Simple atoi implementation
for _, c := range n {
if c >= '0' && c <= '9' {
count = count*10 + int(c-'0')
}
}
}
}

return count, nil

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The integer parsing logic in getCommitCount is overly complex and buggy. It re-implements atoi and uses a confusing method to check for an empty string. This can lead to silent failures if the git command returns non-numeric output. This can be simplified and made more robust using the standard library's strconv.Atoi.

func getCommitCount(gitRoot, ref string) (int, error) {
	cmd := exec.Command("git", "-C", gitRoot, "rev-list", "--count", ref)
	output, err := cmd.Output()
	if err != nil {
		return 0, err
	}

	countStr := strings.TrimSpace(string(output))
	if countStr == "" {
		return 0, nil
	}

	count, err := strconv.Atoi(countStr)
	if err != nil {
		return 0, fmt.Errorf("failed to parse commit count '%s': %w", countStr, err)
	}

	return count, nil
}

Comment on lines +187 to +193
func getMainBranch(gitRoot string) string {
// Try to get the default branch from remote
cmd := exec.Command("sh", "-c", "git -C "+gitRoot+" remote show origin | sed -n '/HEAD branch/s/.*: //p'")
output, err := cmd.Output()
if err == nil && len(output) > 0 {
return strings.TrimSpace(string(output))
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The current implementation for getting the main branch name by shelling out to sh and using sed is fragile and not portable across different operating systems. It depends on the user having sh and sed available and on the specific output format of git remote show origin.

A more robust and portable approach is to use git symbolic-ref refs/remotes/origin/HEAD, which directly provides the default remote branch.

Suggested change
func getMainBranch(gitRoot string) string {
// Try to get the default branch from remote
cmd := exec.Command("sh", "-c", "git -C "+gitRoot+" remote show origin | sed -n '/HEAD branch/s/.*: //p'")
output, err := cmd.Output()
if err == nil && len(output) > 0 {
return strings.TrimSpace(string(output))
}
func getMainBranch(gitRoot string) string {
cmd := exec.Command("git", "-C", gitRoot, "symbolic-ref", "refs/remotes/origin/HEAD")
output, err := cmd.Output()
if err == nil {
refName := strings.TrimSpace(string(output))
if parts := strings.Split(refName, "/"); len(parts) > 0 {
return parts[len(parts)-1]
}
}

Comment on lines +47 to +59
func (uc *UpdateChecker) StartBackgroundCheck() {
go func() {
// Do initial check
uc.checkForUpdates()

// Set up periodic checking
ticker := time.NewTicker(uc.checkInterval)
defer ticker.Stop()

for range ticker.C {
uc.checkForUpdates()
}
}()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

StartBackgroundCheck starts a goroutine with a time.NewTicker that runs indefinitely. However, there is no corresponding Stop method to halt this goroutine. If an UpdateChecker instance is ever replaced or no longer needed, this goroutine will leak.

It's a good practice to provide a way to clean up long-running goroutines. Consider adding a done channel to the UpdateChecker struct and a Stop() method to signal the goroutine to exit.

Comment on lines +167 to +183
func findGitRoot(startPath string) string {
current := startPath
for current != "/" && current != "" {
cmd := exec.Command("git", "-C", current, "rev-parse", "--show-toplevel")
output, err := cmd.Output()
if err == nil {
return strings.TrimSpace(string(output))
}

// Go up one directory
if idx := strings.LastIndex(current, "/"); idx > 0 {
current = current[:idx]
} else {
break
}
}
return ""

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The findGitRoot function manually walks up the directory tree using string manipulation on paths, which is not portable (e.g., current != "/" and strings.LastIndex(current, "/") assume Unix-style paths). This can be made more robust and cross-platform by using the path/filepath package.

Suggested change
func findGitRoot(startPath string) string {
current := startPath
for current != "/" && current != "" {
cmd := exec.Command("git", "-C", current, "rev-parse", "--show-toplevel")
output, err := cmd.Output()
if err == nil {
return strings.TrimSpace(string(output))
}
// Go up one directory
if idx := strings.LastIndex(current, "/"); idx > 0 {
current = current[:idx]
} else {
break
}
}
return ""
func findGitRoot(startPath string) string {
current, err := filepath.Abs(startPath)
if err != nil {
return ""
}
for {
cmd := exec.Command("git", "-C", current, "rev-parse", "--show-toplevel")
if output, err := cmd.Output(); err == nil {
return strings.TrimSpace(string(output))
}
parent := filepath.Dir(current)
if parent == current {
return ""
}
current = parent
}
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant