mirror of
https://gitea.com/gitea/tea.git
synced 2026-10-26 11:01:25 +01:00
revert: tea 1.0 rewrite (#1122–#1126) (#1146)
Reverts #1126, #1125, #1124, #1123 and #1122. `go.mod` keeps the Go 1.26 line with toolchain `go1.26.9` (instead of the original `go1.26.6`) so the security fix from #1131 isn't lost. reopens #69 reopens #141 reopens #162 reopens #172 reopens #197 reopens #232 reopens #240 reopens #246 reopens #323 reopens #325 reopens #326 reopens #328 reopens #344 reopens #351 reopens #373 reopens #403 reopens #406 reopens #417 reopens #430 reopens #457 reopens #480 reopens #482 reopens #489 reopens #490 reopens #498 reopens #500 reopens #504 reopens #510 reopens #516 reopens #523 reopens #556 reopens #560 reopens #569 reopens #571 reopens #602 reopens #632 reopens #633 reopens #643 reopens #666 reopens #670 reopens #672 reopens #677 reopens #683 reopens #697 reopens #726 reopens #737 reopens #770 reopens #779 reopens #804 reopens #817 reopens #831 reopens #833 reopens #834 reopens #908 reopens #966 reopens #1023 reopens #1029 reopens #1032 reopens #1050 reopens #1052 reopens #1069 reopens #1073 reopens #1087 reopens #1091 reopens #1094 reopens #1110 reopens #1115 reopens #1116 reopens #1118 Reviewed-on: https://gitea.com/gitea/tea/pulls/1146 Reviewed-by: TheFox0x7 <95654+thefox0x7@noreply.gitea.com>
This commit is contained in:
1 parent
33bb12729e
commit
c5f674111a
604 files changed
+33711
-35545
No files matched your search
@@ -0,0 +1,31 @@
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package pulls
|
||||
|
||||
import (
|
||||
stdctx "context"
|
||||
|
||||
gitea "gitea.dev/sdk"
|
||||
"github.com/urfave/cli/v3"
|
||||
|
||||
"gitea.dev/tea/cmd/flags"
|
||||
"gitea.dev/tea/modules/context"
|
||||
)
|
||||
|
||||
// CmdPullsApprove approves a PR
|
||||
var CmdPullsApprove = cli.Command{
|
||||
Name: "approve",
|
||||
Aliases: []string{"lgtm", "a"},
|
||||
Usage: "Approve a pull request",
|
||||
Description: "Approve a pull request",
|
||||
ArgsUsage: "<pull index> [<comment>]",
|
||||
Action: func(requestCtx stdctx.Context, cmd *cli.Command) error {
|
||||
ctx, err := context.InitCommand(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return runPullReview(requestCtx, ctx, gitea.ReviewStateApproved, false)
|
||||
},
|
||||
Flags: flags.AllDefaultFlags,
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package pulls
|
||||
|
||||
import (
|
||||
stdctx "context"
|
||||
"fmt"
|
||||
|
||||
"gitea.dev/tea/cmd/flags"
|
||||
"gitea.dev/tea/modules/context"
|
||||
"gitea.dev/tea/modules/interact"
|
||||
"gitea.dev/tea/modules/task"
|
||||
"gitea.dev/tea/modules/utils"
|
||||
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
// CmdPullsCheckout is a command to locally checkout the given PR
|
||||
var CmdPullsCheckout = cli.Command{
|
||||
Name: "checkout",
|
||||
Aliases: []string{"co"},
|
||||
Usage: "Locally check out the given PR",
|
||||
Description: `Locally check out the given PR`,
|
||||
Action: runPullsCheckout,
|
||||
ArgsUsage: "<pull index>",
|
||||
Flags: append([]cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "branch",
|
||||
Aliases: []string{"b"},
|
||||
Usage: "Create a local branch if it doesn't exist yet",
|
||||
},
|
||||
}, flags.AllDefaultFlags...),
|
||||
}
|
||||
|
||||
func runPullsCheckout(requestCtx stdctx.Context, cmd *cli.Command) error {
|
||||
ctx, err := context.InitCommand(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ctx.Ensure(context.CtxRequirement{
|
||||
LocalRepo: true,
|
||||
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
|
||||
}
|
||||
|
||||
if err := task.PullCheckout(requestCtx, ctx.Login, ctx.Owner, ctx.Repo, ctx.Bool("branch"), idx, interact.PromptPassword); err != nil && !interact.IsQuitting(err) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package pulls
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
stdctx "context"
|
||||
|
||||
"github.com/urfave/cli/v3"
|
||||
|
||||
"gitea.dev/tea/cmd/flags"
|
||||
"gitea.dev/tea/modules/context"
|
||||
"gitea.dev/tea/modules/interact"
|
||||
"gitea.dev/tea/modules/task"
|
||||
"gitea.dev/tea/modules/utils"
|
||||
)
|
||||
|
||||
// CmdPullsClean removes the remote and local feature branches, if a PR is merged.
|
||||
var CmdPullsClean = cli.Command{
|
||||
Name: "clean",
|
||||
Usage: "Deletes local & remote feature-branches for a closed pull request",
|
||||
Description: `Deletes local & remote feature-branches for a closed pull request`,
|
||||
ArgsUsage: "<pull index>",
|
||||
Action: runPullsClean,
|
||||
Flags: append([]cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "ignore-sha",
|
||||
Usage: "Find the local branch by name instead of commit hash (less precise)",
|
||||
},
|
||||
}, flags.AllDefaultFlags...),
|
||||
}
|
||||
|
||||
func runPullsClean(requestCtx stdctx.Context, cmd *cli.Command) error {
|
||||
ctx, err := context.InitCommand(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ctx.Ensure(context.CtxRequirement{LocalRepo: 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
|
||||
}
|
||||
|
||||
if err := task.PullClean(requestCtx, ctx.Login, ctx.Owner, ctx.Repo, idx, ctx.Bool("ignore-sha"), interact.PromptPassword); err != nil && !interact.IsQuitting(err) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package pulls
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gitea.dev/sdk"
|
||||
|
||||
"gitea.dev/tea/cmd/flags"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
// CmdPullsClose closes a given open pull request
|
||||
var CmdPullsClose = cli.Command{
|
||||
Name: "close",
|
||||
Usage: "Change state of one or more pull requests to 'closed'",
|
||||
Description: `Change state of one or more pull requests to 'closed'`,
|
||||
ArgsUsage: "<pull index> [<pull index>...]",
|
||||
Action: func(ctx context.Context, cmd *cli.Command) error {
|
||||
s := gitea.StateClosed
|
||||
return editPullState(ctx, cmd, gitea.EditPullRequestOption{State: &s})
|
||||
},
|
||||
Flags: flags.AllDefaultFlags,
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package pulls
|
||||
|
||||
import (
|
||||
stdctx "context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
gitea "gitea.dev/sdk"
|
||||
"github.com/urfave/cli/v3"
|
||||
|
||||
"gitea.dev/tea/cmd/flags"
|
||||
"gitea.dev/tea/modules/context"
|
||||
"gitea.dev/tea/modules/interact"
|
||||
"gitea.dev/tea/modules/print"
|
||||
"gitea.dev/tea/modules/task"
|
||||
"gitea.dev/tea/modules/utils"
|
||||
)
|
||||
|
||||
// CmdPullsCreate creates a pull request
|
||||
var CmdPullsCreate = cli.Command{
|
||||
Name: "create",
|
||||
Aliases: []string{"c"},
|
||||
Usage: "Create a pull-request",
|
||||
Description: "Create a pull-request in the current repo",
|
||||
Action: runPullsCreate,
|
||||
Flags: append([]cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "head",
|
||||
Usage: "Branch name of the PR source (default is current one). To specify a different head repo, use <user>:<branch>",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "base",
|
||||
Aliases: []string{"b"},
|
||||
Usage: "Branch name of the PR target (default is repos default branch)",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "allow-maintainer-edits",
|
||||
Aliases: []string{"edits"},
|
||||
Usage: "Enable maintainers to push to the base branch of created pull",
|
||||
Value: true,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "agit",
|
||||
Usage: "Create an agit flow pull request",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "topic",
|
||||
Usage: "Topic name for agit flow pull request",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "draft",
|
||||
Usage: "Create as a draft (prepends \"WIP: \" to the title; Gitea treats WIP-prefixed PRs as drafts)",
|
||||
},
|
||||
}, flags.IssuePRCreateFlags...),
|
||||
}
|
||||
|
||||
func runPullsCreate(requestCtx stdctx.Context, cmd *cli.Command) error {
|
||||
ctx, err := context.InitCommand(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Interactive mode and head-branch defaulting both need a local repo.
|
||||
// When --head is given explicitly the user can target a cross-fork PR
|
||||
// from outside a working tree (e.g. with --repo <owner>/<repo>);
|
||||
// task.CreatePull only consults ctx.LocalRepo when head is empty.
|
||||
needsLocalRepo := ctx.IsInteractiveMode() || len(ctx.String("head")) == 0
|
||||
requirement := context.CtxRequirement{RemoteRepo: true}
|
||||
if needsLocalRepo {
|
||||
requirement.LocalRepo = true
|
||||
}
|
||||
if err := ctx.Ensure(requirement); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// no args -> interactive mode
|
||||
if ctx.IsInteractiveMode() {
|
||||
if err := interact.CreatePull(requestCtx, ctx); err != nil && !interact.IsQuitting(err) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// agit flow creates the PR via git push and returns no PR object, so
|
||||
// --output cannot be honored there; fail fast before any API calls
|
||||
if ctx.Bool("agit") && ctx.IsSet("output") {
|
||||
return fmt.Errorf("--output cannot be combined with --agit: the PR is created via git push, so no pull request object is available to print")
|
||||
}
|
||||
|
||||
// else use args to create PR
|
||||
opts, err := flags.GetIssuePRCreateFlags(requestCtx, ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if ctx.Bool("draft") {
|
||||
opts.Title = utils.AddDraftPrefix(opts.Title)
|
||||
}
|
||||
|
||||
if ctx.Bool("agit") {
|
||||
return task.CreateAgitFlowPull(
|
||||
requestCtx,
|
||||
ctx,
|
||||
ctx.String("remote"),
|
||||
ctx.String("head"),
|
||||
ctx.String("base"),
|
||||
ctx.String("topic"),
|
||||
opts,
|
||||
interact.PromptPassword,
|
||||
)
|
||||
}
|
||||
|
||||
var allowMaintainerEdits *bool
|
||||
if ctx.IsSet("allow-maintainer-edits") {
|
||||
allowMaintainerEdits = gitea.OptionalBool(ctx.Bool("allow-maintainer-edits"))
|
||||
}
|
||||
|
||||
pr, err := task.CreatePull(
|
||||
requestCtx,
|
||||
ctx,
|
||||
ctx.String("base"),
|
||||
ctx.String("head"),
|
||||
allowMaintainerEdits,
|
||||
opts,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if ctx.IsSet("output") {
|
||||
switch ctx.String("output") {
|
||||
case "json":
|
||||
return writeCreatedPullAsJSON(ctx.Writer, pr)
|
||||
}
|
||||
}
|
||||
|
||||
print.PullDetails(pr, nil, nil)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// createdPullJSON is the machine-readable representation of a freshly
|
||||
// created pull request. A new PR has no reviews, comments or CI yet, so
|
||||
// this is intentionally leaner than the detail view's pullData (cmd/pulls.go).
|
||||
type createdPullJSON struct {
|
||||
Index int64 `json:"index"`
|
||||
Title string `json:"title"`
|
||||
URL string `json:"url"`
|
||||
State gitea.StateType `json:"state"`
|
||||
Base string `json:"base"`
|
||||
Head string `json:"head"`
|
||||
}
|
||||
|
||||
func writeCreatedPullAsJSON(w io.Writer, pr *gitea.PullRequest) error {
|
||||
return json.NewEncoder(w).Encode(createdPullJSON{
|
||||
Index: pr.Index,
|
||||
Title: pr.Title,
|
||||
URL: pr.HTMLURL,
|
||||
State: pr.State,
|
||||
Base: pr.Base.Ref,
|
||||
Head: pr.Head.Ref,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package pulls_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"gitea.dev/tea/cmd"
|
||||
"gitea.dev/tea/modules/config"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// TestPullsCreateAgitOutputRejected verifies that --output (parsed via the
|
||||
// urfave/cli v3 ancestor-flag cascade, since create itself does not declare
|
||||
// it) is rejected for the agit flow before any API call or git push happens.
|
||||
func TestPullsCreateAgitOutputRejected(t *testing.T) {
|
||||
config.SetConfigForTesting(config.LocalConfig{
|
||||
Logins: []config.Login{{
|
||||
Name: "testLogin",
|
||||
URL: "https://gitea.example.com",
|
||||
Token: "test-token",
|
||||
User: "testUser",
|
||||
Default: true,
|
||||
}},
|
||||
})
|
||||
t.Cleanup(func() {
|
||||
config.SetConfigForTesting(config.LocalConfig{})
|
||||
})
|
||||
|
||||
app := cmd.App()
|
||||
args := []string{
|
||||
"tea", "pulls", "create",
|
||||
"--agit",
|
||||
"--output", "json",
|
||||
"--head", "topic-branch",
|
||||
"--title", "test",
|
||||
"--login", "testLogin",
|
||||
"--repo", "user/repo",
|
||||
}
|
||||
|
||||
err := app.Run(context.Background(), args)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "--output cannot be combined with --agit")
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package pulls
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
gitea "gitea.dev/sdk"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestWriteCreatedPullAsJSON(t *testing.T) {
|
||||
pr := &gitea.PullRequest{
|
||||
Index: 33,
|
||||
Title: "test title",
|
||||
HTMLURL: "https://gitea.example.com/owner/repo/pulls/33",
|
||||
State: gitea.StateOpen,
|
||||
Base: &gitea.PRBranchInfo{Ref: "main"},
|
||||
Head: &gitea.PRBranchInfo{Ref: "feature"},
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
require.NoError(t, writeCreatedPullAsJSON(&buf, pr))
|
||||
|
||||
var got map[string]any
|
||||
require.NoError(t, json.Unmarshal(buf.Bytes(), &got))
|
||||
|
||||
assert.Equal(t, float64(33), got["index"])
|
||||
assert.Equal(t, "test title", got["title"])
|
||||
assert.Equal(t, "https://gitea.example.com/owner/repo/pulls/33", got["url"])
|
||||
assert.Equal(t, "open", got["state"])
|
||||
assert.Equal(t, "main", got["base"])
|
||||
assert.Equal(t, "feature", got["head"])
|
||||
|
||||
// exactly the lean field set, nothing extra
|
||||
assert.Len(t, got, 6)
|
||||
|
||||
// machine-readable output must not contain terminal escape sequences
|
||||
assert.NotContains(t, buf.String(), "\x1b")
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package pulls
|
||||
|
||||
import (
|
||||
stdctx "context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
gitea "gitea.dev/sdk"
|
||||
|
||||
"gitea.dev/tea/cmd/flags"
|
||||
"gitea.dev/tea/modules/context"
|
||||
"gitea.dev/tea/modules/print"
|
||||
"gitea.dev/tea/modules/task"
|
||||
"gitea.dev/tea/modules/utils"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
// applyDraftFlag mutates opts.Title according to --draft / --ready.
|
||||
// If a flag is set but --title isn't, it fetches the current title from the server.
|
||||
// Returns an error if both --draft and --ready are set, or on a server error.
|
||||
func applyDraftFlag(requestCtx stdctx.Context, ctx *context.TeaContext, client *gitea.Client, idx int64, opts *task.EditIssueOption) error {
|
||||
draft := ctx.Bool("draft")
|
||||
ready := ctx.Bool("ready")
|
||||
if !draft && !ready {
|
||||
return nil
|
||||
}
|
||||
if draft && ready {
|
||||
return fmt.Errorf("--draft and --ready are mutually exclusive")
|
||||
}
|
||||
|
||||
var current string
|
||||
if opts.Title != nil {
|
||||
current = *opts.Title
|
||||
} else {
|
||||
pr, _, err := client.PullRequests.GetPullRequest(requestCtx, ctx.Owner, ctx.Repo, idx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not fetch pull request #%d: %s", idx, err)
|
||||
}
|
||||
current = pr.Title
|
||||
}
|
||||
|
||||
var next string
|
||||
if draft {
|
||||
next = utils.AddDraftPrefix(current)
|
||||
} else {
|
||||
next = utils.StripDraftPrefix(current)
|
||||
}
|
||||
if next != current || opts.Title != nil {
|
||||
opts.Title = &next
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CmdPullsEdit is the subcommand of pulls to edit pull requests
|
||||
var CmdPullsEdit = cli.Command{
|
||||
Name: "edit",
|
||||
Aliases: []string{"e"},
|
||||
Usage: "Edit one or more pull requests",
|
||||
Description: `Edit one or more pull requests. To unset a property again,
|
||||
use an empty string (eg. --milestone "").`,
|
||||
ArgsUsage: "<idx> [<idx>...]",
|
||||
Action: runPullsEdit,
|
||||
Flags: append(flags.IssuePREditFlags,
|
||||
&cli.StringFlag{
|
||||
Name: "add-reviewers",
|
||||
Aliases: []string{"r"},
|
||||
Usage: "Comma-separated list of usernames to request review from",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "remove-reviewers",
|
||||
Usage: "Comma-separated list of usernames to remove from reviewers",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "draft",
|
||||
Usage: "Mark as draft by prepending \"WIP: \" to the title (idempotent)",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "ready",
|
||||
Usage: "Mark as ready for review by stripping any leading \"WIP: \" or \"[WIP]\" prefix",
|
||||
},
|
||||
),
|
||||
}
|
||||
|
||||
func runPullsEdit(requestCtx 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 !cmd.Args().Present() {
|
||||
return fmt.Errorf("must specify at least one pull request index")
|
||||
}
|
||||
|
||||
opts, err := flags.GetIssuePREditFlags(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if cmd.IsSet("add-reviewers") {
|
||||
opts.AddReviewers = strings.Split(cmd.String("add-reviewers"), ",")
|
||||
}
|
||||
if cmd.IsSet("remove-reviewers") {
|
||||
opts.RemoveReviewers = strings.Split(cmd.String("remove-reviewers"), ",")
|
||||
}
|
||||
|
||||
indices, err := utils.ArgsToIndices(ctx.Args().Slice())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client := ctx.Login.Client()
|
||||
for _, opts.Index = range indices {
|
||||
if err := applyDraftFlag(requestCtx, ctx, client, opts.Index, opts); err != nil {
|
||||
return err
|
||||
}
|
||||
pr, err := task.EditPull(requestCtx, ctx, client, *opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ctx.Args().Len() > 1 {
|
||||
fmt.Println(pr.HTMLURL)
|
||||
} else {
|
||||
print.PullDetails(pr, nil, nil)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// editPullState abstracts the arg parsing to edit the given pull request
|
||||
func editPullState(requestCtx stdctx.Context, cmd *cli.Command, opts gitea.EditPullRequestOption) 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() == 0 {
|
||||
return fmt.Errorf("pull request index is required")
|
||||
}
|
||||
|
||||
indices, err := utils.ArgsToIndices(ctx.Args().Slice())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client := ctx.Login.Client()
|
||||
for _, index := range indices {
|
||||
pr, _, err := client.PullRequests.EditPullRequest(requestCtx, ctx.Owner, ctx.Repo, index, opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(indices) > 1 {
|
||||
fmt.Println(pr.HTMLURL)
|
||||
} else {
|
||||
print.PullDetails(pr, nil, nil)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package pulls
|
||||
|
||||
import (
|
||||
stdctx "context"
|
||||
"fmt"
|
||||
"slices"
|
||||
|
||||
"gitea.dev/sdk"
|
||||
"github.com/urfave/cli/v3"
|
||||
|
||||
"gitea.dev/tea/cmd/flags"
|
||||
"gitea.dev/tea/modules/context"
|
||||
"gitea.dev/tea/modules/print"
|
||||
)
|
||||
|
||||
var pullFieldsFlag = flags.FieldsFlag(print.PullFields, []string{
|
||||
"index", "title", "state", "author", "milestone", "updated", "labels",
|
||||
})
|
||||
|
||||
// CmdPullsList represents a sub command of issues to list pulls
|
||||
var CmdPullsList = cli.Command{
|
||||
Name: "list",
|
||||
Aliases: []string{"ls"},
|
||||
Usage: "List pull requests of the repository",
|
||||
Description: `List pull requests of the repository`,
|
||||
ArgsUsage: " ", // command does not accept arguments
|
||||
Action: RunPullsList,
|
||||
Flags: append([]cli.Flag{pullFieldsFlag}, flags.PRListingFlags...),
|
||||
}
|
||||
|
||||
// RunPullsList return list of pulls
|
||||
func RunPullsList(requestCtx 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
|
||||
}
|
||||
|
||||
state, err := flags.ParseState(ctx.String("state"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client := ctx.Login.Client()
|
||||
prs, _, err := client.PullRequests.ListRepoPullRequests(requestCtx, ctx.Owner, ctx.Repo, gitea.ListPullRequestsOptions{
|
||||
ListOptions: flags.GetListOptions(cmd),
|
||||
State: state,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fields, err := pullFieldsFlag.GetValues(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var ciStatuses map[int64]*gitea.CombinedStatus
|
||||
if slices.Contains(fields, "ci") {
|
||||
ciStatuses = map[int64]*gitea.CombinedStatus{}
|
||||
for _, pr := range prs {
|
||||
if pr.Head == nil || pr.Head.Sha == "" {
|
||||
continue
|
||||
}
|
||||
ci, _, err := client.Repositories.GetCombinedStatus(requestCtx, ctx.Owner, ctx.Repo, pr.Head.Sha)
|
||||
if err != nil {
|
||||
fmt.Printf("error fetching CI status for PR #%d: %v\n", pr.Index, err)
|
||||
continue
|
||||
}
|
||||
ciStatuses[pr.Index] = ci
|
||||
}
|
||||
}
|
||||
|
||||
return print.PullsList(prs, ctx.Output, fields, ciStatuses)
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package pulls
|
||||
|
||||
import (
|
||||
stdctx "context"
|
||||
|
||||
gitea "gitea.dev/sdk"
|
||||
"github.com/urfave/cli/v3"
|
||||
|
||||
"gitea.dev/tea/cmd/flags"
|
||||
"gitea.dev/tea/modules/context"
|
||||
"gitea.dev/tea/modules/interact"
|
||||
"gitea.dev/tea/modules/task"
|
||||
"gitea.dev/tea/modules/utils"
|
||||
)
|
||||
|
||||
// CmdPullsMerge merges a PR
|
||||
var CmdPullsMerge = cli.Command{
|
||||
Name: "merge",
|
||||
Aliases: []string{"m"},
|
||||
Usage: "Merge a pull request",
|
||||
Description: "Merge a pull request",
|
||||
ArgsUsage: "<pull index>",
|
||||
Flags: append([]cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "style",
|
||||
Aliases: []string{"s"},
|
||||
Usage: "Kind of merge to perform: merge, rebase, squash, rebase-merge",
|
||||
Value: "merge",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "title",
|
||||
Aliases: []string{"t"},
|
||||
Usage: "Merge commit title",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "message",
|
||||
Aliases: []string{"m"},
|
||||
Usage: "Merge commit message",
|
||||
},
|
||||
}, flags.AllDefaultFlags...),
|
||||
Action: func(requestCtx 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 {
|
||||
// If no PR index is provided, try interactive mode
|
||||
if err := interact.MergePull(requestCtx, ctx); err != nil && !interact.IsQuitting(err) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
idx, err := utils.ArgToIndex(ctx.Args().First())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return task.PullMerge(requestCtx, ctx.Login, ctx.Owner, ctx.Repo, idx, gitea.MergePullRequestOption{
|
||||
Style: gitea.MergeStyle(ctx.String("style")),
|
||||
Title: ctx.String("title"),
|
||||
Message: ctx.String("message"),
|
||||
})
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package pulls
|
||||
|
||||
import (
|
||||
stdctx "context"
|
||||
|
||||
gitea "gitea.dev/sdk"
|
||||
"github.com/urfave/cli/v3"
|
||||
|
||||
"gitea.dev/tea/cmd/flags"
|
||||
"gitea.dev/tea/modules/context"
|
||||
)
|
||||
|
||||
// CmdPullsReject requests changes to a PR
|
||||
var CmdPullsReject = cli.Command{
|
||||
Name: "reject",
|
||||
Usage: "Request changes to a pull request",
|
||||
Description: "Request changes to a pull request",
|
||||
ArgsUsage: "<pull index> <reason>",
|
||||
Action: func(requestCtx stdctx.Context, cmd *cli.Command) error {
|
||||
ctx, err := context.InitCommand(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return runPullReview(requestCtx, ctx, gitea.ReviewStateRequestChanges, true)
|
||||
},
|
||||
Flags: flags.AllDefaultFlags,
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package pulls
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gitea.dev/sdk"
|
||||
|
||||
"gitea.dev/tea/cmd/flags"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
// CmdPullsReopen reopens a given closed pull request
|
||||
var CmdPullsReopen = cli.Command{
|
||||
Name: "reopen",
|
||||
Aliases: []string{"open"},
|
||||
Usage: "Change state of one or more pull requests to 'open'",
|
||||
Description: `Change state of one or more pull requests to 'open'`,
|
||||
ArgsUsage: "<pull index> [<pull index>...]",
|
||||
Action: func(ctx context.Context, cmd *cli.Command) error {
|
||||
s := gitea.StateOpen
|
||||
return editPullState(ctx, cmd, gitea.EditPullRequestOption{State: &s})
|
||||
},
|
||||
Flags: flags.AllDefaultFlags,
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package pulls
|
||||
|
||||
import (
|
||||
stdctx "context"
|
||||
|
||||
"gitea.dev/tea/cmd/flags"
|
||||
"gitea.dev/tea/modules/context"
|
||||
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
// CmdPullsReply replies to a review comment on a pull request.
|
||||
var CmdPullsReply = cli.Command{
|
||||
Name: "reply",
|
||||
Usage: "Reply to a pull request review comment",
|
||||
Description: "Reply to a pull request review comment",
|
||||
ArgsUsage: "<pull index> <comment id> [<reply>]",
|
||||
Action: func(requestCtx stdctx.Context, cmd *cli.Command) error {
|
||||
ctx, err := context.InitCommand(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return runPullReviewReply(requestCtx, ctx)
|
||||
},
|
||||
Flags: flags.AllDefaultFlags,
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package pulls
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"gitea.dev/tea/modules/config"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestReply(t *testing.T) {
|
||||
config.SetConfigForTesting(config.LocalConfig{
|
||||
Logins: []config.Login{{
|
||||
Name: "testLogin",
|
||||
URL: "https://gitea.example.com",
|
||||
Token: "test-token",
|
||||
User: "testUser",
|
||||
Default: true,
|
||||
}},
|
||||
})
|
||||
t.Cleanup(func() {
|
||||
config.SetConfigForTesting(config.LocalConfig{})
|
||||
})
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args []string
|
||||
wantErr bool
|
||||
errContains string
|
||||
}{
|
||||
{
|
||||
name: "no arguments",
|
||||
args: []string{},
|
||||
wantErr: true,
|
||||
errContains: "pull request index and comment ID are required",
|
||||
},
|
||||
{
|
||||
name: "missing comment id",
|
||||
args: []string{"1"},
|
||||
wantErr: true,
|
||||
errContains: "pull request index and comment ID are required",
|
||||
},
|
||||
{
|
||||
name: "pull index and comment id",
|
||||
args: []string{"1", "2"},
|
||||
wantErr: true,
|
||||
errContains: "no reply content provided",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cmd := CmdPullsReply
|
||||
args := append([]string{"reply"}, tt.args...)
|
||||
args = append(args, "--login", "testLogin", "--repo", "user/repo")
|
||||
err := cmd.Run(context.Background(), args)
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
if tt.errContains != "" {
|
||||
assert.Contains(t, err.Error(), tt.errContains)
|
||||
}
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package pulls
|
||||
|
||||
import (
|
||||
stdctx "context"
|
||||
|
||||
"gitea.dev/tea/cmd/flags"
|
||||
"gitea.dev/tea/modules/context"
|
||||
"gitea.dev/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(requestCtx stdctx.Context, cmd *cli.Command) error {
|
||||
ctx, err := context.InitCommand(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return runResolveComment(requestCtx, ctx, task.ResolvePullReviewComment)
|
||||
},
|
||||
Flags: flags.AllDefaultFlags,
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package pulls
|
||||
|
||||
import (
|
||||
stdctx "context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"gitea.dev/tea/cmd/flags"
|
||||
"gitea.dev/tea/modules/context"
|
||||
"gitea.dev/tea/modules/interact"
|
||||
"gitea.dev/tea/modules/print"
|
||||
"gitea.dev/tea/modules/utils"
|
||||
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
// CmdPullsReview starts an interactive review session
|
||||
var CmdPullsReview = cli.Command{
|
||||
Name: "review",
|
||||
Usage: "Interactively review a pull request",
|
||||
Description: "Interactively review a pull request",
|
||||
ArgsUsage: "<pull index>",
|
||||
Action: func(requestCtx 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().Present() {
|
||||
return fmt.Errorf("must specify at least one PR index")
|
||||
}
|
||||
|
||||
// This command is intentionally interactive. Fail early in CI / non-TTY
|
||||
// contexts rather than hanging on prompts.
|
||||
if os.Getenv("CI") != "" || !print.IsInteractive() || interact.IsStdinPiped() {
|
||||
return fmt.Errorf("pull review requires an interactive terminal")
|
||||
}
|
||||
|
||||
for _, arg := range ctx.Args().Slice() {
|
||||
idx, err := utils.ArgToIndex(arg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := interact.ReviewPull(requestCtx, ctx, idx); err != nil && !interact.IsQuitting(err) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
Flags: flags.AllDefaultFlags,
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package pulls
|
||||
|
||||
import (
|
||||
stdctx "context"
|
||||
"fmt"
|
||||
|
||||
"gitea.dev/tea/cmd/flags"
|
||||
"gitea.dev/tea/modules/context"
|
||||
"gitea.dev/tea/modules/print"
|
||||
"gitea.dev/tea/modules/task"
|
||||
"gitea.dev/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(requestCtx 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(requestCtx, ctx, idx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fields, err := reviewCommentFieldsFlag.GetValues(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return print.PullReviewCommentsList(comments, ctx.Output, fields)
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package pulls
|
||||
|
||||
import (
|
||||
stdctx "context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
gitea "gitea.dev/sdk"
|
||||
|
||||
"gitea.dev/tea/modules/config"
|
||||
"gitea.dev/tea/modules/context"
|
||||
"gitea.dev/tea/modules/interact"
|
||||
"gitea.dev/tea/modules/task"
|
||||
"gitea.dev/tea/modules/theme"
|
||||
"gitea.dev/tea/modules/utils"
|
||||
|
||||
"charm.land/huh/v2"
|
||||
)
|
||||
|
||||
// runPullReview handles the common logic for approving/rejecting pull requests
|
||||
func runPullReview(requestCtx stdctx.Context, ctx *context.TeaContext, state gitea.ReviewStateType, requireComment bool) error {
|
||||
if err := ctx.Ensure(context.CtxRequirement{RemoteRepo: true}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
minArgs := 1
|
||||
if requireComment {
|
||||
minArgs = 2
|
||||
}
|
||||
|
||||
if ctx.Args().Len() < minArgs {
|
||||
if requireComment {
|
||||
return fmt.Errorf("pull request index and comment are required")
|
||||
}
|
||||
return fmt.Errorf("pull request index is required")
|
||||
}
|
||||
|
||||
idx, err := utils.ArgToIndex(ctx.Args().First())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
comment := strings.Join(ctx.Args().Tail(), " ")
|
||||
|
||||
return task.CreatePullReview(requestCtx, ctx, idx, state, comment, nil)
|
||||
}
|
||||
|
||||
// runResolveComment handles the common logic for resolving/unresolving review comments
|
||||
func runResolveComment(requestCtx stdctx.Context, ctx *context.TeaContext, action func(stdctx.Context, *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(requestCtx, ctx, commentID)
|
||||
}
|
||||
|
||||
// runPullReviewReply handles replying to a specific review comment on a pull request.
|
||||
func runPullReviewReply(requestCtx stdctx.Context, ctx *context.TeaContext) error {
|
||||
if err := ctx.Ensure(context.CtxRequirement{RemoteRepo: true}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if ctx.Args().Len() < 2 {
|
||||
return fmt.Errorf("pull request index and comment ID are required")
|
||||
}
|
||||
|
||||
idx, err := utils.ArgToIndex(ctx.Args().First())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
commentID, err := utils.ArgToIndex(ctx.Args().Get(1))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
body, err := getCommentBody(ctx, ctx.Args().Slice()[2:], "Reply(markdown):", "reply")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return task.ReplyToPullReviewComment(requestCtx, ctx, idx, commentID, body)
|
||||
}
|
||||
|
||||
func getCommentBody(ctx *context.TeaContext, extraArgs []string, promptTitle, noun string) (string, error) {
|
||||
body := strings.Join(extraArgs, " ")
|
||||
if interact.IsStdinPiped() {
|
||||
bodyStdin, err := io.ReadAll(ctx.Reader)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(bodyStdin) != 0 {
|
||||
body = strings.Join([]string{body, string(bodyStdin)}, "\n\n")
|
||||
}
|
||||
} else if len(body) == 0 {
|
||||
if err := huh.NewForm(
|
||||
huh.NewGroup(
|
||||
huh.NewText().
|
||||
Title(promptTitle).
|
||||
ExternalEditor(config.GetPreferences().Editor).
|
||||
EditorExtension("md").
|
||||
Value(&body),
|
||||
),
|
||||
).WithTheme(theme.GetTheme()).Run(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
if len(strings.TrimSpace(body)) == 0 {
|
||||
return "", errors.New("no " + noun + " content provided")
|
||||
}
|
||||
|
||||
return body, nil
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
// Copyright 2025 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package pulls
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestReview(t *testing.T) {
|
||||
if os.Getenv("GITEA_TEA_TEST_URL") == "" {
|
||||
t.Skip("GITEA_TEA_TEST_URL is not set, skipping test")
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args []string
|
||||
wantErr bool
|
||||
errContains string
|
||||
}{
|
||||
{
|
||||
name: "no arguments",
|
||||
args: []string{},
|
||||
wantErr: true,
|
||||
errContains: "must specify at least one PR index",
|
||||
},
|
||||
{
|
||||
name: "one argument",
|
||||
args: []string{"1"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "two arguments",
|
||||
args: []string{"1", "2"},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cmd := CmdPullsReview
|
||||
args := append(tt.args, "--repo", "user/repo")
|
||||
err := cmd.Run(t.Context(), args)
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
if tt.errContains != "" {
|
||||
assert.Contains(t, err.Error(), tt.errContains)
|
||||
}
|
||||
return
|
||||
}
|
||||
// Don't assert no error, because we expect an error about the missing
|
||||
// remote. Just assert that the error is not the one we're looking for.
|
||||
if err != nil {
|
||||
assert.NotContains(t, err.Error(), "must specify at least one PR index")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package pulls
|
||||
|
||||
import (
|
||||
stdctx "context"
|
||||
|
||||
"gitea.dev/tea/cmd/flags"
|
||||
"gitea.dev/tea/modules/context"
|
||||
"gitea.dev/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(requestCtx stdctx.Context, cmd *cli.Command) error {
|
||||
ctx, err := context.InitCommand(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return runResolveComment(requestCtx, ctx, task.UnresolvePullReviewComment)
|
||||
},
|
||||
Flags: flags.AllDefaultFlags,
|
||||
}
|
||||
Reference in new issue
Block a user