github.com/ungtb10d/cli/v2@v2.0.0-20221110210412-98537dd9d6a1/pkg/cmd/repo/rename/rename.go (about)

     1  package rename
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"net/http"
     7  
     8  	"github.com/AlecAivazis/survey/v2"
     9  	"github.com/MakeNowJust/heredoc"
    10  	"github.com/ungtb10d/cli/v2/api"
    11  	ghContext "github.com/ungtb10d/cli/v2/context"
    12  	"github.com/ungtb10d/cli/v2/git"
    13  	"github.com/ungtb10d/cli/v2/internal/config"
    14  	"github.com/ungtb10d/cli/v2/internal/ghrepo"
    15  	"github.com/ungtb10d/cli/v2/pkg/cmdutil"
    16  	"github.com/ungtb10d/cli/v2/pkg/iostreams"
    17  	"github.com/ungtb10d/cli/v2/pkg/prompt"
    18  	"github.com/spf13/cobra"
    19  )
    20  
    21  type RenameOptions struct {
    22  	HttpClient      func() (*http.Client, error)
    23  	GitClient       *git.Client
    24  	IO              *iostreams.IOStreams
    25  	Config          func() (config.Config, error)
    26  	BaseRepo        func() (ghrepo.Interface, error)
    27  	Remotes         func() (ghContext.Remotes, error)
    28  	DoConfirm       bool
    29  	HasRepoOverride bool
    30  	newRepoSelector string
    31  }
    32  
    33  func NewCmdRename(f *cmdutil.Factory, runf func(*RenameOptions) error) *cobra.Command {
    34  	opts := &RenameOptions{
    35  		IO:         f.IOStreams,
    36  		HttpClient: f.HttpClient,
    37  		GitClient:  f.GitClient,
    38  		Remotes:    f.Remotes,
    39  		Config:     f.Config,
    40  	}
    41  
    42  	var confirm bool
    43  
    44  	cmd := &cobra.Command{
    45  		Use:   "rename [<new-name>]",
    46  		Short: "Rename a repository",
    47  		Long: heredoc.Doc(`Rename a GitHub repository.
    48  
    49  		By default, this renames the current repository; otherwise renames the specified repository.`),
    50  		Args: cobra.MaximumNArgs(1),
    51  		RunE: func(cmd *cobra.Command, args []string) error {
    52  			opts.BaseRepo = f.BaseRepo
    53  			opts.HasRepoOverride = cmd.Flags().Changed("repo")
    54  
    55  			if len(args) > 0 {
    56  				opts.newRepoSelector = args[0]
    57  			} else if !opts.IO.CanPrompt() {
    58  				return cmdutil.FlagErrorf("new name argument required when not running interactively")
    59  			}
    60  
    61  			if len(args) == 1 && !confirm && !opts.HasRepoOverride {
    62  				if !opts.IO.CanPrompt() {
    63  					return cmdutil.FlagErrorf("--confirm required when passing a single argument")
    64  				}
    65  				opts.DoConfirm = true
    66  			}
    67  
    68  			if runf != nil {
    69  				return runf(opts)
    70  			}
    71  			return renameRun(opts)
    72  		},
    73  	}
    74  
    75  	cmdutil.EnableRepoOverride(cmd, f)
    76  	cmd.Flags().BoolVarP(&confirm, "confirm", "y", false, "skip confirmation prompt")
    77  
    78  	return cmd
    79  }
    80  
    81  func renameRun(opts *RenameOptions) error {
    82  	httpClient, err := opts.HttpClient()
    83  	if err != nil {
    84  		return err
    85  	}
    86  
    87  	newRepoName := opts.newRepoSelector
    88  
    89  	currRepo, err := opts.BaseRepo()
    90  	if err != nil {
    91  		return err
    92  	}
    93  
    94  	if newRepoName == "" {
    95  		//nolint:staticcheck // SA1019: prompt.SurveyAskOne is deprecated: use Prompter
    96  		err = prompt.SurveyAskOne(
    97  			&survey.Input{
    98  				Message: fmt.Sprintf("Rename %s to: ", ghrepo.FullName(currRepo)),
    99  			},
   100  			&newRepoName,
   101  		)
   102  		if err != nil {
   103  			return err
   104  		}
   105  	}
   106  
   107  	if opts.DoConfirm {
   108  		var confirmed bool
   109  		p := &survey.Confirm{
   110  			Message: fmt.Sprintf("Rename %s to %s?", ghrepo.FullName(currRepo), newRepoName),
   111  			Default: false,
   112  		}
   113  		//nolint:staticcheck // SA1019: prompt.SurveyAskOne is deprecated: use Prompter
   114  		err = prompt.SurveyAskOne(p, &confirmed)
   115  		if err != nil {
   116  			return fmt.Errorf("failed to prompt: %w", err)
   117  		}
   118  		if !confirmed {
   119  			return nil
   120  		}
   121  	}
   122  
   123  	apiClient := api.NewClientFromHTTP(httpClient)
   124  
   125  	newRepo, err := api.RenameRepo(apiClient, currRepo, newRepoName)
   126  	if err != nil {
   127  		return err
   128  	}
   129  
   130  	renamedRepo := ghrepo.New(newRepo.Owner.Login, newRepo.Name)
   131  
   132  	cs := opts.IO.ColorScheme()
   133  	if opts.IO.IsStdoutTTY() {
   134  		fmt.Fprintf(opts.IO.Out, "%s Renamed repository %s\n", cs.SuccessIcon(), ghrepo.FullName(newRepo))
   135  	}
   136  
   137  	if opts.HasRepoOverride {
   138  		return nil
   139  	}
   140  
   141  	remote, err := updateRemote(currRepo, renamedRepo, opts)
   142  	if err != nil {
   143  		fmt.Fprintf(opts.IO.ErrOut, "%s Warning: unable to update remote %q: %v\n", cs.WarningIcon(), remote.Name, err)
   144  	} else if opts.IO.IsStdoutTTY() {
   145  		fmt.Fprintf(opts.IO.Out, "%s Updated the %q remote\n", cs.SuccessIcon(), remote.Name)
   146  	}
   147  
   148  	return nil
   149  }
   150  
   151  func updateRemote(repo ghrepo.Interface, renamed ghrepo.Interface, opts *RenameOptions) (*ghContext.Remote, error) {
   152  	cfg, err := opts.Config()
   153  	if err != nil {
   154  		return nil, err
   155  	}
   156  
   157  	protocol, err := cfg.GetOrDefault(repo.RepoHost(), "git_protocol")
   158  	if err != nil {
   159  		return nil, err
   160  	}
   161  
   162  	remotes, err := opts.Remotes()
   163  	if err != nil {
   164  		return nil, err
   165  	}
   166  
   167  	remote, err := remotes.FindByRepo(repo.RepoOwner(), repo.RepoName())
   168  	if err != nil {
   169  		return nil, err
   170  	}
   171  
   172  	remoteURL := ghrepo.FormatRemoteURL(renamed, protocol)
   173  	err = opts.GitClient.UpdateRemoteURL(context.Background(), remote.Name, remoteURL)
   174  
   175  	return remote, err
   176  }