feat(github): PRReader + FileReader client (Phase 3) #80
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Architecture Reference
Base branch:
feature/github-support— open all PRs against this branch.Goal
Implement
PRReaderandFileReaderfor the GitHub client, with a configurable base URL to support bothgithub.comand GitHub Enterprise (github.concur.com).Background
review-bot needs to post reviews on GitHub and GitHub Enterprise PRs. The
vcs.Clientinterface and all canonical types live invcs/(created in issue #78).The GitHub client uses diff-position natively —
ReviewComment.Positionmaps directly to the GitHub APIpositionfield with no translation needed.Auth:
Authorization: Bearer <token>Base URL: configurable; empty string defaults to
https://api.github.com. For GitHub Enterprise (github.concur.com), the base URL ishttps://github.concur.com/api/v3.Work
github/client.goHTTP helpers: set
Authorization: Bearer <token>,Accept,Content-Typeheaders. Handle 429 withRetry-After.github/pr.goGetPullRequest—GET /repos/{owner}/{repo}/pulls/{number}GetPullRequestDiff— same URL (GET /repos/{owner}/{repo}/pulls/{number}) withAccept: application/vnd.github.diffset per-request (overrides the defaultapplication/vnd.github+json). The response body is raw unified diff text — do not JSON-unmarshal it.GetPullRequestFiles—GET /repos/{owner}/{repo}/pulls/{number}/files. Paginate through all pages (default 30/page, use?per_page=100&page=N). For each file object, populatevcs.ChangedFile{Filename, Status, Patch}wherePatchis thepatchfield from the response (the per-file unified diff hunk).Patchmay be absent for binary files — set to""in that case.GetFileContentAtRef—GET /repos/{owner}/{repo}/contents/{path}?ref={ref}, base64-decode thecontentfield. An emptyrefomits the query parameter (uses default branch).GetCommitStatuses— fetches both commit statuses and check runs, merges them into[]vcs.CommitStatus:Commit statuses:
GET /repos/{owner}/{repo}/commits/{sha}/statusreturns a JSON envelope{state, statuses: [...]}— unmarshal the wrapper object and extract thestatusesarray. Map each entry:Context→context,Status→state,Description→description,TargetURL→target_url.Check runs:
GET /repos/{owner}/{repo}/commits/{sha}/check-runs(paginated). For each check run, map tovcs.CommitStatus{Context: name, Status: <mapped>, Description: conclusion, TargetURL: html_url}. Map check runconclusiontovcs.CommitStatus.Statusas follows:"success"→"success""failure","action_required","timed_out"→"failure""cancelled","skipped","neutral"→"success"(non-blocking)nil/"in_progress"/"queued"→"pending"Return the merged slice. Deduplication is not required (different context names).
github/files.goGetFileContent— delegates toGetFileContentAtRef(ctx, owner, repo, path, "")(empty ref = default branch)ListContents—GET /repos/{owner}/{repo}/contents/{path}(no ref), returns array when path is a directoryUnit tests
For each method: happy path, 404, 401, 429 (with retry), malformed response.
Compile-time check
(Full
vcs.Clientcheck happens after #81 adds Reviewer + Identity.)Exit criteria
go test ./github/...passes for all PRReader + FileReader methodsNewClient(token, "")useshttps://api.github.comNewClient(token, "https://github.concur.com/api/v3")targets GHE correctlyGetFileContentdelegates toGetFileContentAtRefwith empty refGetPullRequestFilesreturns all files for PRs with >30 changed files (pagination tested)GetPullRequestFilespopulatesPatchfrom the GitHub responsepatchfieldGetCommitStatusesreturns results from both commit statuses and check-runs endpointsOut of scope
Update from codebase audit (issue #82): Two additional methods needed in the GitHub client:
GetFileContentAtRef(ctx, owner, repo, path, ref string) (string, error)— same asGetFileContentbut with an explicit ref. GitHub:GET /repos/{owner}/{repo}/contents/{path}?ref={ref}+ base64 decode (same endpoint asGetFileContent, just with the ref param populated).GetCommitStatuses(ctx, owner, repo, sha string) ([]CommitStatus, error)— GitHub:GET /repos/{owner}/{repo}/commits/{sha}/status. Map thestatusesarray to[]vcs.CommitStatus.Add unit tests for both.
Split into sub-issues for focused review:
vcs/types.go,github/client.go,github/client_test.go) → PR feat(github): implement GitHub API client foundation (#101)github/pr.go,github/pr_test.go) → PR feat(github): implement PRReader interface (#102)github/files.go,github/files_test.go,github/conformance_test.go) → PR feat(github): implement FileReader interface (#103)Closing in favour of the sub-issues.