github.com/secman-team/gh-api@v1.8.2/pkg/cmd/repo/list/list.go (about) 1 package list 2 3 import ( 4 "fmt" 5 "net/http" 6 "time" 7 8 "github.com/secman-team/gh-api/core/config" 9 "github.com/secman-team/gh-api/pkg/cmdutil" 10 "github.com/secman-team/gh-api/pkg/iostreams" 11 "github.com/secman-team/gh-api/pkg/text" 12 "github.com/secman-team/gh-api/utils" 13 "github.com/spf13/cobra" 14 ) 15 16 type ListOptions struct { 17 HttpClient func() (*http.Client, error) 18 Config func() (config.Config, error) 19 IO *iostreams.IOStreams 20 21 Limit int 22 Owner string 23 24 Visibility string 25 Fork bool 26 Source bool 27 Language string 28 Archived bool 29 NonArchived bool 30 31 Now func() time.Time 32 } 33 34 func NewCmdList(f *cmdutil.Factory, runF func(*ListOptions) error) *cobra.Command { 35 opts := ListOptions{ 36 IO: f.IOStreams, 37 Config: f.Config, 38 HttpClient: f.HttpClient, 39 Now: time.Now, 40 } 41 42 var ( 43 flagPublic bool 44 flagPrivate bool 45 ) 46 47 cmd := &cobra.Command{ 48 Use: "list [<owner>]", 49 Args: cobra.MaximumNArgs(1), 50 Short: "List repositories owned by user or organization", 51 RunE: func(c *cobra.Command, args []string) error { 52 if opts.Limit < 1 { 53 return &cmdutil.FlagError{Err: fmt.Errorf("invalid limit: %v", opts.Limit)} 54 } 55 56 if flagPrivate && flagPublic { 57 return &cmdutil.FlagError{Err: fmt.Errorf("specify only one of `--public` or `--private`")} 58 } 59 if opts.Source && opts.Fork { 60 return &cmdutil.FlagError{Err: fmt.Errorf("specify only one of `--source` or `--fork`")} 61 } 62 if opts.Archived && opts.NonArchived { 63 return &cmdutil.FlagError{Err: fmt.Errorf("specify only one of `--archived` or `--no-archived`")} 64 } 65 66 if flagPrivate { 67 opts.Visibility = "private" 68 } else if flagPublic { 69 opts.Visibility = "public" 70 } 71 72 if len(args) > 0 { 73 opts.Owner = args[0] 74 } 75 76 if runF != nil { 77 return runF(&opts) 78 } 79 return listRun(&opts) 80 }, 81 } 82 83 cmd.Flags().IntVarP(&opts.Limit, "limit", "L", 30, "Maximum number of repositories to list") 84 cmd.Flags().BoolVar(&flagPrivate, "private", false, "Show only private repositories") 85 cmd.Flags().BoolVar(&flagPublic, "public", false, "Show only public repositories") 86 cmd.Flags().BoolVar(&opts.Source, "source", false, "Show only non-forks") 87 cmd.Flags().BoolVar(&opts.Fork, "fork", false, "Show only forks") 88 cmd.Flags().StringVarP(&opts.Language, "language", "l", "", "Filter by primary coding language") 89 cmd.Flags().BoolVar(&opts.Archived, "archived", false, "Show only archived repositories") 90 cmd.Flags().BoolVar(&opts.NonArchived, "no-archived", false, "Omit archived repositories") 91 92 return cmd 93 } 94 95 func listRun(opts *ListOptions) error { 96 httpClient, err := opts.HttpClient() 97 if err != nil { 98 return err 99 } 100 101 filter := FilterOptions{ 102 Visibility: opts.Visibility, 103 Fork: opts.Fork, 104 Source: opts.Source, 105 Language: opts.Language, 106 Archived: opts.Archived, 107 NonArchived: opts.NonArchived, 108 } 109 110 cfg, err := opts.Config() 111 if err != nil { 112 return err 113 } 114 115 host, err := cfg.DefaultHost() 116 if err != nil { 117 return err 118 } 119 120 listResult, err := listRepos(httpClient, host, opts.Limit, opts.Owner, filter) 121 if err != nil { 122 return err 123 } 124 125 if err := opts.IO.StartPager(); err != nil { 126 fmt.Fprintf(opts.IO.ErrOut, "error starting pager: %v\n", err) 127 } 128 defer opts.IO.StopPager() 129 130 cs := opts.IO.ColorScheme() 131 tp := utils.NewTablePrinter(opts.IO) 132 now := opts.Now() 133 134 for _, repo := range listResult.Repositories { 135 info := repo.Info() 136 infoColor := cs.Gray 137 if repo.IsPrivate { 138 infoColor = cs.Yellow 139 } 140 141 t := repo.PushedAt 142 // if listResult.FromSearch { 143 // t = repo.UpdatedAt 144 // } 145 146 tp.AddField(repo.NameWithOwner, nil, cs.Bold) 147 tp.AddField(text.ReplaceExcessiveWhitespace(repo.Description), nil, nil) 148 tp.AddField(info, nil, infoColor) 149 if tp.IsTTY() { 150 tp.AddField(utils.FuzzyAgoAbbr(now, t), nil, cs.Gray) 151 } else { 152 tp.AddField(t.Format(time.RFC3339), nil, nil) 153 } 154 tp.EndRow() 155 } 156 157 if opts.IO.IsStdoutTTY() { 158 hasFilters := filter.Visibility != "" || filter.Fork || filter.Source || filter.Language != "" 159 title := listHeader(listResult.Owner, len(listResult.Repositories), listResult.TotalCount, hasFilters) 160 fmt.Fprintf(opts.IO.Out, "\n%s\n\n", title) 161 } 162 163 return tp.Render() 164 } 165 166 func listHeader(owner string, matchCount, totalMatchCount int, hasFilters bool) string { 167 if totalMatchCount == 0 { 168 if hasFilters { 169 return "No results match your search" 170 } else if owner != "" { 171 return "There are no repositories in @" + owner 172 } 173 return "No results" 174 } 175 176 var matchStr string 177 if hasFilters { 178 matchStr = " that match your search" 179 } 180 return fmt.Sprintf("Showing %d of %d repositories in @%s%s", matchCount, totalMatchCount, owner, matchStr) 181 }