feat: let user select pr to checkout

This commit is contained in:
nilvng 2024-11-03 11:36:14 +11:00
parent 30066b0042
commit 7d7c240f4b
5 changed files with 90 additions and 10 deletions

View file

@ -11,6 +11,7 @@ import (
"github.com/cli/cli/v2/git"
"github.com/cli/cli/v2/internal/gh"
"github.com/cli/cli/v2/internal/ghrepo"
"github.com/cli/cli/v2/pkg/cmd/pr/list"
"github.com/cli/cli/v2/pkg/cmd/pr/shared"
"github.com/cli/cli/v2/pkg/cmdutil"
"github.com/cli/cli/v2/pkg/iostreams"
@ -25,8 +26,10 @@ type CheckoutOptions struct {
Remotes func() (cliContext.Remotes, error)
Branch func() (string, error)
Finder shared.PRFinder
Finder shared.PRFinder
Prompter shared.Prompter
BaseRepo func() (ghrepo.Interface, error)
SelectorArg string
RecurseSubmodules bool
Force bool
@ -42,12 +45,14 @@ func NewCmdCheckout(f *cmdutil.Factory, runF func(*CheckoutOptions) error) *cobr
Config: f.Config,
Remotes: f.Remotes,
Branch: f.Branch,
Prompter: f.Prompter,
BaseRepo: f.BaseRepo,
}
cmd := &cobra.Command{
Use: "checkout {<number> | <url> | <branch>}",
Use: "checkout [<number> | <url> | <branch>]",
Short: "Check out a pull request in git",
Args: cmdutil.ExactArgs(1, "argument required"),
Args: cobra.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
opts.Finder = shared.NewFinder(f)
@ -71,15 +76,41 @@ func NewCmdCheckout(f *cmdutil.Factory, runF func(*CheckoutOptions) error) *cobr
}
func checkoutRun(opts *CheckoutOptions) error {
findOptions := shared.FindOptions{
Selector: opts.SelectorArg,
Fields: []string{"number", "headRefName", "headRepository", "headRepositoryOwner", "isCrossRepository", "maintainerCanModify"},
}
pr, baseRepo, err := opts.Finder.Find(findOptions)
baseRepo, err := opts.BaseRepo()
if err != nil {
return err
}
var pr *api.PullRequest
if len(opts.SelectorArg) > 0 {
findOptions := shared.FindOptions{
Selector: opts.SelectorArg,
Fields: []string{"number", "headRefName", "headRepository", "headRepositoryOwner", "isCrossRepository", "maintainerCanModify"},
}
pr0, _, err := opts.Finder.Find(findOptions)
if err != nil {
return err
}
pr = pr0
} else {
httpClient, err := opts.HttpClient()
if err != nil {
return err
}
pr0, err := selectPR(httpClient, baseRepo, opts.Prompter, opts.IO.ColorScheme())
if err != nil {
return err
}
pr = pr0
}
cfg, err := opts.Config()
if err != nil {
return err
@ -258,3 +289,40 @@ func executeCmds(client *git.Client, cmdQueue [][]string) error {
}
return nil
}
func selectPR(httpClient *http.Client, baseRepo ghrepo.Interface, prompter shared.Prompter, cs *iostreams.ColorScheme) (*api.PullRequest, error) {
listResult, err := list.ListPullRequests(httpClient, baseRepo, shared.FilterOptions{Entity: "pr", State: "open", Fields: []string{
"number",
"title",
"state",
"url",
"headRefName",
"headRepositoryOwner",
"isCrossRepository",
"isDraft",
"createdAt",
}}, 30)
if err != nil {
return nil, err
}
pr, err := promptForPR(prompter, cs, *listResult)
return pr, err
}
func promptForPR(prompter shared.Prompter, cs *iostreams.ColorScheme, jobs api.PullRequestAndTotalCount) (*api.PullRequest, error) {
candidates := []string{}
for _, pr := range jobs.PullRequests {
candidates = append(candidates, fmt.Sprintf("%s %s", shared.PRNumberTitleWithColor(cs, pr), pr.Title))
}
selected, err := prompter.Select("Check out a specific PR?", "", candidates)
if err != nil {
return nil, err
}
if selected >= 0 {
return &jobs.PullRequests[selected], nil
}
return nil, nil
}

View file

@ -13,7 +13,7 @@ func shouldUseSearch(filters prShared.FilterOptions) bool {
return filters.Draft != nil || filters.Author != "" || filters.Assignee != "" || filters.Search != "" || len(filters.Labels) > 0
}
func listPullRequests(httpClient *http.Client, repo ghrepo.Interface, filters prShared.FilterOptions, limit int) (*api.PullRequestAndTotalCount, error) {
func ListPullRequests(httpClient *http.Client, repo ghrepo.Interface, filters prShared.FilterOptions, limit int) (*api.PullRequestAndTotalCount, error) {
if shouldUseSearch(filters) {
return searchPullRequests(httpClient, repo, filters, limit)
}

View file

@ -172,7 +172,7 @@ func listRun(opts *ListOptions) error {
return opts.Browser.Browse(openURL)
}
listResult, err := listPullRequests(httpClient, baseRepo, filters, opts.LimitResults)
listResult, err := ListPullRequests(httpClient, baseRepo, filters, opts.LimitResults)
if err != nil {
return err
}

View file

@ -19,6 +19,9 @@ import (
type InputType int
type Prompter interface {
Select(string, string, []string) (int, error)
}
const (
InputTypeEditor InputType = iota
InputTypeInline

View file

@ -17,6 +17,15 @@ func StateTitleWithColor(cs *iostreams.ColorScheme, pr api.PullRequest) string {
return prStateColorFunc(text.Title(pr.State))
}
func PRNumberTitleWithColor(cs *iostreams.ColorScheme, pr api.PullRequest) string {
prStateColorFunc := cs.ColorFromString((ColorForPRState(pr)))
prNumber := fmt.Sprintf("%d", pr.Number)
if pr.State == "OPEN" && pr.IsDraft {
return prStateColorFunc(text.Title(prNumber))
}
return prStateColorFunc(text.Title(prNumber))
}
func ColorForPRState(pr api.PullRequest) string {
switch pr.State {
case "OPEN":