From 2e6f46f28dbaa2a36537e4c63f95519772bb88de Mon Sep 17 00:00:00 2001 From: claw Date: Tue, 12 May 2026 10:04:57 -0700 Subject: [PATCH 1/4] feat(vcs): extract interfaces and types from gitea/ (Phase 1, #78) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add vcs/interfaces.go and vcs/types.go as the foundation for multi-platform VCS support. Interfaces are discovered from working gitea/client.go code, not invented in a vacuum. vcs/interfaces.go — role-based interfaces: - PRReader: GetPullRequest, GetPullRequestDiff, GetPullRequestFiles - FileReader: GetFileContent (path + ref), ListContents - Reviewer: PostReview (ReviewRequest), ListReviews, DeleteReview - Identity: GetAuthenticatedUser - Client: all four composed vcs/types.go — types extracted from gitea/: - PullRequest, ChangedFile, ContentEntry, Review (identical to gitea/) - ReviewComment: uses GitHub diff-position convention (Position int, CommitID string) instead of Gitea's NewPosition int64 - ReviewRequest: new type wrapping Body, Event, Comments vcs/check.go (//go:build ignore) — documents the gaps gitea.Client must bridge in Phase 2: 1. PostReview signature mismatch (event+body+[]ReviewComment vs ReviewRequest) 2. GetFileContent missing ref parameter 3. ReviewComment type mismatch (NewPosition vs Position/CommitID) No behavior changes. All existing tests pass. --- vcs/check.go | 30 +++++++++++++++++++++++ vcs/interfaces.go | 37 ++++++++++++++++++++++++++++ vcs/types.go | 62 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 129 insertions(+) create mode 100644 vcs/check.go create mode 100644 vcs/interfaces.go create mode 100644 vcs/types.go diff --git a/vcs/check.go b/vcs/check.go new file mode 100644 index 0000000..4989eac --- /dev/null +++ b/vcs/check.go @@ -0,0 +1,30 @@ +//go:build ignore + +// This file is excluded from normal builds. +// Run `go build -v .` inside this file to see the documented gaps between +// gitea.Client and vcs.Client that Phase 2 (Gitea adapter) must bridge. +// +// Known gaps (as of Phase 1): +// +// 1. PostReview signature mismatch: +// gitea.Client: PostReview(ctx, owner, repo, number, event, body string, comments []gitea.ReviewComment) +// vcs.Reviewer: PostReview(ctx, owner, repo, number, req vcs.ReviewRequest) +// +// 2. GetFileContent signature mismatch: +// gitea.Client: GetFileContent(ctx, owner, repo, filepath string) [no ref; uses default branch] +// vcs.FileReader: GetFileContent(ctx, owner, repo, path, ref string) +// (gitea.Client has GetFileContentRef for the ref variant) +// +// 3. ReviewComment type mismatch: +// gitea.ReviewComment uses NewPosition int64 (Gitea line-number convention) +// vcs.ReviewComment uses Position int (GitHub diff-position convention) +// +// The Gitea adapter (Phase 2) will wrap gitea.Client to bridge these gaps. + +package vcs + +import "gitea.weiker.me/rodin/review-bot/gitea" + +// This compile-time assertion intentionally fails. +// It documents what the Gitea adapter must implement to satisfy vcs.Client. +var _ Client = (*gitea.Client)(nil) diff --git a/vcs/interfaces.go b/vcs/interfaces.go new file mode 100644 index 0000000..62642f1 --- /dev/null +++ b/vcs/interfaces.go @@ -0,0 +1,37 @@ +package vcs + +import "context" + +// PRReader can fetch pull request metadata, diffs, and changed files. +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) +} + +// FileReader can fetch file contents and list directory entries. +type FileReader interface { + GetFileContent(ctx context.Context, owner, repo, path, ref string) (string, error) + ListContents(ctx context.Context, owner, repo, path string) ([]ContentEntry, error) +} + +// Reviewer can post, list, and delete pull request reviews. +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 +} + +// Identity can report who the authenticated user is. +type Identity interface { + GetAuthenticatedUser(ctx context.Context) (string, error) +} + +// Client is the full VCS interface: PR reads, file reads, review management, and identity. +// Platform adapters (gitea, github) implement this interface. +type Client interface { + PRReader + FileReader + Reviewer + Identity +} diff --git a/vcs/types.go b/vcs/types.go new file mode 100644 index 0000000..82f0ec1 --- /dev/null +++ b/vcs/types.go @@ -0,0 +1,62 @@ +// Package vcs defines the shared interface and types for VCS platform clients. +// Adapters (gitea, github) implement these interfaces; the core review logic +// uses them without knowing the underlying platform. +package vcs + +// PullRequest holds relevant PR metadata. +type PullRequest struct { + Title string `json:"title"` + Body string `json:"body"` + Head struct { + Sha string `json:"sha"` + Ref string `json:"ref"` + } `json:"head"` +} + +// ChangedFile represents a file modified in a PR. +type ChangedFile struct { + Filename string `json:"filename"` + Status string `json:"status"` +} + +// ContentEntry represents a file or directory entry from the contents API. +type ContentEntry struct { + Name string `json:"name"` + Path string `json:"path"` + Type string `json:"type"` // "file" or "dir" +} + +// Review represents a pull request review. +type Review struct { + ID int64 `json:"id"` + Body string `json:"body"` + User struct { + Login string `json:"login"` + } `json:"user"` + State string `json:"state"` + Stale bool `json:"stale"` + CommitID string `json:"commit_id"` +} + +// ReviewComment represents an inline comment in a review. +// All adapters use GitHub diff-position convention: +// - Position is a 1-indexed offset from the @@ hunk line in the unified diff. +// - CommitID is the commit SHA the comment is anchored to. +// +// Adapters are responsible for translating to/from platform-native formats +// (e.g. Gitea uses line numbers; GitHub uses diff positions natively). +type ReviewComment struct { + Path string `json:"path"` + Position int `json:"position"` // diff-position: 1-indexed offset from @@ hunk line + CommitID string `json:"commit_id"` + Body string `json:"body"` +} + +// ReviewRequest is the payload for posting a review. +type ReviewRequest struct { + // Body is the top-level review comment. + Body string `json:"body"` + // Event is "APPROVED" or "REQUEST_CHANGES". + Event string `json:"event"` + Comments []ReviewComment `json:"comments,omitempty"` +} -- 2.47.3 From 1ac51669ed1b2da07c0b2f322ba38f92b9797488 Mon Sep 17 00:00:00 2001 From: claw Date: Tue, 12 May 2026 10:05:39 -0700 Subject: [PATCH 2/4] docs(vcs): add package doc to interfaces.go --- vcs/interfaces.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/vcs/interfaces.go b/vcs/interfaces.go index 62642f1..ecada13 100644 --- a/vcs/interfaces.go +++ b/vcs/interfaces.go @@ -1,3 +1,6 @@ +// Package vcs defines the shared VCS client interface and supporting types. +// Platform adapters (gitea, github) implement these interfaces so the core +// review logic can work with any VCS platform without platform-specific code. package vcs import "context" -- 2.47.3 From c889724dda61ee4e67bf4cbb47436ab8c6d4d91c Mon Sep 17 00:00:00 2001 From: claw Date: Tue, 12 May 2026 12:00:30 -0700 Subject: [PATCH 3/4] fix(vcs): address Phase 1 review findings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Rename PullRequest.Head.Sha → SHA (Go acronym convention) - Add typed ReviewEvent alias with exported constants - Remove duplicate package doc from types.go (kept in interfaces.go) - Fix misleading comment in check.go --- vcs/check.go | 2 +- vcs/types.go | 21 +++++++++++++++------ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/vcs/check.go b/vcs/check.go index 4989eac..0e39af7 100644 --- a/vcs/check.go +++ b/vcs/check.go @@ -1,7 +1,7 @@ //go:build ignore // This file is excluded from normal builds. -// Run `go build -v .` inside this file to see the documented gaps between +// Remove the //go:build ignore tag and run `go build ./vcs/` to see the documented gaps between // gitea.Client and vcs.Client that Phase 2 (Gitea adapter) must bridge. // // Known gaps (as of Phase 1): diff --git a/vcs/types.go b/vcs/types.go index 82f0ec1..2b43985 100644 --- a/vcs/types.go +++ b/vcs/types.go @@ -1,14 +1,23 @@ -// Package vcs defines the shared interface and types for VCS platform clients. -// Adapters (gitea, github) implement these interfaces; the core review logic -// uses them without knowing the underlying platform. package vcs +// ReviewEvent is the event type for a pull request review action. +type ReviewEvent string + +const ( + // ReviewEventApprove approves the pull request. + ReviewEventApprove ReviewEvent = "APPROVED" + // ReviewEventRequestChanges requests changes to the pull request. + ReviewEventRequestChanges ReviewEvent = "REQUEST_CHANGES" + // ReviewEventComment posts a review comment without approval or rejection. + ReviewEventComment ReviewEvent = "COMMENT" +) + // PullRequest holds relevant PR metadata. type PullRequest struct { Title string `json:"title"` Body string `json:"body"` Head struct { - Sha string `json:"sha"` + SHA string `json:"sha"` Ref string `json:"ref"` } `json:"head"` } @@ -56,7 +65,7 @@ type ReviewComment struct { type ReviewRequest struct { // Body is the top-level review comment. Body string `json:"body"` - // Event is "APPROVED" or "REQUEST_CHANGES". - Event string `json:"event"` + // Event is the review action (approve, request changes, or comment). + Event ReviewEvent `json:"event"` Comments []ReviewComment `json:"comments,omitempty"` } -- 2.47.3 From cd6cd93bf0dede13d9d9bc97861949d605cda997 Mon Sep 17 00:00:00 2001 From: claw Date: Tue, 12 May 2026 12:06:29 -0700 Subject: [PATCH 4/4] fix(vcs): address PR #83 review findings (round 2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Extract named HeadRef and UserInfo structs from anonymous structs in PullRequest and Review (comments 16615, 16616) - Change ReviewEventApprove value from "APPROVED" to "APPROVE" to represent the action, not the state; document adapter translation responsibility (comment 16621) - Add doc comment on ReviewComment.CommitID noting optionality (16531) - Move compile-time assertion from check.go (//go:build ignore) to check_test.go with a "phase2" build tag — removes gitea adapter import from the vcs package (comment 16622) - check.go misleading comment was already fixed in prior commit (16532, 16539) - Sha→SHA, typed ReviewEvent, duplicate package doc already resolved (16537, 16538, 16530) --- vcs/{check.go => check_test.go} | 25 +++++++++---------- vcs/types.go | 43 +++++++++++++++++++++------------ 2 files changed, 38 insertions(+), 30 deletions(-) rename vcs/{check.go => check_test.go} (61%) diff --git a/vcs/check.go b/vcs/check_test.go similarity index 61% rename from vcs/check.go rename to vcs/check_test.go index 0e39af7..c64e954 100644 --- a/vcs/check.go +++ b/vcs/check_test.go @@ -1,10 +1,14 @@ -//go:build ignore +//go:build phase2 -// This file is excluded from normal builds. -// Remove the //go:build ignore tag and run `go build ./vcs/` to see the documented gaps between -// gitea.Client and vcs.Client that Phase 2 (Gitea adapter) must bridge. -// -// Known gaps (as of Phase 1): +package vcs_test + +import ( + "gitea.weiker.me/rodin/review-bot/gitea" + "gitea.weiker.me/rodin/review-bot/vcs" +) + +// Compile-time assertion: documents the gap between gitea.Client and vcs.Client. +// Guarded by the "phase2" build tag — enable once the Gitea adapter bridges these gaps: // // 1. PostReview signature mismatch: // gitea.Client: PostReview(ctx, owner, repo, number, event, body string, comments []gitea.ReviewComment) @@ -20,11 +24,4 @@ // vcs.ReviewComment uses Position int (GitHub diff-position convention) // // The Gitea adapter (Phase 2) will wrap gitea.Client to bridge these gaps. - -package vcs - -import "gitea.weiker.me/rodin/review-bot/gitea" - -// This compile-time assertion intentionally fails. -// It documents what the Gitea adapter must implement to satisfy vcs.Client. -var _ Client = (*gitea.Client)(nil) +var _ vcs.Client = (*gitea.Client)(nil) diff --git a/vcs/types.go b/vcs/types.go index 2b43985..6dfd932 100644 --- a/vcs/types.go +++ b/vcs/types.go @@ -1,25 +1,36 @@ package vcs // ReviewEvent is the event type for a pull request review action. +// Adapters must translate these action constants to/from platform-native values. +// For example, Gitea uses "APPROVED" as both action and state, while GitHub +// uses "APPROVE" for the action and returns "approved" as the state. type ReviewEvent string const ( // ReviewEventApprove approves the pull request. - ReviewEventApprove ReviewEvent = "APPROVED" + ReviewEventApprove ReviewEvent = "APPROVE" // ReviewEventRequestChanges requests changes to the pull request. ReviewEventRequestChanges ReviewEvent = "REQUEST_CHANGES" // ReviewEventComment posts a review comment without approval or rejection. ReviewEventComment ReviewEvent = "COMMENT" ) +// HeadRef identifies the source branch and latest commit of a pull request. +type HeadRef struct { + SHA string `json:"sha"` + Ref string `json:"ref"` +} + +// UserInfo identifies a user by login name. +type UserInfo struct { + Login string `json:"login"` +} + // PullRequest holds relevant PR metadata. type PullRequest struct { - Title string `json:"title"` - Body string `json:"body"` - Head struct { - SHA string `json:"sha"` - Ref string `json:"ref"` - } `json:"head"` + Title string `json:"title"` + Body string `json:"body"` + Head HeadRef `json:"head"` } // ChangedFile represents a file modified in a PR. @@ -37,20 +48,20 @@ type ContentEntry struct { // Review represents a pull request review. type Review struct { - ID int64 `json:"id"` - Body string `json:"body"` - User struct { - Login string `json:"login"` - } `json:"user"` - State string `json:"state"` - Stale bool `json:"stale"` - CommitID string `json:"commit_id"` + ID int64 `json:"id"` + Body string `json:"body"` + User UserInfo `json:"user"` + State string `json:"state"` + Stale bool `json:"stale"` + CommitID string `json:"commit_id"` } // ReviewComment represents an inline comment in a review. // All adapters use GitHub diff-position convention: // - Position is a 1-indexed offset from the @@ hunk line in the unified diff. -// - CommitID is the commit SHA the comment is anchored to. +// - CommitID identifies the commit the comment is anchored to. +// It is optional; omit (empty string) for review-level comments that are +// not attached to a specific commit. // // Adapters are responsible for translating to/from platform-native formats // (e.g. Gitea uses line numbers; GitHub uses diff positions natively). -- 2.47.3