feat(vcs): extract interfaces and types from gitea/ (Phase 1) #78
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
Extract
vcs.Clientinterface and shared types from the existing workinggitea/code. The interface is discovered from working code, not invented.Background
review-bot currently only supports Gitea. The goal is to add GitHub and GitHub Enterprise support behind a shared
vcs.Clientinterface. This issue is the foundation: define that interface by reading whatgitea/client.goalready does, then declare the shared types.All
ReviewCommenttypes invcs/use GitHub diff-position convention (path+position+commit_id) — this is the canonical shape across all adapters.positionis a 1-indexed offset from the@@hunk line in the unified diff. Adapters translate to/from this format; the core never deals with platform-native formats.ReviewRequest.Eventuses GitHub's string values as canonical:"APPROVE","REQUEST_CHANGES","COMMENT". The Gitea adapter is responsible for translating these to Gitea-native event values before posting.Work
vcs/interfaces.goRole-based interfaces extracted from
gitea/client.go:vcs/types.goExtract or alias from
gitea/. Explicit required fields listed below:vcs/util.goTwo utility functions — do not add either to the interface:
1.
GetAllFilesInPath— recursive directory traversal:Returns
map[string]string(path → content). Implementation: callclient.ListContents(ctx, owner, repo, path), recurse intoType == "dir"entries, fetch content forType == "file"entries viaclient.GetFileContent.2.
BuildLineToPositionMap— inverts the diff-position map for use inmain.go:This is the inverse of the Gitea adapter's
position → linemap. Used inmain.goto convert LLM-produced line numbers to diff-positions before constructingvcs.ReviewComment. Replacesgitea.ParseDiffNewLines+diffRanges.Containsentirely.Counting rules are identical to the diff-position spec in issue #79:
@@hunk header is position 1 for the first hunk@@hunk continues the count, does not resetUnit tests for
BuildLineToPositionMap:Unit tests for
GetAllFilesInPath: empty dir, flat dir, nested dirs, mixed.Type alias strategy
When
gitea/types have identical fields tovcs/types, prefer type aliases to avoid mapping boilerplate:Use an independent copy (no alias) only when the gitea type has fields that differ from or extend the vcs type (e.g.
gitea.PullRequesthas noNumberfield — it cannot be aliased tovcs.PullRequest). In that case, map field-by-field in the adapter.This decision affects which methods are true pass-throughs vs. require mapping — the compile-time check will make divergences visible.
review.ContentEntryandreview.GiteaClientcleanupreview/repo_persona.godefinesreview.ContentEntry(mirroringgitea.ContentEntryto avoid import cycles) andreview.GiteaClientinterface. Oncevcs/types.goexists,review/can safely importvcs/(no cycle —vcs/has no upward dependencies).As part of this issue (#78), delete:
review.ContentEntry— replace withvcs.ContentEntrythroughoutreview/review.GiteaClient— replace withvcs.FileReaderinreview.LoadRepoPersonassignaturereview/repo_persona_test.go:mockGiteaClient.ListContentsreturn type changes from[]review.ContentEntryto[]vcs.ContentEntryThese deletions are owned by #78 and must be included in the same PR as
vcs/types.go. They are not deferred — #82 depends on them already being gone.Compile-time check
Expected to fail on
PostReview,GetFileContentAtRef,GetCommitStatuses,DismissReview,ListContents, andListReviews— this is intentional (return types differ:[]gitea.ContentEntry/[]gitea.Reviewvs[]vcs.ContentEntry/[]vcs.Reviewunless type-aliased). Each failure documents a gap the Gitea adapter (issue #79) must bridge.GetFileContentandGetAuthenticatedUserwill pass without changes.Exit criteria
vcs/interfaces.go,vcs/types.go,vcs/util.gocompilegitea/, not inventedvcs.GetAllFilesInPathunit tests passvcs.BuildLineToPositionMapunit tests passOut of scope
Update:
Reviewerinterface must includeDismissReview(added in issue #81 design):Rationale: GitHub cannot delete submitted reviews;
DismissReviewis the cross-platform primitive for replacing a review. The Gitea adapter implements it by calling the underlyingDeleteReview. See #81 for full details.Update from codebase audit (issue #82): Two methods are missing from the interface design and must be added to
vcs/interfaces.go:GetFileContentAtRef— the current code callsGetFileContentRef(ctx, owner, repo, path, ref)to fetch file content at a specific git ref (branch/SHA). The currentGetFileContentin the design takes norefparameter. Add:GetCommitStatuses— used for CI status checks. Add toPRReader:Add
CommitStatustovcs/types.go:{ Context, Status, Description string }The compile-time check
var _ vcs.Client = (*gitea.Client)(nil)will fail on both of these — which is correct and expected, as they need adapter coverage.Update:
vcs/util.go—GetAllFilesInPathis recursive directory traversal, not a VCS primitive. Do not add it to thevcs.Clientinterface. Instead, add a utility function in this issue:Implementation: call
client.ListContents(ctx, owner, repo, path), recurse into entries whereType == "dir", collect entries whereType == "file".Add a unit test for: empty dir, flat dir, nested dirs, mixed files and dirs.
rodin referenced this issue2026-05-12 19:31:28 +00:00