diff --git a/commands/mr/create/mr_create.go b/commands/mr/create/mr_create.go index 87ca3b2635d299405549d89ee1d7ad952b5e9e6a..01629fb5b2f7a9f954373f6fdbe452330cf73c64 100644 --- a/commands/mr/create/mr_create.go +++ b/commands/mr/create/mr_create.go @@ -56,12 +56,13 @@ type CreateOpts struct { IsWIP bool `json:"is_wip,omitempty"` ShouldPush bool `json:"should_push,omitempty"` - NoEditor bool `json:"-"` - IsInteractive bool `json:"-"` - Yes bool `json:"-"` - Web bool `json:"-"` - Recover bool `json:"-"` - Signoff bool `json:"-"` + NoEditor bool `json:"-"` + IsInteractive bool `json:"-"` + Yes bool `json:"-"` + Web bool `json:"-"` + OpenAfterCreation bool `json:"-"` + Recover bool `json:"-"` + Signoff bool `json:"-"` IO *iostreams.IOStreams `json:"-"` Branch func() (string, error) `json:"-"` @@ -183,6 +184,7 @@ func NewCmdCreate(f *cmdutils.Factory) *cobra.Command { mrCreateCmd.Flags().StringP("head", "H", "", "Select another head repository using the `OWNER/REPO` or `GROUP/NAMESPACE/REPO` format, the project ID, or the full URL.") mrCreateCmd.Flags().BoolVarP(&opts.Yes, "yes", "y", false, "Skip submission confirmation prompt. Use --fill to skip all optional prompts.") mrCreateCmd.Flags().BoolVarP(&opts.Web, "web", "w", false, "Continue merge request creation in a browser.") + mrCreateCmd.Flags().BoolVarP(&opts.OpenAfterCreation, "open-after-creation", "o", false, "Open merge request in browser after creation.") mrCreateCmd.Flags().BoolVarP(&opts.CopyIssueLabels, "copy-issue-labels", "", false, "Copy labels from issue to the merge request. Used with --related-issue.") mrCreateCmd.Flags().StringVarP(&opts.RelatedIssue, "related-issue", "i", "", "Create a merge request for an issue. If --title is not provided, uses the issue title.") mrCreateCmd.Flags().BoolVar(&opts.Recover, "recover", false, "Save the options to a file if the merge request creation fails. If the file exists, the options are loaded from the recovery file. (EXPERIMENTAL.)") @@ -512,7 +514,7 @@ func createRun(opts *CreateOpts) error { var action cmdutils.Action - // submit without prompting for non interactive mode + // submit without prompting for non-interactive mode if !opts.IsInteractive || opts.Yes { action = cmdutils.SubmitAction } @@ -637,6 +639,11 @@ func createRun(opts *CreateOpts) error { } fmt.Fprintln(out, mrutils.DisplayMR(c, &mr.BasicMergeRequest, opts.IO.IsaTTY)) + + if opts.OpenAfterCreation { + return openURLInBrowser(opts, mr.WebURL) + } + return nil } @@ -730,26 +737,12 @@ func handlePush(opts *CreateOpts, remote *glrepo.Remote) error { } func previewMR(opts *CreateOpts) error { - repo, err := opts.BaseRepo() - if err != nil { - return err - } - - cfg, err := opts.Config() - if err != nil { - return err - } - openURL, err := generateMRCompareURL(opts) if err != nil { return err } - if opts.IO.IsOutputTTY() { - fmt.Fprintf(opts.IO.StdErr, "Opening %s in your browser.\n", utils.DisplayURL(openURL)) - } - browser, _ := cfg.Get(repo.RepoHost(), "browser") - return utils.OpenInBrowser(openURL, browser) + return openURLInBrowser(opts, openURL) } func generateMRCompareURL(opts *CreateOpts) (string, error) { @@ -793,6 +786,25 @@ func generateMRCompareURL(opts *CreateOpts) (string, error) { return u.String(), nil } +func openURLInBrowser(opts *CreateOpts, url string) error { + repo, err := opts.BaseRepo() + if err != nil { + return err + } + + cfg, err := opts.Config() + if err != nil { + return err + } + + if opts.IO.IsOutputTTY() { + fmt.Fprintf(opts.IO.StdErr, "Opening %s in your browser.\n\n", utils.DisplayURL(url)) + } + + browser, _ := cfg.Get(repo.RepoHost(), "browser") + return utils.OpenInBrowser(url, browser) +} + func ResolvedHeadRepo(f *cmdutils.Factory) func() (glrepo.Interface, error) { return func() (glrepo.Interface, error) { httpClient, err := f.HttpClient() diff --git a/commands/mr/create/mr_create_test.go b/commands/mr/create/mr_create_test.go index 787d0c22aa6e4a9bb298daf2022608212b136e6b..3a9665da10cc85abb47f6df5d7121d3ae6bef160 100644 --- a/commands/mr/create/mr_create_test.go +++ b/commands/mr/create/mr_create_test.go @@ -406,6 +406,7 @@ func TestNewCmdCreate_RelatedIssueWithTitleAndDescription(t *testing.T) { "--description", "\"my custom MR description\"", "--related-issue", "1", "--source-branch", "feat-new-mr", + "--open-after-creation", } cli := strings.Join(cliStr, " ") @@ -421,7 +422,7 @@ func TestNewCmdCreate_RelatedIssueWithTitleAndDescription(t *testing.T) { return } assert.Contains(t, cmdtest.FirstLine([]byte(output.String())), "!12 my custom MR title (feat-new-mr)") - assert.Contains(t, output.Stderr(), "\nCreating draft merge request for feat-new-mr into master in OWNER/REPO\n\n") + assert.Equal(t, "\nCreating draft merge request for feat-new-mr into master in OWNER/REPO\n\nOpening gitlab.com/OWNER/REPO/-/merge_requests/12 in your browser.\n\n", output.Stderr()) assert.Contains(t, output.String(), "https://gitlab.com/OWNER/REPO/-/merge_requests/12") } diff --git a/docs/source/mr/create.md b/docs/source/mr/create.md index d0363dc043b0291f41005683714382642f1a034a..ae81a632ede2ba4e93fceaaa5fd3ac9cec001fc4 100644 --- a/docs/source/mr/create.md +++ b/docs/source/mr/create.md @@ -49,6 +49,7 @@ glab mr create --fill --fill-commit-body --yes -l, --label strings Add label by name. Multiple labels should be comma-separated. -m, --milestone string The global ID or title of a milestone to assign. --no-editor Don't open editor to enter a description. If true, uses prompt. Defaults to false. + -o, --open-after-creation Open merge request in browser after creation. --push Push committed changes after creating merge request. Make sure you have committed changes. --recover Save the options to a file if the merge request creation fails. If the file exists, the options are loaded from the recovery file. (EXPERIMENTAL.) -i, --related-issue string Create a merge request for an issue. If --title is not provided, uses the issue title.