diff --git a/context/context.go b/context/context.go index 40a9383a3..7b6863def 100644 --- a/context/context.go +++ b/context/context.go @@ -59,6 +59,93 @@ type ResolvedRemotes struct { apiClient *api.Client } +func GetBaseRepo(remotes Remotes) (ghrepo.Interface, error) { + for _, r := range remotes { + if r.Resolved == "base" { + return r, nil + } else if r.Resolved != "" { + repo, err := ghrepo.FromFullName(r.Resolved) + if err != nil { + return nil, err + } + return ghrepo.NewWithHost(repo.RepoOwner(), repo.RepoName(), r.RepoHost()), nil + } + } + return nil, errors.New("a default repo has not been set, use `gh repo default` to set a default repo") +} + +func RemoveBaseRepo(remotes Remotes) { + for _, remote := range remotes { + if remote.Resolved == "base" { + remote.Resolved = "" + git.UnsetRemoteResolution(remote.Remote.Name) + } + } +} + +func (r *ResolvedRemotes) SetGitConfigBaseRepo(io *iostreams.IOStreams) error { + resolution := "base" + if !io.CanPrompt() { + git.SetRemoteResolution(r.remotes[0].Name, resolution) + return nil + } + + // from here on, consult the API + if r.network == nil { + err := resolveNetwork(r) + if err != nil { + return err + } + } + + var repoNames []string + repoMap := map[string]*api.Repository{} + add := func(r *api.Repository) { + fn := ghrepo.FullName(r) + if _, ok := repoMap[fn]; !ok { + repoMap[fn] = r + repoNames = append(repoNames, fn) + } + } + + for _, repo := range r.network.Repositories { + if repo == nil { + continue + } + if repo.Parent != nil { + add(repo.Parent) + } + add(repo) + } + + if len(repoNames) == 0 { + git.SetRemoteResolution(r.remotes[0].Name, resolution) + return nil + } + + baseName := repoNames[0] + if len(repoNames) > 1 { + err := prompt.SurveyAskOne(&survey.Select{ + Message: "Which should be the base repository (used for e.g. querying issues) for this directory?", + Options: repoNames, + }, &baseName) + if err != nil { + return err + } + } + + // determine corresponding git remote + selectedRepo := repoMap[baseName] + remote, _ := r.RemoteForRepo(selectedRepo) + if remote == nil { + remote = r.remotes[0] + resolution = ghrepo.FullName(selectedRepo) + } + + // cache the result to git config + return git.SetRemoteResolution(remote.Name, resolution) +} + func (r *ResolvedRemotes) BaseRepo(io *iostreams.IOStreams) (ghrepo.Interface, error) { if r.baseOverride != nil { return r.baseOverride, nil diff --git a/git/remote.go b/git/remote.go index df3a37201..423bd17cb 100644 --- a/git/remote.go +++ b/git/remote.go @@ -169,9 +169,9 @@ func SetRemoteResolution(name, resolution string) error { } func UnsetRemoteResolution(name string) error { - addCmd, err := GitCommand("config", "--unset-all", fmt.Sprintf("remote.%s.gh-resolved", name)) + unsetCmd, err := GitCommand("config", "--unset-all", fmt.Sprintf("remote.%s.gh-resolved", name)) if err != nil { return err } - return run.PrepareCmd(addCmd).Run() + return run.PrepareCmd(unsetCmd).Run() } diff --git a/pkg/cmd/base/base.go b/pkg/cmd/base/base.go new file mode 100644 index 000000000..d7650a36d --- /dev/null +++ b/pkg/cmd/base/base.go @@ -0,0 +1,43 @@ +package base + +import ( + "net/http" + + "github.com/AlecAivazis/survey/v2" + "github.com/cli/cli/v2/context" + "github.com/cli/cli/v2/internal/ghrepo" + "github.com/cli/cli/v2/pkg/iostreams" + "github.com/cli/cli/v2/pkg/prompt" + + "github.com/cli/cli/v2/api" +) + +func GetAllResolvedRemotes(Remotes func() (context.Remotes, error), HttpClient func() (*http.Client, error)) ([]func(ghrepo.Interface, error), error) { + // return []func() (ghrepo.Interface, error) { + httpClient, err := HttpClient() + if err != nil { + return nil, err + } + + apiClient := api.NewClientFromHTTP(httpClient) + + remotes, err := Remotes() + if err != nil { + return nil, err + } + + repoContext, err := context.ResolveRemotesToRepos(remotes, apiClient, "") + if err != nil { + return nil, err + } + + repoContext.GetBaseRepo() + // baseRepo, err := repoContext.BaseRepo(f.IOStreams) + // if err != nil { + // return nil, err + // } + + // return baseRepo, nil + // } + // urn nil +} diff --git a/pkg/cmd/repo/default/default.go b/pkg/cmd/repo/default/default.go index 0481c39eb..6b1926fd2 100644 --- a/pkg/cmd/repo/default/default.go +++ b/pkg/cmd/repo/default/default.go @@ -1,129 +1,87 @@ -package base - -import ( - "fmt" - "net/http" - "strings" - - "github.com/MakeNowJust/heredoc" - "github.com/cli/cli/v2/api" - "github.com/cli/cli/v2/context" - "github.com/cli/cli/v2/git" - "github.com/cli/cli/v2/internal/ghrepo" - "github.com/cli/cli/v2/pkg/cmdutil" - "github.com/cli/cli/v2/pkg/iostreams" - "github.com/spf13/cobra" -) - -type DefaultOptions struct { - IO *iostreams.IOStreams - Remotes func() (context.Remotes, error) - BaseRepo func() (ghrepo.Interface, error) - HttpClient func() (*http.Client, error) - - RemoteName string - ListFlag bool -} - -func NewCmdDefault(f *cmdutil.Factory, runF func(*DefaultOptions) error) *cobra.Command { - opts := &DefaultOptions{ - IO: f.IOStreams, - HttpClient: f.HttpClient, - BaseRepo: f.BaseRepo, - Remotes: f.Remotes, - } - - cmd := &cobra.Command{ - Use: "default ", - Short: "Configure the default repository used for various commands", - Long: heredoc.Doc(` - The default repository is used to determine which repository gh - should automatically query for the commands: - issue, pr, browse, run, repo rename, secret, workflow - `), - Example: heredoc.Doc(` - $ gh repo default upstream - `), - Args: cobra.MaximumNArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - if len(args) > 0 { - opts.RemoteName = args[0] - } - if !opts.IO.CanPrompt() && !opts.ListFlag && opts.RemoteName == "" { - return cmdutil.FlagErrorf("remote name is required when not running interactively, use `--list` to see available remotes") - } - if err := cmdutil.MutuallyExclusive( - "args cannot be passed in with --list", - opts.RemoteName != "", - opts.ListFlag, - ); err != nil { - return err - } - - if runF != nil { - return runF(opts) - } - return defaultRun(opts) - }, - } - - cmd.Flags().BoolVarP(&opts.ListFlag, "list", "l", false, "list the current and available repositories") - return cmd -} - -func defaultRun(opts *DefaultOptions) error { - remotes, err := opts.Remotes() - if err != nil { - return err - } - - if opts.ListFlag { - found := false - list := &strings.Builder{} - for _, remote := range remotes { - if remote.Resolved == "base" { - list.WriteString(fmt.Sprintf("* %s\n", remote.Remote.Name)) - found = true - } else { - list.WriteString(fmt.Sprintf(" %s\n", remote.Remote.Name)) - } - } - if !found { - fmt.Fprint(opts.IO.Out, "the default repo has not been set\n") - } - fmt.Fprint(opts.IO.Out, list.String()) - return nil - } - - if opts.RemoteName != "" { - for _, remote := range remotes { - if opts.RemoteName == remote.Remote.Name { - removeBaseRepo(remotes) - git.SetRemoteResolution(remote.Name, "base") - return nil - } - } - return fmt.Errorf("could not find local remote name %s", opts.RemoteName) - } - removeBaseRepo(remotes) - httpClient, err := opts.HttpClient() - if err != nil { - return err - } - apiClient := api.NewClientFromHTTP(httpClient) - repoContext, err := context.ResolveRemotesToRepos(remotes, apiClient, "") - if err != nil { - return err - } - _, err = repoContext.BaseRepo(opts.IO) - return err -} - -func removeBaseRepo(remotes context.Remotes) { - for _, remote := range remotes { - if remote.Resolved == "base" { - remote.Resolved = "" - git.UnsetRemoteResolution(remote.Remote.Name) - } - } -} +package base + +import ( + "fmt" + "net/http" + + "github.com/MakeNowJust/heredoc" + "github.com/cli/cli/v2/api" + "github.com/cli/cli/v2/context" + "github.com/cli/cli/v2/internal/ghrepo" + "github.com/cli/cli/v2/pkg/cmdutil" + "github.com/cli/cli/v2/pkg/iostreams" + "github.com/spf13/cobra" +) + +type DefaultOptions struct { + IO *iostreams.IOStreams + BaseRepo func() (ghrepo.Interface, error) + Remotes func() (context.Remotes, error) + HttpClient func() (*http.Client, error) + + ViewFlag bool +} + +func NewCmdDefault(f *cmdutil.Factory, runF func(*DefaultOptions) error) *cobra.Command { + opts := &DefaultOptions{ + IO: f.IOStreams, + HttpClient: f.HttpClient, + BaseRepo: f.BaseRepo, + Remotes: f.Remotes, + } + + cmd := &cobra.Command{ + Use: "default", + Short: "Configure the default repository used for various commands", + Long: heredoc.Doc(` + The default repository is used to determine which repository gh + should automatically query for the commands: + issue, pr, browse, run, repo rename, secret, workflow + `), + Example: heredoc.Doc(` + $ gh repo default cli/cli + `), + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, args []string) error { + if !opts.IO.CanPrompt() && !opts.ViewFlag { + return cmdutil.FlagErrorf("a repository name is required when not running interactively") + } + + if runF != nil { + return runF(opts) + } + return defaultRun(opts) + }, + } + + cmd.Flags().BoolVarP(&opts.ViewFlag, "view", "v", false, "view the default repository used for various commands") + return cmd +} + +func defaultRun(opts *DefaultOptions) error { + remotes, err := opts.Remotes() + if err != nil { + return err + } + + if opts.ViewFlag { + baseRepo, err := context.GetBaseRepo(remotes) + if err != nil { + return err + } + fmt.Fprintln(opts.IO.Out, ghrepo.FullName(baseRepo)) + return nil + } + + httpClient, err := opts.HttpClient() + if err != nil { + return err + } + apiClient := api.NewClientFromHTTP(httpClient) + context.RemoveBaseRepo(remotes) + repoContext, err := context.ResolveRemotesToRepos(remotes, apiClient, "") + if err != nil { + return err + } + return repoContext.SetGitConfigBaseRepo(opts.IO) +}