Skip to content

Conversation

@amargiovanni
Copy link
Contributor

No description provided.

… 004)

Add interactive menu [A/S/O/L/Q] when repos.txt is missing or empty:
- [A] Analyze ALL accessible repositories
- [S] Specify repository names manually (owner/repo format)
- [O] Analyze organization repositories
- [L] Select from list by number (e.g., 1,3,5 or 1-3)
- [Q] Quit/Skip GitHub analysis

Implementation includes:
- GitHubClient.list_user_repos() with pagination support
- GitHubClient.list_org_repos() with pagination support
- validate_repo_format() and validate_org_name() helpers
- format_repo_list() for numbered display
- load_github_repos_from_file() for repos.txt parsing
- Rate limit handling with wait time display
- EOF/KeyboardInterrupt graceful handling
- Non-interactive mode support (--quiet flag)

Tests: 727 passed (24 new GitHub selection tests)
The select_github_repos() function was implemented but not integrated into
main(). This caused the interactive menu to never appear when repos.txt was
missing/empty.

Changes:
- Replace load_repositories() with select_github_repos() in main()
- Convert returned repo names (strings) to Repository objects
- Update unit tests to mock select_github_repos instead of load_repositories
- Add required github_token and repos_file attributes to mock configs
GitHub allows repository names that start with a period (e.g., .github,
.dotfiles). Updated REPO_COMPONENT_PATTERN to accept names starting with
alphanumeric OR period.

This fixes the "Invalid repository name format" error when selecting
repositories from the GitHub API.
@gemini-code-assist
Copy link

Summary of Changes

Hello @amargiovanni, 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!

This pull request delivers a significant enhancement to the GitHub analysis tool by introducing an interactive repository selection mechanism. This feature improves user experience by providing flexible options for choosing repositories directly from the command line, aligning with the UX pattern of existing Jira project selection. The changes are backed by a comprehensive design and testing process, ensuring a robust and user-friendly addition.

Highlights

  • Interactive GitHub Repository Selection: Introduced a new interactive command-line interface for selecting GitHub repositories when the repos.txt file is missing or empty. Users can now choose from options like analyzing all personal repositories, manually specifying repository names, selecting from an organization's repositories, or picking from a numbered list.
  • New GitHub API Integrations: Added list_user_repos and list_org_repos methods to the GitHubClient to fetch personal and organizational repositories, respectively. These methods leverage existing pagination and rate limit handling mechanisms.
  • Comprehensive Specification and Planning: A complete set of specification documents has been added, including detailed user stories, functional and non-functional requirements, edge case handling, data models, API contracts, implementation plans, and quickstart guides. This ensures a well-defined and thoroughly planned feature.
  • Enhanced Input Validation: Implemented robust validation for GitHub repository names (owner/repo format) and organization names, including regex patterns and error handling for invalid inputs. Repository name patterns were also updated to allow components to start with a period.
  • Extensive Test Coverage: Added numerous unit and integration tests to cover the new interactive selection logic, API client methods, and various user interaction scenarios, ensuring the reliability and correctness of the feature.
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 by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

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 pull request 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. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

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.

@amargiovanni amargiovanni merged commit d5ad6d4 into main Nov 29, 2025
1 of 5 checks passed
@amargiovanni amargiovanni deleted the 004-github-repo-selection branch November 29, 2025 07:18
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 significant and well-designed feature for interactive GitHub repository selection. The changes are thoroughly documented with specifications, contracts, and checklists, which is excellent practice. The implementation is mostly solid, with comprehensive test coverage for the new functionality. I've identified a high-severity bug where user configuration is ignored during API calls, a medium-severity issue with brittle error handling, and a style violation. Once these are addressed, this will be a great addition to the tool.

Comment on lines +554 to +606
def select_github_repos(
repos_file: str,
github_token: str,
interactive: bool = True,
output: TerminalOutput | None = None,
) -> list[str]:
"""Select GitHub repositories from file or interactively (Feature 004).
Per spec FR-001 to FR-014:
- Display interactive menu when repos.txt is missing or empty
- Options: [A] All personal, [S] Specify manually, [O] Organization,
[L] Select from list, [Q] Quit/Skip
- Follow select_jira_projects pattern for UX consistency (FR-003)
Args:
repos_file: Path to repos.txt file.
github_token: GitHub API token for API calls.
interactive: If True, prompt user when file is missing/empty.
If False (--quiet mode), skip prompts per FR-013.
output: Optional TerminalOutput for consistent logging.
Returns:
List of repository names (owner/repo format) to analyze.
"""
# Helper for consistent output
def log(msg: str, level: str = "info") -> None:
if output:
output.log(msg, level)
else:
print(msg)

