feat(vcs): Gitea adapter with diff-position translation (Phase 2) #79
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
Wrap
gitea.Clientin an adapter (gitea.Adapter) that satisfiesvcs.Client. The critical work is: (1) translating GitHub diff-position inReviewCommentto Gitea line numbers, and (2) translatingReviewRequest.Eventcanonical values to Gitea-native event strings.Background
review-bot uses GitHub diff-position as the canonical
ReviewCommentformat (path+position+commit_id, wherepositionis a 1-indexed offset from the@@hunk line). Gitea takes line numbers instead. The adapter is responsible for this translation.ReviewRequest.Eventuses GitHub canonical values ("APPROVE","REQUEST_CHANGES","COMMENT"). Gitea uses different strings. The adapter must translate.The
vcs.Clientinterface and all types live invcs/(created in issue #78).Work
gitea/adapter.goAlias strategy
Before implementing any method, check whether the gitea type can be aliased to the vcs type (see issue #78 type alias strategy). If
gitea.ContentEntry = vcs.ContentEntryandgitea.CommitStatus = vcs.CommitStatusare in place,ListContentsandGetCommitStatusesbecome true pass-throughs. Only types with structural differences (e.g.gitea.PullRequestvsvcs.PullRequest,gitea.Reviewvsvcs.Review) require field mapping.Methods requiring mapping (not simple pass-through)
These methods require explicit type translation, not just delegation:
GetPullRequest— mapsgitea.PullRequest→vcs.PullRequest:(Pseudo-code; use the correct anonymous struct literal syntax for Go.)
GetPullRequestFiles— maps[]gitea.ChangedFile→[]vcs.ChangedFile:ListReviews— maps[]gitea.Review→[]vcs.Review:GetCommitStatuses— maps[]gitea.CommitStatus→[]vcs.CommitStatus:Pass-through methods
These delegate directly with no type translation (signature and return type already match):
GetPullRequestDiffGetFileContentGetAuthenticatedUserDeleteReviewListContentsPer the alias strategy (see issue #78):
gitea.ContentEntryshould be aliased tovcs.ContentEntry(type ContentEntry = vcs.ContentEntry). If that alias is in place,gitea.Client.ListContentsalready satisfies the interface with no adapter wrapper needed — it becomes a true pass-through.If for any reason the alias cannot be applied (e.g. a conflicting field), fall back to a field-by-field map (Name, Path, Type — all strings). Add a unit test either way.
PostReviewtranslationTwo translations required:
1. Event string — translate
ReviewRequest.Eventfrom GitHub canonical to Gitea-native:ReviewRequest.Event(canonical)"APPROVE""APPROVED""REQUEST_CHANGES""REQUEST_CHANGES""COMMENT""COMMENT"Note:
"APPROVE"(no D) →"APPROVED"(with D) is the critical translation.gitea.Client.PostReviewpasses the event string verbatim to the Gitea API, which requires"APPROVED".Unit test: verify
"APPROVE"produces a Gitea API call with event"APPROVED".2. Diff-position → line number — for each
ReviewComment.Position:GetPullRequestDiffposition → new-file line numbermap per fileReviewComment.Positionto the corresponding file line numberCommitIDfromReviewComment.CommitID(pass through to Gitea)Diff-position counting rules (GitHub spec)
Position is a per-file counter. For each file in the diff:
@@hunk header line is position 1@@hunk within the same file continues incrementing (does not reset)Example:
DismissReviewGetFileContentAtRefPass-through to
gitea.Client.GetFileContentRef:GetCommitStatusesPass-through to
gitea.Client.GetCommitStatusesafter type mapping (see above).Compile-time check
Edge cases for diff-position parser
@@is still position 1; count from thereExit criteria
var _ vcs.Client = (*Adapter)(nil)compiles"APPROVE"→ Gitea API receives"APPROVED"(not"APPROVE")GetPullRequest,GetPullRequestFiles,ListReviews,GetCommitStatusesOut of scope
Update: The
Reviewerinterface now includesDismissReview(see #81). For the Gitea adapter, implementDismissReviewas a call to the underlyinggitea.Client.DeleteReview— Gitea supports deletion of any review state, so dismissal and deletion are equivalent outcomes.Update from codebase audit (issue #82): The Gitea adapter needs two additional pass-through methods beyond PostReview:
GetFileContentAtRef→ pass through togitea.Client.GetFileContentRefGetCommitStatuses→ pass through togitea.Client.GetCommitStatusesSee #78 update comment for the full interface signatures.