From efe95d51add5ae018e7ad1c2335c80a9be6d0f5d Mon Sep 17 00:00:00 2001 From: Clouding Date: Mon, 17 Feb 2020 00:47:40 +0800 Subject: [PATCH] Add mr create cmd --- cmd/mr.go | 1 + cmd/mr_create.go | 13 ++++++++ internal/git/git.go | 15 +++++++++ internal/gitlab/contract/contract.go | 5 +++ internal/gitlab/project/manager.go | 11 ++++--- internal/gitlab/project/merge_request.go | 39 +++++++++++++++++++++--- 6 files changed, 74 insertions(+), 10 deletions(-) create mode 100644 cmd/mr_create.go diff --git a/cmd/mr.go b/cmd/mr.go index adab5da..b45f3ed 100644 --- a/cmd/mr.go +++ b/cmd/mr.go @@ -11,6 +11,7 @@ var mrCmd = &cobra.Command{ func init() { rootCmd.AddCommand(mrCmd) + mrCmd.AddCommand(mrCreateCmd) mrCmd.AddCommand(mrListCmd) mrCmd.AddCommand(mrOpenCmd) mrCmd.AddCommand(mrSearchCmd) diff --git a/cmd/mr_create.go b/cmd/mr_create.go new file mode 100644 index 0000000..40e59f2 --- /dev/null +++ b/cmd/mr_create.go @@ -0,0 +1,13 @@ +package cmd + +import ( + "github.com/spf13/cobra" +) + +var mrCreateCmd = &cobra.Command{ + Use: "create", + Short: "Create a merge request", + RunE: func(cmd *cobra.Command, args []string) error { + return projectManager(nil).MergeRequest.Create() + }, +} diff --git a/internal/git/git.go b/internal/git/git.go index a1c4ddd..0ed3dbc 100644 --- a/internal/git/git.go +++ b/internal/git/git.go @@ -1,6 +1,7 @@ package git import ( + "bytes" "log" "os" "os/exec" @@ -12,6 +13,11 @@ var command = func(args ...string) *exec.Cmd { return exec.Command("git", args...) } +// Push update remote refs along with associated objects +func Push(ref string) error { + return command("push", "--set-upstream", "origin", ref).Run() +} + // Clone clone a repository form GitLab. func Clone(repo, dir string) error { args := []string{"clone", repo} @@ -33,3 +39,12 @@ func CurrentRepo() string { } return utils.ParseGitProject(string(output)) } + +// CurrentBranch returns current branch. +func CurrentBranch() string { + output, err := command("rev-parse", "--abbrev-ref", "HEAD").CombinedOutput() + if err != nil { + log.Fatal(string(output)) + } + return string(bytes.Trim(output, "\n")) +} diff --git a/internal/gitlab/contract/contract.go b/internal/gitlab/contract/contract.go index 5125f7f..b81232c 100644 --- a/internal/gitlab/contract/contract.go +++ b/internal/gitlab/contract/contract.go @@ -23,6 +23,11 @@ type GitlabValidate interface { Lint(content string, options ...gitlab.OptionFunc) (*gitlab.LintResult, *gitlab.Response, error) } +// GitlabProject is go-gitlab project service interface. +type GitlabProject interface { + GetProject(pid interface{}, opt *gitlab.GetProjectOptions, options ...gitlab.OptionFunc) (*gitlab.Project, *gitlab.Response, error) +} + // GitlabGroup is go-gitlab group service interface. type GitlabGroup interface { ListGroupProjects(gid interface{}, opt *gitlab.ListGroupProjectsOptions, options ...gitlab.OptionFunc) ([]*gitlab.Project, *gitlab.Response, error) diff --git a/internal/gitlab/project/manager.go b/internal/gitlab/project/manager.go index 56d4805..a5ee11c 100644 --- a/internal/gitlab/project/manager.go +++ b/internal/gitlab/project/manager.go @@ -17,11 +17,12 @@ type Manager struct { func NewManager(c *gitlab.Client, project string, w io.Writer) *Manager { m := &Manager{} m.MergeRequest = &mergeRequestsService{ - project: project, - gitlabMR: c.MergeRequests, - out: w, - baseURL: c.BaseURL(), - openURL: browser.OpenURL, + project: project, + gitlabMR: c.MergeRequests, + gitlabProject: c.Projects, + out: w, + baseURL: c.BaseURL(), + openURL: browser.OpenURL, } m.Search = &searchService{ project: project, diff --git a/internal/gitlab/project/merge_request.go b/internal/gitlab/project/merge_request.go index 3482ff5..93d4670 100644 --- a/internal/gitlab/project/merge_request.go +++ b/internal/gitlab/project/merge_request.go @@ -7,17 +7,19 @@ import ( "path" "strconv" + "github.com/cloudingcity/golab/internal/git" "github.com/cloudingcity/golab/internal/gitlab/contract" "github.com/cloudingcity/golab/internal/gitlab/render" "github.com/xanzy/go-gitlab" ) type mergeRequestsService struct { - project string - gitlabMR contract.GitlabMergeRequests - out io.Writer - baseURL *url.URL - openURL func(url string) error + project string + gitlabMR contract.GitlabMergeRequests + gitlabProject contract.GitlabProject + out io.Writer + baseURL *url.URL + openURL func(url string) error } // List lists merge requests on a project. @@ -50,3 +52,30 @@ func (s *mergeRequestsService) Show(mrID int) error { render.New(s.out).MR(mr) return nil } + +// Create create a merge request. +func (s *mergeRequestsService) Create() error { + project, _, err := s.gitlabProject.GetProject(s.project, &gitlab.GetProjectOptions{}) + if err != nil { + return err + } + + defaultBranch := project.DefaultBranch + currentBranch := git.CurrentBranch() + if defaultBranch == currentBranch { + return fmt.Errorf("must be on a branch named differently than %q", defaultBranch) + } + + if err = git.Push(currentBranch); err != nil { + return err + } + + u := *s.baseURL + u.Path = path.Join(s.project, "merge_requests", "new") + q := make(url.Values) + q.Set("merge_request[source_branch]", currentBranch) + u.RawQuery = q.Encode() + + fmt.Fprintf(s.out, "Opening %s in your browser\n", u.String()) + return s.openURL(u.String()) +}