# GitHub Support for review-bot ## Goal AI code reviews on GitHub PRs using SAP AI Core as the LLM provider. ## Non-Goals - Auto-detection of platform (explicit `--provider` flag is fine) - Unifying into one abstraction layer for its own sake ## Constraints 1. **Same features on both platforms** — anything review-bot does on Gitea should work on GitHub 2. **Testable** — small interfaces, dependency injection, no global state 3. **Interface from working code** — extract from gitea/, don't invent in vacuum --- ## Part 1: Feature Inventory What does review-bot actually do? ### Core Review Flow | Feature | Description | |---------|-------------| | Get PR metadata | Title, body, head SHA, base ref | | Get PR diff | Unified diff format | | Get PR files | List of changed files with status | | Get file content | Raw file at ref | | List directory | Enumerate files in path | | Post review | Body + inline comments + verdict | ### Review Management | Feature | Description | |---------|-------------| | List reviews | Get existing reviews on PR | | Delete review | Remove old review before re-posting | | Get authenticated user | Who am I? | ### Platform-Specific (not in shared interface) | Feature | Gitea | GitHub | |---------|-------|--------| | Resolve comment | Yes | No equivalent | | Timeline API | Yes | No equivalent | These stay on gitea.Client directly. Callers that need them type-assert. --- ## Part 2: GitHub API Mapping | Feature | Gitea API | GitHub API | |---------|-----------|------------| | Get PR | `GET /api/v1/repos/.../pulls/{n}` | `GET /repos/.../pulls/{n}` | | Get diff | `.diff` suffix | `Accept: application/vnd.github.diff` header | | Get files | `GET .../pulls/{n}/files` | Same | | Get file content | `GET .../raw/{path}?ref=` | `GET .../contents/{path}?ref=` + base64 decode | | List directory | `GET .../contents/{path}` | Same | | Post review | `POST .../pulls/{n}/reviews` | Same (adapter handles comment schema) | | List reviews | `GET .../pulls/{n}/reviews` | Same | | Delete review | `DELETE .../pulls/{n}/reviews/{id}` | Same | | Get user | `GET /api/v1/user` | `GET /user` | --- ## Part 3: Interface Design **Principle:** Extract from working gitea/ code. The interface is discovered, not invented. ### Small, role-based interfaces ```go // vcs/interfaces.go type PRReader interface { GetPullRequest(ctx context.Context, owner, repo string, number int) (*PullRequest, error) GetPullRequestDiff(ctx context.Context, owner, repo string, number int) (string, error) GetPullRequestFiles(ctx context.Context, owner, repo string, number int) ([]ChangedFile, error) } type FileReader interface { GetFileContent(ctx context.Context, owner, repo, path, ref string) (string, error) ListContents(ctx context.Context, owner, repo, path string) ([]ContentEntry, error) } type Reviewer interface { PostReview(ctx context.Context, owner, repo string, number int, req ReviewRequest) (*Review, error) ListReviews(ctx context.Context, owner, repo string, number int) ([]Review, error) DeleteReview(ctx context.Context, owner, repo string, number int, reviewID int64) error } type Identity interface { GetAuthenticatedUser(ctx context.Context) (string, error) } // Client combines all for callers that need everything type Client interface { PRReader FileReader Reviewer Identity } ``` ### Types Use what gitea/ already has. Move to vcs/types.go or re-export. ```go type PullRequest struct { ... } // from gitea.PullRequest type ChangedFile struct { ... } // from gitea.ChangedFile type ContentEntry struct { ... } // from gitea.ContentEntry type Review struct { ... } // from gitea.Review type ReviewRequest struct { ... } // new, for PostReview input type ReviewComment struct { ... } // from gitea.ReviewComment ``` ### Adapter responsibilities Each adapter (gitea, github) handles: - API URL construction - Auth header format (`token` vs `Bearer`) - Request/response mapping - Comment schema translation (line numbers, commit IDs, etc.) --- ## Part 4: Test Plan ### Unit Tests (mock HTTP) ``` github/ pr_test.go # TestGetPullRequest, TestGetDiff, TestGetFiles files_test.go # TestGetFileContent, TestListContents review_test.go # TestPostReview, TestListReviews, TestDeleteReview identity_test.go # TestGetAuthenticatedUser ``` Per method: happy path, 404, 401, 429, malformed response. ### Integration Tests Against github.com/aweiker/ai-core-review-bot: - Fetch real PR - Fetch real file - Post + delete review (clean up) ### End-to-End Open PR on test repo, run full review-bot, verify review appears. --- ## Part 5: Implementation Phases ### Phase 1: Extract interfaces from gitea/ **Work:** - Create `vcs/interfaces.go` with interfaces extracted from gitea/client.go signatures - Create `vcs/types.go` — move or alias types from gitea/ - Verify gitea.Client satisfies vcs.Client (compile-time check) **Exit criteria:** `var _ vcs.Client = (*gitea.Client)(nil)` compiles. --- ### Phase 2: Gitea adapter (if needed) **Work:** - If gitea.Client method signatures don't match exactly, create wrapper - Keep gitea/ working exactly as before **Exit criteria:** Existing tests pass. No behavior change. --- ### Phase 3: GitHub client — PRReader **Work:** - `github/client.go` — struct, constructor, HTTP helpers - `github/pr.go` — GetPullRequest, GetPullRequestDiff, GetPullRequestFiles - Unit tests **Exit criteria:** `go test ./github/...` passes for PR methods. --- ### Phase 4: GitHub client — FileReader **Work:** - `github/files.go` — GetFileContent, ListContents - Unit tests **Exit criteria:** Unit tests pass. --- ### Phase 5: GitHub client — Reviewer + Identity **Work:** - `github/review.go` — PostReview, ListReviews, DeleteReview - `github/identity.go` — GetAuthenticatedUser - Unit tests **Exit criteria:** Unit tests pass. --- ### Phase 6: Integration tests **Work:** - `integration/github_test.go` - Test against real GitHub **Exit criteria:** All integration tests pass. --- ### Phase 7: Wire into cmd/review-bot **Work:** - Add `--provider github|gitea` flag (default: gitea for backward compat) - Select client based on flag - Update to use vcs interfaces where it makes sense **Exit criteria:** - `./review-bot --provider github ...` works - `./review-bot --provider gitea ...` works (same as before) - Existing Gitea workflows unchanged --- ### Phase 8: GitHub Actions workflow + releases **Work:** - `.github/workflows/ci.yml` — test on PR - `.github/workflows/release.yml` — publish binary to GitHub releases - `.github/actions/review/action.yml` — composite action - Action downloads binary from github.com/aweiker/ai-core-review-bot releases **Exit criteria:** - CI runs on github.com/aweiker/ai-core-review-bot - Release creates downloadable binary - Review action posts review successfully --- ## Part 6: Decisions | Question | Decision | |----------|----------| | Auth token | Workflow `GITHUB_TOKEN` (automatic) | | Binary distribution | GitHub releases on aweiker/ai-core-review-bot | | Comment schema | Adapter's job — translate ReviewComment to platform format | | Default provider | `gitea` for backward compatibility | | Shared types | vcs/types.go (extracted from gitea/) | | Platform-specific features | Stay on concrete client, not interface | --- ## Summary 8 phases. Start by extracting interfaces from working gitea/ code, not inventing them. GitHub implements the same interfaces. Each phase has clear exit criteria.