cli/pkg/cmd/repo/list/list.go

181 lines
4.5 KiB
Go

package list
import (
"fmt"
"net/http"
"time"
"github.com/cli/cli/internal/config"
"github.com/cli/cli/pkg/cmdutil"
"github.com/cli/cli/pkg/iostreams"
"github.com/cli/cli/pkg/text"
"github.com/cli/cli/utils"
"github.com/spf13/cobra"
)
type ListOptions struct {
HttpClient func() (*http.Client, error)
Config func() (config.Config, error)
IO *iostreams.IOStreams
Limit int
Owner string
Visibility string
Fork bool
Source bool
Language string
Archived bool
NonArchived bool
Now func() time.Time
}
func NewCmdList(f *cmdutil.Factory, runF func(*ListOptions) error) *cobra.Command {
opts := ListOptions{
IO: f.IOStreams,
Config: f.Config,
HttpClient: f.HttpClient,
Now: time.Now,
}
var (
flagPublic bool
flagPrivate bool
)
cmd := &cobra.Command{
Use: "list [<owner>]",
Args: cobra.MaximumNArgs(1),
Short: "List repositories owned by user or organization",
RunE: func(c *cobra.Command, args []string) error {
if opts.Limit < 1 {
return &cmdutil.FlagError{Err: fmt.Errorf("invalid limit: %v", opts.Limit)}
}
if flagPrivate && flagPublic {
return &cmdutil.FlagError{Err: fmt.Errorf("specify only one of `--public` or `--private`")}
}
if opts.Source && opts.Fork {
return &cmdutil.FlagError{Err: fmt.Errorf("specify only one of `--source` or `--fork`")}
}
if opts.Archived && opts.NonArchived {
return &cmdutil.FlagError{Err: fmt.Errorf("specify only one of `--archived` or `--no-archived`")}
}
if flagPrivate {
opts.Visibility = "private"
} else if flagPublic {
opts.Visibility = "public"
}
if len(args) > 0 {
opts.Owner = args[0]
}
if runF != nil {
return runF(&opts)
}
return listRun(&opts)
},
}
cmd.Flags().IntVarP(&opts.Limit, "limit", "L", 30, "Maximum number of repositories to list")
cmd.Flags().BoolVar(&flagPrivate, "private", false, "Show only private repositories")
cmd.Flags().BoolVar(&flagPublic, "public", false, "Show only public repositories")
cmd.Flags().BoolVar(&opts.Source, "source", false, "Show only non-forks")
cmd.Flags().BoolVar(&opts.Fork, "fork", false, "Show only forks")
cmd.Flags().StringVarP(&opts.Language, "language", "l", "", "Filter by primary coding language")
cmd.Flags().BoolVar(&opts.Archived, "archived", false, "Show only archived repositories")
cmd.Flags().BoolVar(&opts.NonArchived, "no-archived", false, "Omit archived repositories")
return cmd
}
func listRun(opts *ListOptions) error {
httpClient, err := opts.HttpClient()
if err != nil {
return err
}
filter := FilterOptions{
Visibility: opts.Visibility,
Fork: opts.Fork,
Source: opts.Source,
Language: opts.Language,
Archived: opts.Archived,
NonArchived: opts.NonArchived,
}
cfg, err := opts.Config()
if err != nil {
return err
}
host, err := cfg.DefaultHost()
if err != nil {
return err
}
listResult, err := listRepos(httpClient, host, opts.Limit, opts.Owner, filter)
if err != nil {
return err
}
if err := opts.IO.StartPager(); err != nil {
fmt.Fprintf(opts.IO.ErrOut, "error starting pager: %v\n", err)
}
defer opts.IO.StopPager()
cs := opts.IO.ColorScheme()
tp := utils.NewTablePrinter(opts.IO)
now := opts.Now()
for _, repo := range listResult.Repositories {
info := repo.Info()
infoColor := cs.Gray
if repo.IsPrivate {
infoColor = cs.Yellow
}
t := repo.PushedAt
// if listResult.FromSearch {
// t = repo.UpdatedAt
// }
tp.AddField(repo.NameWithOwner, nil, cs.Bold)
tp.AddField(text.ReplaceExcessiveWhitespace(repo.Description), nil, nil)
tp.AddField(info, nil, infoColor)
if tp.IsTTY() {
tp.AddField(utils.FuzzyAgoAbbr(now, t), nil, cs.Gray)
} else {
tp.AddField(t.Format(time.RFC3339), nil, nil)
}
tp.EndRow()
}
if opts.IO.IsStdoutTTY() {
hasFilters := filter.Visibility != "" || filter.Fork || filter.Source || filter.Language != ""
title := listHeader(listResult.Owner, len(listResult.Repositories), listResult.TotalCount, hasFilters)
fmt.Fprintf(opts.IO.Out, "\n%s\n\n", title)
}
return tp.Render()
}
func listHeader(owner string, matchCount, totalMatchCount int, hasFilters bool) string {
if totalMatchCount == 0 {
if hasFilters {
return "No results match your search"
} else if owner != "" {
return "There are no repositories in @" + owner
}
return "No results"
}
var matchStr string
if hasFilters {
matchStr = " that match your search"
}
return fmt.Sprintf("Showing %d of %d repositories in @%s%s", matchCount, totalMatchCount, owner, matchStr)
}