github.com/cli/cli@v1.14.1-0.20210902173923-1af6a669e342/pkg/cmd/pr/close/close.go (about) 1 package close 2 3 import ( 4 "fmt" 5 "net/http" 6 7 "github.com/cli/cli/api" 8 "github.com/cli/cli/git" 9 "github.com/cli/cli/pkg/cmd/pr/shared" 10 "github.com/cli/cli/pkg/cmdutil" 11 "github.com/cli/cli/pkg/iostreams" 12 "github.com/spf13/cobra" 13 ) 14 15 type CloseOptions struct { 16 HttpClient func() (*http.Client, error) 17 IO *iostreams.IOStreams 18 Branch func() (string, error) 19 20 Finder shared.PRFinder 21 22 SelectorArg string 23 DeleteBranch bool 24 DeleteLocalBranch bool 25 } 26 27 func NewCmdClose(f *cmdutil.Factory, runF func(*CloseOptions) error) *cobra.Command { 28 opts := &CloseOptions{ 29 IO: f.IOStreams, 30 HttpClient: f.HttpClient, 31 Branch: f.Branch, 32 } 33 34 cmd := &cobra.Command{ 35 Use: "close {<number> | <url> | <branch>}", 36 Short: "Close a pull request", 37 Args: cobra.ExactArgs(1), 38 RunE: func(cmd *cobra.Command, args []string) error { 39 opts.Finder = shared.NewFinder(f) 40 41 if len(args) > 0 { 42 opts.SelectorArg = args[0] 43 } 44 45 opts.DeleteLocalBranch = !cmd.Flags().Changed("repo") 46 47 if runF != nil { 48 return runF(opts) 49 } 50 return closeRun(opts) 51 }, 52 } 53 cmd.Flags().BoolVarP(&opts.DeleteBranch, "delete-branch", "d", false, "Delete the local and remote branch after close") 54 55 return cmd 56 } 57 58 func closeRun(opts *CloseOptions) error { 59 cs := opts.IO.ColorScheme() 60 61 findOptions := shared.FindOptions{ 62 Selector: opts.SelectorArg, 63 Fields: []string{"state", "number", "title", "isCrossRepository", "headRefName"}, 64 } 65 pr, baseRepo, err := opts.Finder.Find(findOptions) 66 if err != nil { 67 return err 68 } 69 70 if pr.State == "MERGED" { 71 fmt.Fprintf(opts.IO.ErrOut, "%s Pull request #%d (%s) can't be closed because it was already merged\n", cs.FailureIcon(), pr.Number, pr.Title) 72 return cmdutil.SilentError 73 } else if !pr.IsOpen() { 74 fmt.Fprintf(opts.IO.ErrOut, "%s Pull request #%d (%s) is already closed\n", cs.WarningIcon(), pr.Number, pr.Title) 75 return nil 76 } 77 78 httpClient, err := opts.HttpClient() 79 if err != nil { 80 return err 81 } 82 apiClient := api.NewClientFromHTTP(httpClient) 83 84 err = api.PullRequestClose(apiClient, baseRepo, pr) 85 if err != nil { 86 return fmt.Errorf("API call failed: %w", err) 87 } 88 89 fmt.Fprintf(opts.IO.ErrOut, "%s Closed pull request #%d (%s)\n", cs.SuccessIconWithColor(cs.Red), pr.Number, pr.Title) 90 91 if opts.DeleteBranch { 92 branchSwitchString := "" 93 94 if opts.DeleteLocalBranch { 95 currentBranch, err := opts.Branch() 96 if err != nil { 97 return err 98 } 99 100 var branchToSwitchTo string 101 if currentBranch == pr.HeadRefName { 102 branchToSwitchTo, err = api.RepoDefaultBranch(apiClient, baseRepo) 103 if err != nil { 104 return err 105 } 106 err = git.CheckoutBranch(branchToSwitchTo) 107 if err != nil { 108 return err 109 } 110 } 111 112 localBranchExists := git.HasLocalBranch(pr.HeadRefName) 113 if localBranchExists { 114 if err := git.DeleteLocalBranch(pr.HeadRefName); err != nil { 115 return fmt.Errorf("failed to delete local branch %s: %w", cs.Cyan(pr.HeadRefName), err) 116 } 117 } 118 119 if branchToSwitchTo != "" { 120 branchSwitchString = fmt.Sprintf(" and switched to branch %s", cs.Cyan(branchToSwitchTo)) 121 } 122 } 123 124 if pr.IsCrossRepository { 125 fmt.Fprintf(opts.IO.ErrOut, "%s Skipped deleting the remote branch of a pull request from fork\n", cs.WarningIcon()) 126 if !opts.DeleteLocalBranch { 127 return nil 128 } 129 } else { 130 if err := api.BranchDeleteRemote(apiClient, baseRepo, pr.HeadRefName); err != nil { 131 return fmt.Errorf("failed to delete remote branch %s: %w", cs.Cyan(pr.HeadRefName), err) 132 } 133 } 134 fmt.Fprintf(opts.IO.ErrOut, "%s Deleted branch %s%s\n", cs.SuccessIconWithColor(cs.Red), cs.Cyan(pr.HeadRefName), branchSwitchString) 135 } 136 137 return nil 138 }