mirror of
https://gitea.com/gitea/tea.git
synced 2026-02-21 22:03:32 +01:00
support create agit flow pull request (#867)
while looks the alibaba has not maintain [`git-repo-go`](https://github.com/alibaba/git-repo-go/) tool, to make agit flow pull requst can be create quickly. add creating agit flow pull request feature in tea tool example: ```SHELL tea pulls create --agit --remote=origin --topic=test-topic --title="hello world" --description="test1 test 2 test 3" ``` Signed-off-by: a1012112796 <1012112796@qq.com> Reviewed-on: https://gitea.com/gitea/tea/pulls/867 Co-authored-by: a1012112796 <1012112796@qq.com> Co-committed-by: a1012112796 <1012112796@qq.com>
This commit is contained in:
committed by
techknowlogick
parent
82d8a14c73
commit
0d5bf60632
@@ -37,6 +37,14 @@ var CmdPullsCreate = cli.Command{
|
|||||||
Usage: "Enable maintainers to push to the base branch of created pull",
|
Usage: "Enable maintainers to push to the base branch of created pull",
|
||||||
Value: true,
|
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",
|
||||||
|
},
|
||||||
}, flags.IssuePRCreateFlags...),
|
}, flags.IssuePRCreateFlags...),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,6 +69,18 @@ func runPullsCreate(_ stdctx.Context, cmd *cli.Command) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ctx.Bool("agit") {
|
||||||
|
return task.CreateAgitFlowPull(
|
||||||
|
ctx,
|
||||||
|
ctx.String("remote"),
|
||||||
|
ctx.String("head"),
|
||||||
|
ctx.String("base"),
|
||||||
|
ctx.String("topic"),
|
||||||
|
opts,
|
||||||
|
interact.PromptPassword,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
var allowMaintainerEdits *bool
|
var allowMaintainerEdits *bool
|
||||||
if ctx.IsSet("allow-maintainer-edits") {
|
if ctx.IsSet("allow-maintainer-edits") {
|
||||||
allowMaintainerEdits = gitea.OptionalBool(ctx.Bool("allow-maintainer-edits"))
|
allowMaintainerEdits = gitea.OptionalBool(ctx.Bool("allow-maintainer-edits"))
|
||||||
|
|||||||
@@ -345,6 +345,8 @@ Deletes local & remote feature-branches for a closed pull request
|
|||||||
|
|
||||||
Create a pull-request
|
Create a pull-request
|
||||||
|
|
||||||
|
**--agit**: Create an agit flow pull request
|
||||||
|
|
||||||
**--allow-maintainer-edits, --edits**: Enable maintainers to push to the base branch of created pull
|
**--allow-maintainer-edits, --edits**: Enable maintainers to push to the base branch of created pull
|
||||||
|
|
||||||
**--assignees, -a**="": Comma-separated list of usernames to assign
|
**--assignees, -a**="": Comma-separated list of usernames to assign
|
||||||
@@ -371,6 +373,8 @@ Create a pull-request
|
|||||||
|
|
||||||
**--title, -t**="":
|
**--title, -t**="":
|
||||||
|
|
||||||
|
**--topic**="": Topic name for agit flow pull request
|
||||||
|
|
||||||
### close
|
### close
|
||||||
|
|
||||||
Change state of one or more pull requests to 'closed'
|
Change state of one or more pull requests to 'closed'
|
||||||
|
|||||||
@@ -4,8 +4,10 @@
|
|||||||
package git
|
package git
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
|
||||||
"github.com/go-git/go-git/v5"
|
"github.com/go-git/go-git/v5"
|
||||||
git_config "github.com/go-git/go-git/v5/config"
|
git_config "github.com/go-git/go-git/v5/config"
|
||||||
@@ -247,3 +249,47 @@ func (r TeaRepo) TeaGetCurrentBranchNameAndSHA() (string, string, error) {
|
|||||||
|
|
||||||
return localHead.Name().Short(), localHead.Hash().String(), nil
|
return localHead.Name().Short(), localHead.Hash().String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PushToCreatAgitFlowPR pushes the given head to the refs/for/<base>/<topic> ref on the remote to create an agit flow PR.
|
||||||
|
func (r TeaRepo) PushToCreatAgitFlowPR(remoteName, head, base, topic, title, description string, auth git_transport.AuthMethod) error {
|
||||||
|
if !strings.HasPrefix(head, "refs/") {
|
||||||
|
head = "refs/heads/" + head
|
||||||
|
}
|
||||||
|
|
||||||
|
ref := fmt.Sprintf("%s:refs/for/%s/%s", head, base, topic)
|
||||||
|
|
||||||
|
pushOptions := make(map[string]string)
|
||||||
|
if len(title) > 0 {
|
||||||
|
pushOptions["title"] = b64Encode(title)
|
||||||
|
}
|
||||||
|
if len(description) > 0 {
|
||||||
|
pushOptions["description"] = b64Encode(description)
|
||||||
|
}
|
||||||
|
|
||||||
|
opts := &git.PushOptions{
|
||||||
|
RemoteName: remoteName,
|
||||||
|
RefSpecs: []git_config.RefSpec{git_config.RefSpec(ref)},
|
||||||
|
Options: pushOptions,
|
||||||
|
Auth: auth,
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.Push(opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// b64Encode implements base64 encode for string if necessary.
|
||||||
|
func b64Encode(s string) string {
|
||||||
|
if strings.Contains(s, "\n") || !isASCII(s) {
|
||||||
|
return "{base64}" + base64.StdEncoding.EncodeToString([]byte(s))
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// isASCII indicates string contains only ASCII.
|
||||||
|
func isASCII(s string) bool {
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
if s[i] > unicode.MaxASCII {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
package git
|
package git
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/url"
|
||||||
|
|
||||||
"github.com/go-git/go-git/v5"
|
"github.com/go-git/go-git/v5"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -33,3 +35,13 @@ func RepoFromPath(path string) (*TeaRepo, error) {
|
|||||||
|
|
||||||
return &TeaRepo{repo}, nil
|
return &TeaRepo{repo}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RemoteURL returns the URL of the given remote
|
||||||
|
func (r TeaRepo) RemoteURL(remoteName string) (*url.URL, error) {
|
||||||
|
remote, err := r.Remote(remoteName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return url.Parse(remote.Config().URLs[0])
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"code.gitea.io/sdk/gitea"
|
"code.gitea.io/sdk/gitea"
|
||||||
"code.gitea.io/tea/modules/context"
|
"code.gitea.io/tea/modules/context"
|
||||||
"code.gitea.io/tea/modules/task"
|
"code.gitea.io/tea/modules/task"
|
||||||
|
"code.gitea.io/tea/modules/theme"
|
||||||
|
|
||||||
"github.com/charmbracelet/huh"
|
"github.com/charmbracelet/huh"
|
||||||
)
|
)
|
||||||
@@ -16,6 +17,8 @@ func CreatePull(ctx *context.TeaContext) (err error) {
|
|||||||
var (
|
var (
|
||||||
base, head string
|
base, head string
|
||||||
allowMaintainerEdits = true
|
allowMaintainerEdits = true
|
||||||
|
|
||||||
|
agit bool
|
||||||
)
|
)
|
||||||
|
|
||||||
// owner, repo
|
// owner, repo
|
||||||
@@ -37,6 +40,66 @@ func CreatePull(ctx *context.TeaContext) (err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := huh.NewConfirm().
|
||||||
|
Title("Do you want to create an agit flow pull request?").
|
||||||
|
Value(&agit).
|
||||||
|
WithTheme(theme.GetTheme()).
|
||||||
|
Run(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if agit {
|
||||||
|
var (
|
||||||
|
topic string
|
||||||
|
baseRemote string
|
||||||
|
)
|
||||||
|
|
||||||
|
topic = headBranch
|
||||||
|
|
||||||
|
head = "HEAD"
|
||||||
|
baseRemote = "origin"
|
||||||
|
|
||||||
|
if err := huh.NewForm(
|
||||||
|
huh.NewGroup(
|
||||||
|
huh.NewInput().
|
||||||
|
Title("Target branch:").
|
||||||
|
Value(&base).
|
||||||
|
Validate(huh.ValidateNotEmpty()),
|
||||||
|
|
||||||
|
huh.NewInput().
|
||||||
|
Title("Source repo remote:").
|
||||||
|
Value(&baseRemote),
|
||||||
|
|
||||||
|
huh.NewInput().
|
||||||
|
Title("Topic branch:").
|
||||||
|
Value(&topic).
|
||||||
|
Validate(validator),
|
||||||
|
|
||||||
|
huh.NewInput().
|
||||||
|
Title("Head branch:").
|
||||||
|
Value(&head).
|
||||||
|
Validate(validator),
|
||||||
|
),
|
||||||
|
).Run(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
opts := gitea.CreateIssueOption{Title: task.GetDefaultPRTitle(head)}
|
||||||
|
if err = promptIssueProperties(ctx.Login, ctx.Owner, ctx.Repo, &opts); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return task.CreateAgitFlowPull(
|
||||||
|
ctx,
|
||||||
|
baseRemote,
|
||||||
|
head,
|
||||||
|
base,
|
||||||
|
topic,
|
||||||
|
&opts,
|
||||||
|
PromptPassword,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
if err := huh.NewForm(
|
if err := huh.NewForm(
|
||||||
huh.NewGroup(
|
huh.NewGroup(
|
||||||
huh.NewInput().
|
huh.NewInput().
|
||||||
|
|||||||
@@ -153,3 +153,67 @@ func GetDefaultPRTitle(header string) string {
|
|||||||
|
|
||||||
return title
|
return title
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateAgitFlowPull creates a agit flow PR in the given repo and prints the result
|
||||||
|
func CreateAgitFlowPull(ctx *context.TeaContext, remote, head, base, topic string,
|
||||||
|
opts *gitea.CreateIssueOption,
|
||||||
|
callback func(string) (string, error)) (err error) {
|
||||||
|
// default is default branch
|
||||||
|
if len(base) == 0 {
|
||||||
|
base, err = GetDefaultPRBase(ctx.Login, ctx.Owner, ctx.Repo)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// default is current one
|
||||||
|
if len(head) == 0 {
|
||||||
|
if ctx.LocalRepo == nil {
|
||||||
|
return fmt.Errorf("no local git repo detected, please specify topic branch")
|
||||||
|
}
|
||||||
|
headOwner, headBranch, err := GetDefaultPRHead(ctx.LocalRepo)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
head = GetHeadSpec(headOwner, headBranch, ctx.Owner)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(remote) == 0 {
|
||||||
|
return fmt.Errorf("remote is required for agit flow PR")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(topic) == 0 {
|
||||||
|
topic = head
|
||||||
|
}
|
||||||
|
|
||||||
|
if head == base || topic == base {
|
||||||
|
return fmt.Errorf("can't create PR from %s to %s", topic, base)
|
||||||
|
}
|
||||||
|
|
||||||
|
// default is head branch name
|
||||||
|
if len(opts.Title) == 0 {
|
||||||
|
opts.Title = GetDefaultPRTitle(head)
|
||||||
|
}
|
||||||
|
// title is required
|
||||||
|
if len(opts.Title) == 0 {
|
||||||
|
return fmt.Errorf("title is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
localRepo, err := local_git.RepoForWorkdir()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
url, err := localRepo.RemoteURL(remote)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
auth, err := local_git.GetAuthForURL(url, ctx.Login.Token, ctx.Login.SSHKey, callback)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return localRepo.PushToCreatAgitFlowPR(remote, head, base, topic, opts.Title, opts.Body, auth)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user