3fc31c0822
Key changes: - Interface discovered from gitea/, not invented - Gitea adapter first (Phase 1-2), GitHub second (Phase 3-5) - Removed 'Open Questions' — all resolved: - Token: workflow GITHUB_TOKEN - Binary: GitHub releases on aweiker/ai-core-review-bot - Comment schema: adapter responsibility - 8 phases with clear exit criteria - Platform-specific features (resolve, timeline) stay on concrete client Issue: #76
269 lines
7.4 KiB
Markdown
269 lines
7.4 KiB
Markdown
# 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.
|