Skip to content

feat: migrate tools to github_api httpx client, fix async tests#199

Merged
ichoosetoaccept merged 1 commit intomainfrom
migrate-to-github-api
Feb 27, 2026
Merged

feat: migrate tools to github_api httpx client, fix async tests#199
ichoosetoaccept merged 1 commit intomainfrom
migrate-to-github-api

Conversation

@ichoosetoaccept
Copy link
Member

@ichoosetoaccept ichoosetoaccept commented Feb 27, 2026

What

Replaces all per-request gh CLI subprocess calls with a direct GitHub REST/GraphQL client built on httpx. Token is resolved once at startup (env var → gh auth token fallback) and reused for every request.

Why

  • Fixes subprocess hang bug (#65): gh CLI calls block the event loop and were the root cause of the MCP server becoming unresponsive under load.
  • Eliminates cwd dependency: gh used the working directory to detect the repo; httpx calls use an explicit repo parameter or a one-time gh fallback, so the server no longer needs a workspace root.
  • Unblocks PAT migration (#85): The new github_api.py layer is the foundation for switching to fine-grained PATs and removing the gh CLI runtime dependency entirely.

Changes

New: src/codereviewbuddy/github_api.py

  • GitHubError / GitHubAuthError exception hierarchy
  • parse_repo() — splits owner/repo strings
  • get_token() — resolves token once (GH_TOKEN → GITHUB_TOKEN → gh auth token), raises GitHubAuthError with a magic PAT-creation URL if none found
  • graphql() — cached GraphQL queries, cache-busting mutations
  • rest() — cached GET requests, paginated REST with Link: header following
  • download_bytes() — for CI log zip downloads

Migrated callers

File Change
tools/comments.py All gh.graphql/gh.restawait github_api.graphql/rest; internal functions made async
tools/stack.py Same; _fetch_merged_prs, _fetch_pr_summary, _fetch_open_prs now async
tools/issues.py create_issue_from_comment now async
tools/descriptions.py _fetch_pr_info now async
server.py await added to calls that became async

Tests

  • tests/test_github_api.py — new; covers token resolution, error types, REST/GraphQL/paginate/download via respx mocks
  • tests/test_comments.py, test_stack.py, test_issues.py — updated to AsyncMock, correct github_api.* patch paths, await on async calls

What's not changed yet (follow-up phases)

  • gh.get_repo_info and gh.get_current_pr_number still used as sync fallbacks (wrapped in asyncio.to_thread) — Phase 3 will replace these with git + API
  • server.py workspace detection still present — Phase 4 will simplify
  • ci.py still uses gh for log fetching — Phase 5

Closes #65
Related to #85

Copy link
Member Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@greptile-apps
Copy link

greptile-apps bot commented Feb 27, 2026

Greptile Summary

This PR successfully replaces subprocess-based gh CLI calls with a direct httpx-based GitHub API client, fixing the subprocess hang bug (#65) and laying the foundation for PAT migration (#85).

Major changes:

  • New github_api.py module provides async REST/GraphQL methods with token caching, error handling, and automatic pagination
  • All tools (comments.py, stack.py, issues.py, descriptions.py) migrated from gh.rest()/gh.graphql() to await github_api.rest()/await github_api.graphql()
  • Token resolution moved from per-request to startup (cached after first call)
  • Comprehensive test coverage added for new module with respx mocks
  • All existing tests updated to use AsyncMock and patch correct module paths

Implementation quality:

  • Error handling is robust with proper exception hierarchy (GitHubError, GitHubAuthError)
  • Caching strategy is sound (queries cached, mutations bypass/clear cache)
  • Pagination follows GitHub's Link: header pattern correctly
  • Tests are thorough and properly structured

The async migration is complete and correct - all blocking calls now properly use await. Previous review comments about blocking sync calls have been resolved.

Confidence Score: 5/5

  • This PR is safe to merge with no blocking issues
  • The implementation is clean, well-tested, and addresses all previous review feedback. All async patterns are correct, error handling is comprehensive, and the migration is complete with no breaking changes to the API surface.
  • No files require special attention

Important Files Changed

Filename Overview
src/codereviewbuddy/github_api.py New httpx-based GitHub API client with token caching, error handling, and pagination support
src/codereviewbuddy/tools/comments.py Migrated all gh CLI subprocess calls to async github_api methods
src/codereviewbuddy/tools/stack.py Converted REST API calls to async github_api methods throughout stack discovery and status functions
tests/test_github_api.py Comprehensive test coverage for new github_api module using respx mocks
pyproject.toml Added httpx and respx dependencies for HTTP client and testing

Sequence Diagram

sequenceDiagram
    participant Tool as Tool Function
    participant API as github_api
    participant Cache as LRU Cache
    participant HTTP as httpx Client
    participant GitHub as GitHub API

    Tool->>API: graphql(query) / rest(endpoint)
    API->>Cache: Check cache key
    alt Cache Hit
        Cache-->>API: Return cached result
        API-->>Tool: Return data
    else Cache Miss
        API->>API: get_token() (cached)
        API->>HTTP: POST/GET with Bearer token
        HTTP->>GitHub: HTTPS Request
        GitHub-->>HTTP: JSON Response
        HTTP-->>API: Parse response
        API->>API: _raise_for_status()
        alt Success
            API->>Cache: Store result
            API-->>Tool: Return data
        else Error (401/403/5xx)
            API-->>Tool: Raise GitHubError/GitHubAuthError
        end
    end
Loading

Last reviewed commit: 6f8ce5c

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

12 files reviewed, 6 comments

Edit Code Review Agent Settings | Greptile

- Add github_api.py with GitHubClient (httpx), token resolution, REST/GraphQL helpers
- Migrate comments.py, stack.py, issues.py, descriptions.py to github_api
- Make async: _get_pr_reviews, _get_pr_issue_comments, _get_pr_commits,
  _fetch_thread_detail, resolve_comment, reply_to_comment, _fetch_merged_prs,
  _fetch_pr_summary, _fetch_open_prs, create_issue_from_comment
- Update server.py to await now-async calls
- Fix test_comments.py, test_stack.py, test_issues.py: AsyncMock, await,
  github_api mock paths throughout
- Add httpx to dependencies
@ichoosetoaccept ichoosetoaccept merged commit 9ae4f28 into main Feb 27, 2026
12 checks passed
@ichoosetoaccept ichoosetoaccept deleted the migrate-to-github-api branch February 27, 2026 20:38
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.

bug: MCP server becomes unresponsive after multiple rapid tool calls

1 participant