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  }