mirror of
https://gitea.com/gitea/tea.git
synced 2026-02-22 06:13: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
@@ -4,8 +4,10 @@
|
||||
package git
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/go-git/go-git/v5"
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
|
||||
"github.com/go-git/go-git/v5"
|
||||
)
|
||||
|
||||
@@ -33,3 +35,13 @@ func RepoFromPath(path string) (*TeaRepo, error) {
|
||||
|
||||
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/tea/modules/context"
|
||||
"code.gitea.io/tea/modules/task"
|
||||
"code.gitea.io/tea/modules/theme"
|
||||
|
||||
"github.com/charmbracelet/huh"
|
||||
)
|
||||
@@ -16,6 +17,8 @@ func CreatePull(ctx *context.TeaContext) (err error) {
|
||||
var (
|
||||
base, head string
|
||||
allowMaintainerEdits = true
|
||||
|
||||
agit bool
|
||||
)
|
||||
|
||||
// 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(
|
||||
huh.NewGroup(
|
||||
huh.NewInput().
|
||||
|
||||
@@ -153,3 +153,67 @@ func GetDefaultPRTitle(header string) string {
|
||||
|
||||
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