# Try loading from file first (FR-001)
file_repos = load_github_repos_from_file(repos_file)
if file_repos:
return file_repos

# No file or empty - need to prompt or skip
if not interactive:
# FR-013, FR-014: Non-interactive mode skips prompts
log("No repos.txt found. Skipping GitHub analysis in non-interactive mode.", "info")
return []

# Display menu per FR-002
print("\nOptions:")
print(" [A] Analyze ALL accessible repositories")
print(" [S] Specify repository names manually (owner/repo format)")
print(" [O] Analyze organization repositories")
print(" [L] Select from list by number (e.g., 1,3,5 or 1-3)")
print(" [Q] Quit/Skip GitHub analysis")

# Create client for API calls - use provided token
config = AnalyzerConfig(github_token=github_token)
client = GitHubClient(config)

Choose a reason for hiding this comment

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

high

The select_github_repos function is re-creating its own AnalyzerConfig object using only the github_token. This causes any other user-defined settings from the environment or config (like timeout, per_page, and max_pages) to be ignored and fall back to defaults. This can lead to unexpected behavior, such as premature timeouts or incomplete data from pagination.

The function should accept the fully-populated config object from main() and use that to initialize the GitHubClient.

After applying this suggestion, you'll also need to update the call to this function in main() at line 1004 to pass the config object instead of repos_file and github_token separately:

repo_names = select_github_repos(
    config=config,
    interactive=interactive,
    output=output,
)
def select_github_repos(
    config: AnalyzerConfig,
    interactive: bool = True,
    output: TerminalOutput | None = None,
) -> list[str]:
    """Select GitHub repositories from file or interactively (Feature 004).

    Per spec FR-001 to FR-014:
    - Display interactive menu when repos.txt is missing or empty
    - Options: [A] All personal, [S] Specify manually, [O] Organization,
               [L] Select from list, [Q] Quit/Skip
    - Follow select_jira_projects pattern for UX consistency (FR-003)

    Args:
        config: The main analyzer configuration object.
        interactive: If True, prompt user when file is missing/empty.
                    If False (--quiet mode), skip prompts per FR-013.
        output: Optional TerminalOutput for consistent logging.

    Returns:
        List of repository names (owner/repo format) to analyze.
    """
    # Helper for consistent output
    def log(msg: str, level: str = "info") -> None:
        if output:
            output.log(msg, level)
        else:
            print(msg)

    # Try loading from file first (FR-001)
    file_repos = load_github_repos_from_file(config.repos_file)
    if file_repos:
        return file_repos

    # No file or empty - need to prompt or skip
    if not interactive:
        # FR-013, FR-014: Non-interactive mode skips prompts
        log("No repos.txt found. Skipping GitHub analysis in non-interactive mode.", "info")
        return []

    # Display menu per FR-002
    print("\nOptions:")
    print("  [A] Analyze ALL accessible repositories")
    print("  [S] Specify repository names manually (owner/repo format)")
    print("  [O] Analyze organization repositories")
    print("  [L] Select from list by number (e.g., 1,3,5 or 1-3)")
    print("  [Q] Quit/Skip GitHub analysis")

    # Create client for API calls
    client = GitHubClient(config)

e: RateLimitError with reset_time.
log: Logging function.
"""
import time as time_module

Choose a reason for hiding this comment

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

medium

According to the PEP 8 style guide, imports should be at the top of the file, not inside a function. This local import of time makes the code harder to read and maintain.

Please move this import to the top of the file as import time, and then change time_module.time() to time.time() in this function.

Comment on lines +702 to +707
except GitHubAnalyzerError as e:
if "404" in str(e):
log(f"Organization '{org_name}' not found or not accessible.", "warning")
else:
log(f"Error fetching organization repos: {e.message}", "error")
continue

Choose a reason for hiding this comment

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

medium

Checking for a 404 error by converting the exception to a string and searching for "404" is brittle. The error message could change, or another part of the exception's string representation might contain "404" for a different reason.

A more robust approach is to check if the exception is an APIError and then inspect its status_code attribute directly.

Suggested change
except GitHubAnalyzerError as e:
if "404" in str(e):
log(f"Organization '{org_name}' not found or not accessible.", "warning")
else:
log(f"Error fetching organization repos: {e.message}", "error")
continue
except GitHubAnalyzerError as e:
# APIError is a subclass of GitHubAnalyzerError and has a status_code
if hasattr(e, "status_code") and e.status_code == 404:
log(f"Organization '{org_name}' not found or not accessible.", "warning")
else:
log(f"Error fetching organization repos: {e.message}", "error")
continue

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.

2 participants