mirror of
https://gitea.com/gitea/tea.git
synced 2026-04-25 17:53:37 +02:00
feat(pulls): add resolve, unresolve and review-comments subcommands (#948)
## Summary - Add `tea pulls review-comments <pull-index>` subcommand to list PR review comments with configurable fields (supports table/json/csv/yaml output) - Add `tea pulls resolve <comment-id>` subcommand to mark a review comment as resolved - Add `tea pulls unresolve <comment-id>` subcommand to unmark a review comment as resolved - Follow existing approve/reject pattern with shared `runResolveComment` helper in `review_helpers.go` ## Usage ```bash # List review comments for PR #42 tea pulls review-comments 42 # Resolve comment #789 tea pulls resolve 789 # Unresolve comment #789 tea pulls unresolve 789 # Custom output fields tea pulls review-comments 42 --fields id,path,body,resolver --output json ``` ## New Files | File | Description | |------|-------------| | `cmd/pulls/review_comments.go` | `review-comments` subcommand | | `cmd/pulls/resolve.go` | `resolve` subcommand | | `cmd/pulls/unresolve.go` | `unresolve` subcommand | | `modules/task/pull_review_comment.go` | Task layer: list, resolve, unresolve via SDK | | `modules/print/pull_review_comment.go` | Print formatting with `printable` interface | ## Modified Files | File | Description | |------|-------------| | `cmd/pulls.go` | Register 3 new commands | | `cmd/pulls/review_helpers.go` | Add shared `runResolveComment` helper | ## Test Plan - [x] `go build ./...` passes - [x] `go vet ./...` passes - [x] `tea pulls review-comments <PR-index>` lists comments with IDs - [x] `tea pulls resolve <comment-id>` resolves successfully - [x] `tea pulls unresolve <comment-id>` unresolves successfully - [x] `--output json` produces valid JSON output Reviewed-on: https://gitea.com/gitea/tea/pulls/948 Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com> Co-authored-by: Bo-Yi Wu <appleboy.tw@gmail.com> Co-committed-by: Bo-Yi Wu <appleboy.tw@gmail.com>
This commit is contained in:
@@ -77,6 +77,9 @@ var CmdPulls = cli.Command{
|
|||||||
&pulls.CmdPullsApprove,
|
&pulls.CmdPullsApprove,
|
||||||
&pulls.CmdPullsReject,
|
&pulls.CmdPullsReject,
|
||||||
&pulls.CmdPullsMerge,
|
&pulls.CmdPullsMerge,
|
||||||
|
&pulls.CmdPullsReviewComments,
|
||||||
|
&pulls.CmdPullsResolve,
|
||||||
|
&pulls.CmdPullsUnresolve,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
30
cmd/pulls/resolve.go
Normal file
30
cmd/pulls/resolve.go
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package pulls
|
||||||
|
|
||||||
|
import (
|
||||||
|
stdctx "context"
|
||||||
|
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/task"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdPullsResolve resolves a review comment on a pull request
|
||||||
|
var CmdPullsResolve = cli.Command{
|
||||||
|
Name: "resolve",
|
||||||
|
Usage: "Resolve a review comment on a pull request",
|
||||||
|
Description: "Resolve a review comment on a pull request",
|
||||||
|
ArgsUsage: "<comment id>",
|
||||||
|
Action: func(_ stdctx.Context, cmd *cli.Command) error {
|
||||||
|
ctx, err := context.InitCommand(cmd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return runResolveComment(ctx, task.ResolvePullReviewComment)
|
||||||
|
},
|
||||||
|
Flags: flags.AllDefaultFlags,
|
||||||
|
}
|
||||||
63
cmd/pulls/review_comments.go
Normal file
63
cmd/pulls/review_comments.go
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package pulls
|
||||||
|
|
||||||
|
import (
|
||||||
|
stdctx "context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/print"
|
||||||
|
"code.gitea.io/tea/modules/task"
|
||||||
|
"code.gitea.io/tea/modules/utils"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
var reviewCommentFieldsFlag = flags.FieldsFlag(print.PullReviewCommentFields, []string{
|
||||||
|
"id", "path", "line", "body", "reviewer", "resolver",
|
||||||
|
})
|
||||||
|
|
||||||
|
// CmdPullsReviewComments lists review comments on a pull request
|
||||||
|
var CmdPullsReviewComments = cli.Command{
|
||||||
|
Name: "review-comments",
|
||||||
|
Aliases: []string{"rc"},
|
||||||
|
Usage: "List review comments on a pull request",
|
||||||
|
Description: "List review comments on a pull request",
|
||||||
|
ArgsUsage: "<pull index>",
|
||||||
|
Action: runPullsReviewComments,
|
||||||
|
Flags: append([]cli.Flag{reviewCommentFieldsFlag}, flags.AllDefaultFlags...),
|
||||||
|
}
|
||||||
|
|
||||||
|
func runPullsReviewComments(_ stdctx.Context, cmd *cli.Command) error {
|
||||||
|
ctx, err := context.InitCommand(cmd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := ctx.Ensure(context.CtxRequirement{RemoteRepo: true}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx.Args().Len() < 1 {
|
||||||
|
return fmt.Errorf("pull request index is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
idx, err := utils.ArgToIndex(ctx.Args().First())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
comments, err := task.ListPullReviewComments(ctx, idx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fields, err := reviewCommentFieldsFlag.GetValues(cmd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return print.PullReviewCommentsList(comments, ctx.Output, fields)
|
||||||
|
}
|
||||||
@@ -40,3 +40,21 @@ func runPullReview(ctx *context.TeaContext, state gitea.ReviewStateType, require
|
|||||||
|
|
||||||
return task.CreatePullReview(ctx, idx, state, comment, nil)
|
return task.CreatePullReview(ctx, idx, state, comment, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// runResolveComment handles the common logic for resolving/unresolving review comments
|
||||||
|
func runResolveComment(ctx *context.TeaContext, action func(*context.TeaContext, int64) error) error {
|
||||||
|
if err := ctx.Ensure(context.CtxRequirement{RemoteRepo: true}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx.Args().Len() < 1 {
|
||||||
|
return fmt.Errorf("comment ID is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
commentID, err := utils.ArgToIndex(ctx.Args().First())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return action(ctx, commentID)
|
||||||
|
}
|
||||||
|
|||||||
30
cmd/pulls/unresolve.go
Normal file
30
cmd/pulls/unresolve.go
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package pulls
|
||||||
|
|
||||||
|
import (
|
||||||
|
stdctx "context"
|
||||||
|
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/task"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdPullsUnresolve unresolves a review comment on a pull request
|
||||||
|
var CmdPullsUnresolve = cli.Command{
|
||||||
|
Name: "unresolve",
|
||||||
|
Usage: "Unresolve a review comment on a pull request",
|
||||||
|
Description: "Unresolve a review comment on a pull request",
|
||||||
|
ArgsUsage: "<comment id>",
|
||||||
|
Action: func(_ stdctx.Context, cmd *cli.Command) error {
|
||||||
|
ctx, err := context.InitCommand(cmd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return runResolveComment(ctx, task.UnresolvePullReviewComment)
|
||||||
|
},
|
||||||
|
Flags: flags.AllDefaultFlags,
|
||||||
|
}
|
||||||
40
docs/CLI.md
40
docs/CLI.md
@@ -483,6 +483,46 @@ Merge a pull request
|
|||||||
|
|
||||||
**--title, -t**="": Merge commit title
|
**--title, -t**="": Merge commit title
|
||||||
|
|
||||||
|
### review-comments, rc
|
||||||
|
|
||||||
|
List review comments on a pull request
|
||||||
|
|
||||||
|
**--fields, -f**="": Comma-separated list of fields to print. Available values:
|
||||||
|
id,body,reviewer,path,line,resolver,created,updated,url
|
||||||
|
(default: "id,path,line,body,reviewer,resolver")
|
||||||
|
|
||||||
|
**--login, -l**="": Use a different Gitea Login. Optional
|
||||||
|
|
||||||
|
**--output, -o**="": Output format. (simple, table, csv, tsv, yaml, json)
|
||||||
|
|
||||||
|
**--remote, -R**="": Discover Gitea login from remote. Optional
|
||||||
|
|
||||||
|
**--repo, -r**="": Override local repository path or gitea repository slug to interact with. Optional
|
||||||
|
|
||||||
|
### resolve
|
||||||
|
|
||||||
|
Resolve a review comment on a pull request
|
||||||
|
|
||||||
|
**--login, -l**="": Use a different Gitea Login. Optional
|
||||||
|
|
||||||
|
**--output, -o**="": Output format. (simple, table, csv, tsv, yaml, json)
|
||||||
|
|
||||||
|
**--remote, -R**="": Discover Gitea login from remote. Optional
|
||||||
|
|
||||||
|
**--repo, -r**="": Override local repository path or gitea repository slug to interact with. Optional
|
||||||
|
|
||||||
|
### unresolve
|
||||||
|
|
||||||
|
Unresolve a review comment on a pull request
|
||||||
|
|
||||||
|
**--login, -l**="": Use a different Gitea Login. Optional
|
||||||
|
|
||||||
|
**--output, -o**="": Output format. (simple, table, csv, tsv, yaml, json)
|
||||||
|
|
||||||
|
**--remote, -R**="": Discover Gitea login from remote. Optional
|
||||||
|
|
||||||
|
**--repo, -r**="": Override local repository path or gitea repository slug to interact with. Optional
|
||||||
|
|
||||||
## labels, label
|
## labels, label
|
||||||
|
|
||||||
Manage issue labels
|
Manage issue labels
|
||||||
|
|||||||
73
modules/print/pull_review_comment.go
Normal file
73
modules/print/pull_review_comment.go
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package print
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/sdk/gitea"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PullReviewCommentFields are all available fields to print with PullReviewCommentsList()
|
||||||
|
var PullReviewCommentFields = []string{
|
||||||
|
"id",
|
||||||
|
"body",
|
||||||
|
"reviewer",
|
||||||
|
"path",
|
||||||
|
"line",
|
||||||
|
"resolver",
|
||||||
|
"created",
|
||||||
|
"updated",
|
||||||
|
"url",
|
||||||
|
}
|
||||||
|
|
||||||
|
// PullReviewCommentsList prints a listing of pull review comments
|
||||||
|
func PullReviewCommentsList(comments []*gitea.PullReviewComment, output string, fields []string) error {
|
||||||
|
printables := make([]printable, len(comments))
|
||||||
|
for i, c := range comments {
|
||||||
|
printables[i] = &printablePullReviewComment{c}
|
||||||
|
}
|
||||||
|
t := tableFromItems(fields, printables, isMachineReadable(output))
|
||||||
|
return t.print(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
type printablePullReviewComment struct {
|
||||||
|
*gitea.PullReviewComment
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x printablePullReviewComment) FormatField(field string, machineReadable bool) string {
|
||||||
|
switch field {
|
||||||
|
case "id":
|
||||||
|
return fmt.Sprintf("%d", x.ID)
|
||||||
|
case "body":
|
||||||
|
return x.Body
|
||||||
|
case "reviewer":
|
||||||
|
if x.Reviewer != nil {
|
||||||
|
return formatUserName(x.Reviewer)
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
case "path":
|
||||||
|
return x.Path
|
||||||
|
case "line":
|
||||||
|
if x.LineNum != 0 {
|
||||||
|
return fmt.Sprintf("%d", x.LineNum)
|
||||||
|
}
|
||||||
|
if x.OldLineNum != 0 {
|
||||||
|
return fmt.Sprintf("%d", x.OldLineNum)
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
case "resolver":
|
||||||
|
if x.Resolver != nil {
|
||||||
|
return formatUserName(x.Resolver)
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
case "created":
|
||||||
|
return FormatTime(x.Created, machineReadable)
|
||||||
|
case "updated":
|
||||||
|
return FormatTime(x.Updated, machineReadable)
|
||||||
|
case "url":
|
||||||
|
return x.HTMLURL
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
60
modules/task/pull_review_comment.go
Normal file
60
modules/task/pull_review_comment.go
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package task
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/sdk/gitea"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ListPullReviewComments lists all review comments across all reviews for a PR
|
||||||
|
func ListPullReviewComments(ctx *context.TeaContext, idx int64) ([]*gitea.PullReviewComment, error) {
|
||||||
|
c := ctx.Login.Client()
|
||||||
|
|
||||||
|
reviews, _, err := c.ListPullReviews(ctx.Owner, ctx.Repo, idx, gitea.ListPullReviewsOptions{
|
||||||
|
ListOptions: gitea.ListOptions{Page: -1},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var allComments []*gitea.PullReviewComment
|
||||||
|
for _, review := range reviews {
|
||||||
|
comments, _, err := c.ListPullReviewComments(ctx.Owner, ctx.Repo, idx, review.ID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
allComments = append(allComments, comments...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return allComments, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResolvePullReviewComment resolves a review comment
|
||||||
|
func ResolvePullReviewComment(ctx *context.TeaContext, commentID int64) error {
|
||||||
|
c := ctx.Login.Client()
|
||||||
|
|
||||||
|
_, err := c.ResolvePullReviewComment(ctx.Owner, ctx.Repo, commentID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Comment %d resolved\n", commentID)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnresolvePullReviewComment unresolves a review comment
|
||||||
|
func UnresolvePullReviewComment(ctx *context.TeaContext, commentID int64) error {
|
||||||
|
c := ctx.Login.Client()
|
||||||
|
|
||||||
|
_, err := c.UnresolvePullReviewComment(ctx.Owner, ctx.Repo, commentID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Comment %d unresolved\n", commentID)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user