mirror of
https://gitea.com/gitea/tea.git
synced 2025-09-06 11:52:55 +02:00
Refactor: apply new internal structurs (#206)
fix lint fix lint Move print TrackedTimesList to print package Move AbsPathWithExpansion to utils/path.go rename module intern to config Move Subcomands into it's own Packages Split times subcomands into own sourcefiles Split repos subcomands into own sourcefiles Split releases subcomands into own sourcefiles Split pulls subcomands into own sourcefiles Split milestones subcomands into own sourcefiles Split login subcomands into own sourcefiles Split labels subcomands into own sourcefiles split issues subcomands into own sourcefiles mv Move Interactive Login Creation to interact package Move Add Login function to intern/login.go apply from review lint: add description to exported func smal nits Move DetailViews stdout print func to print package Refactor: * Move Config & Login routines into intern package * rename global var in cmd * Move help func to utils Co-authored-by: 6543 <6543@obermui.de> Reviewed-on: https://gitea.com/gitea/tea/pulls/206 Reviewed-by: Norwin <noerw@noreply.gitea.io> Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
This commit is contained in:
97
cmd/pulls/checkout.go
Normal file
97
cmd/pulls/checkout.go
Normal file
@ -0,0 +1,97 @@
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package pulls
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"code.gitea.io/tea/cmd/flags"
|
||||
"code.gitea.io/tea/modules/config"
|
||||
local_git "code.gitea.io/tea/modules/git"
|
||||
"code.gitea.io/tea/modules/utils"
|
||||
|
||||
"github.com/go-git/go-git/v5"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
// CmdPullsCheckout is a command to locally checkout the given PR
|
||||
var CmdPullsCheckout = cli.Command{
|
||||
Name: "checkout",
|
||||
Usage: "Locally check out the given PR",
|
||||
Description: `Locally check out the given PR`,
|
||||
Action: runPullsCheckout,
|
||||
ArgsUsage: "<pull index>",
|
||||
Flags: flags.AllDefaultFlags,
|
||||
}
|
||||
|
||||
func runPullsCheckout(ctx *cli.Context) error {
|
||||
login, owner, repo := config.InitCommand(flags.GlobalRepoValue, flags.GlobalLoginValue, flags.GlobalRemoteValue)
|
||||
if ctx.Args().Len() != 1 {
|
||||
log.Fatal("Must specify a PR index")
|
||||
}
|
||||
|
||||
// fetch PR source-repo & -branch from gitea
|
||||
idx, err := utils.ArgToIndex(ctx.Args().First())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pr, _, err := login.Client().GetPullRequest(owner, repo, idx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
remoteURL := pr.Head.Repository.CloneURL
|
||||
remoteBranchName := pr.Head.Ref
|
||||
|
||||
// open local git repo
|
||||
localRepo, err := local_git.RepoForWorkdir()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// verify related remote is in local repo, otherwise add it
|
||||
newRemoteName := fmt.Sprintf("pulls/%v", pr.Head.Repository.Owner.UserName)
|
||||
localRemote, err := localRepo.GetOrCreateRemote(remoteURL, newRemoteName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
localRemoteName := localRemote.Config().Name
|
||||
localBranchName := fmt.Sprintf("pulls/%v-%v", idx, remoteBranchName)
|
||||
|
||||
// fetch remote
|
||||
fmt.Printf("Fetching PR %v (head %s:%s) from remote '%s'\n",
|
||||
idx, remoteURL, remoteBranchName, localRemoteName)
|
||||
|
||||
url, err := local_git.ParseURL(localRemote.Config().URLs[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
auth, err := local_git.GetAuthForURL(url, login.User, login.SSHKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = localRemote.Fetch(&git.FetchOptions{Auth: auth})
|
||||
if err == git.NoErrAlreadyUpToDate {
|
||||
fmt.Println(err)
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// checkout local branch
|
||||
fmt.Printf("Creating branch '%s'\n", localBranchName)
|
||||
err = localRepo.TeaCreateBranch(localBranchName, remoteBranchName, localRemoteName)
|
||||
if err == git.ErrBranchExists {
|
||||
fmt.Println(err)
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Checking out PR %v\n", idx)
|
||||
err = localRepo.TeaCheckout(localBranchName)
|
||||
|
||||
return err
|
||||
}
|
105
cmd/pulls/clean.go
Normal file
105
cmd/pulls/clean.go
Normal file
@ -0,0 +1,105 @@
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package pulls
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"code.gitea.io/tea/cmd/flags"
|
||||
"code.gitea.io/tea/modules/config"
|
||||
local_git "code.gitea.io/tea/modules/git"
|
||||
"code.gitea.io/tea/modules/utils"
|
||||
|
||||
"code.gitea.io/sdk/gitea"
|
||||
git_config "github.com/go-git/go-git/v5/config"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
// 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(ctx *cli.Context) error {
|
||||
login, owner, repo := config.InitCommand(flags.GlobalRepoValue, flags.GlobalLoginValue, flags.GlobalRemoteValue)
|
||||
if ctx.Args().Len() != 1 {
|
||||
return fmt.Errorf("Must specify a PR index")
|
||||
}
|
||||
|
||||
// fetch PR source-repo & -branch from gitea
|
||||
idx, err := utils.ArgToIndex(ctx.Args().First())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pr, _, err := login.Client().GetPullRequest(owner, repo, idx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if pr.State == gitea.StateOpen {
|
||||
return fmt.Errorf("PR is still open, won't delete branches")
|
||||
}
|
||||
|
||||
// IDEA: abort if PR.Head.Repository.CloneURL does not match login.URL?
|
||||
|
||||
r, err := local_git.RepoForWorkdir()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// find a branch with matching sha or name, that has a remote matching the repo url
|
||||
var branch *git_config.Branch
|
||||
if ctx.Bool("ignore-sha") {
|
||||
branch, err = r.TeaFindBranchByName(pr.Head.Ref, pr.Head.Repository.CloneURL)
|
||||
} else {
|
||||
branch, err = r.TeaFindBranchBySha(pr.Head.Sha, pr.Head.Repository.CloneURL)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if branch == nil {
|
||||
if ctx.Bool("ignore-sha") {
|
||||
return fmt.Errorf("Remote branch %s not found in local repo", pr.Head.Ref)
|
||||
}
|
||||
return fmt.Errorf(`Remote branch %s not found in local repo.
|
||||
Either you don't track this PR, or the local branch has diverged from the remote.
|
||||
If you still want to continue & are sure you don't loose any important commits,
|
||||
call me again with the --ignore-sha flag`, pr.Head.Ref)
|
||||
}
|
||||
|
||||
// prepare deletion of local branch:
|
||||
headRef, err := r.Head()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if headRef.Name().Short() == branch.Name {
|
||||
fmt.Printf("Checking out 'master' to delete local branch '%s'\n", branch.Name)
|
||||
err = r.TeaCheckout("master")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// remove local & remote branch
|
||||
fmt.Printf("Deleting local branch %s and remote branch %s\n", branch.Name, pr.Head.Ref)
|
||||
url, err := r.TeaRemoteURL(branch.Remote)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
auth, err := local_git.GetAuthForURL(url, login.User, login.SSHKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return r.TeaDeleteBranch(branch, pr.Head.Ref, auth)
|
||||
}
|
145
cmd/pulls/create.go
Normal file
145
cmd/pulls/create.go
Normal file
@ -0,0 +1,145 @@
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package pulls
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/tea/cmd/flags"
|
||||
"code.gitea.io/tea/modules/config"
|
||||
local_git "code.gitea.io/tea/modules/git"
|
||||
"code.gitea.io/tea/modules/print"
|
||||
|
||||
"code.gitea.io/sdk/gitea"
|
||||
"github.com/go-git/go-git/v5"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
// CmdPullsCreate creates a pull request
|
||||
var CmdPullsCreate = cli.Command{
|
||||
Name: "create",
|
||||
Usage: "Create a pull-request",
|
||||
Description: "Create a pull-request",
|
||||
Action: runPullsCreate,
|
||||
Flags: append([]cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "head",
|
||||
Usage: "Set head branch (default is current one)",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "base",
|
||||
Aliases: []string{"b"},
|
||||
Usage: "Set base branch (default is default branch)",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "title",
|
||||
Aliases: []string{"t"},
|
||||
Usage: "Set title of pull (default is head branch name)",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "description",
|
||||
Aliases: []string{"d"},
|
||||
Usage: "Set body of new pull",
|
||||
},
|
||||
}, flags.AllDefaultFlags...),
|
||||
}
|
||||
|
||||
func runPullsCreate(ctx *cli.Context) error {
|
||||
login, ownerArg, repoArg := config.InitCommand(flags.GlobalRepoValue, flags.GlobalLoginValue, flags.GlobalRemoteValue)
|
||||
client := login.Client()
|
||||
|
||||
repo, _, err := client.GetRepo(ownerArg, repoArg)
|
||||
if err != nil {
|
||||
log.Fatal("could not fetch repo meta: ", err)
|
||||
}
|
||||
|
||||
// open local git repo
|
||||
localRepo, err := local_git.RepoForWorkdir()
|
||||
if err != nil {
|
||||
log.Fatal("could not open local repo: ", err)
|
||||
}
|
||||
|
||||
// push if possible
|
||||
log.Println("git push")
|
||||
err = localRepo.Push(&git.PushOptions{})
|
||||
if err != nil && err != git.NoErrAlreadyUpToDate {
|
||||
log.Printf("Error occurred during 'git push':\n%s\n", err.Error())
|
||||
}
|
||||
|
||||
base := ctx.String("base")
|
||||
// default is default branch
|
||||
if len(base) == 0 {
|
||||
base = repo.DefaultBranch
|
||||
}
|
||||
|
||||
head := ctx.String("head")
|
||||
// default is current one
|
||||
if len(head) == 0 {
|
||||
headBranch, err := localRepo.Head()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
sha := headBranch.Hash().String()
|
||||
|
||||
remote, err := localRepo.TeaFindBranchRemote("", sha)
|
||||
if err != nil {
|
||||
log.Fatal("could not determine remote for current branch: ", err)
|
||||
}
|
||||
|
||||
if remote == nil {
|
||||
// if no remote branch is found for the local hash, we abort:
|
||||
// user has probably not configured a remote for the local branch,
|
||||
// or local branch does not represent remote state.
|
||||
log.Fatal("no matching remote found for this branch. try git push -u <remote> <branch>")
|
||||
}
|
||||
|
||||
branchName, err := localRepo.TeaGetCurrentBranchName()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
url, err := local_git.ParseURL(remote.Config().URLs[0])
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
owner, _ := config.GetOwnerAndRepo(strings.TrimLeft(url.Path, "/"), "")
|
||||
head = fmt.Sprintf("%s:%s", owner, branchName)
|
||||
}
|
||||
|
||||
title := ctx.String("title")
|
||||
// default is head branch name
|
||||
if len(title) == 0 {
|
||||
title = head
|
||||
if strings.Contains(title, ":") {
|
||||
title = strings.SplitN(title, ":", 2)[1]
|
||||
}
|
||||
title = strings.Replace(title, "-", " ", -1)
|
||||
title = strings.Replace(title, "_", " ", -1)
|
||||
title = strings.Title(strings.ToLower(title))
|
||||
}
|
||||
// title is required
|
||||
if len(title) == 0 {
|
||||
fmt.Printf("Title is required")
|
||||
return nil
|
||||
}
|
||||
|
||||
pr, _, err := client.CreatePullRequest(ownerArg, repoArg, gitea.CreatePullRequestOption{
|
||||
Head: head,
|
||||
Base: base,
|
||||
Title: title,
|
||||
Body: ctx.String("description"),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("could not create PR from %s to %s:%s: %s", head, ownerArg, base, err)
|
||||
}
|
||||
|
||||
print.PullDetails(pr)
|
||||
|
||||
fmt.Println(pr.HTMLURL)
|
||||
return err
|
||||
}
|
93
cmd/pulls/list.go
Normal file
93
cmd/pulls/list.go
Normal file
@ -0,0 +1,93 @@
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package pulls
|
||||
|
||||
import (
|
||||
"log"
|
||||
"strconv"
|
||||
|
||||
"code.gitea.io/tea/cmd/flags"
|
||||
"code.gitea.io/tea/modules/config"
|
||||
"code.gitea.io/tea/modules/print"
|
||||
|
||||
"code.gitea.io/sdk/gitea"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
// CmdPullsList represents a sub command of issues to list pulls
|
||||
var CmdPullsList = cli.Command{
|
||||
Name: "ls",
|
||||
Usage: "List pull requests of the repository",
|
||||
Description: `List pull requests of the repository`,
|
||||
Action: RunPullsList,
|
||||
Flags: flags.IssuePRFlags,
|
||||
}
|
||||
|
||||
// RunPullsList return list of pulls
|
||||
func RunPullsList(ctx *cli.Context) error {
|
||||
login, owner, repo := config.InitCommand(flags.GlobalRepoValue, flags.GlobalLoginValue, flags.GlobalRemoteValue)
|
||||
|
||||
state := gitea.StateOpen
|
||||
switch ctx.String("state") {
|
||||
case "all":
|
||||
state = gitea.StateAll
|
||||
case "open":
|
||||
state = gitea.StateOpen
|
||||
case "closed":
|
||||
state = gitea.StateClosed
|
||||
}
|
||||
|
||||
prs, _, err := login.Client().ListRepoPullRequests(owner, repo, gitea.ListPullRequestsOptions{
|
||||
State: state,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
headers := []string{
|
||||
"Index",
|
||||
"Title",
|
||||
"State",
|
||||
"Author",
|
||||
"Milestone",
|
||||
"Updated",
|
||||
}
|
||||
|
||||
var values [][]string
|
||||
|
||||
if len(prs) == 0 {
|
||||
print.OutputList(flags.GlobalOutputValue, headers, values)
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, pr := range prs {
|
||||
if pr == nil {
|
||||
continue
|
||||
}
|
||||
author := pr.Poster.FullName
|
||||
if len(author) == 0 {
|
||||
author = pr.Poster.UserName
|
||||
}
|
||||
mile := ""
|
||||
if pr.Milestone != nil {
|
||||
mile = pr.Milestone.Title
|
||||
}
|
||||
values = append(
|
||||
values,
|
||||
[]string{
|
||||
strconv.FormatInt(pr.Index, 10),
|
||||
pr.Title,
|
||||
string(pr.State),
|
||||
author,
|
||||
mile,
|
||||
pr.Updated.Format("2006-01-02 15:04:05"),
|
||||
},
|
||||
)
|
||||
}
|
||||
print.OutputList(flags.GlobalOutputValue, headers, values)
|
||||
|
||||
return nil
|
||||
}
|
Reference in New Issue
Block a user