cli/pkg/cmd/ruleset/list/list.go
2023-10-20 11:20:02 +02:00

176 lines
5.1 KiB
Go

package list
import (
"fmt"
"net/http"
"strconv"
"strings"
"github.com/MakeNowJust/heredoc"
"github.com/cli/cli/v2/internal/browser"
"github.com/cli/cli/v2/internal/ghinstance"
"github.com/cli/cli/v2/internal/ghrepo"
"github.com/cli/cli/v2/internal/tableprinter"
"github.com/cli/cli/v2/internal/text"
"github.com/cli/cli/v2/pkg/cmd/ruleset/shared"
"github.com/cli/cli/v2/pkg/cmdutil"
"github.com/cli/cli/v2/pkg/iostreams"
ghAuth "github.com/cli/go-gh/v2/pkg/auth"
"github.com/spf13/cobra"
)
type ListOptions struct {
HttpClient func() (*http.Client, error)
IO *iostreams.IOStreams
BaseRepo func() (ghrepo.Interface, error)
Browser browser.Browser
Limit int
IncludeParents bool
WebMode bool
Organization string
}
func NewCmdList(f *cmdutil.Factory, runF func(*ListOptions) error) *cobra.Command {
opts := &ListOptions{
IO: f.IOStreams,
HttpClient: f.HttpClient,
Browser: f.Browser,
}
cmd := &cobra.Command{
Use: "list",
Short: "List rulesets for a repository or organization",
Long: heredoc.Doc(`
List GitHub rulesets for a repository or organization.
If no options are provided, the current repository's rulesets are listed. You can query a different
repository's rulesets by using the --repo flag. You can also use the --org flag to list rulesets
configured for the provided organization.
Use the --parents flag to control whether rulesets configured at higher levels that also apply to the provided
repository or organization should be returned. The default is true.
Your access token must have the admin:org scope to use the --org flag, which can be granted by running "gh auth refresh -s admin:org".
`),
Example: heredoc.Doc(`
# List rulesets in the current repository
$ gh ruleset list
# List rulesets in a different repository, including those configured at higher levels
$ gh ruleset list --repo owner/repo --parents
# List rulesets in an organization
$ gh ruleset list --org org-name
`),
Aliases: []string{"ls"},
Args: cobra.ExactArgs(0),
RunE: func(cmd *cobra.Command, args []string) error {
if repoOverride, _ := cmd.Flags().GetString("repo"); repoOverride != "" && opts.Organization != "" {
return cmdutil.FlagErrorf("only one of --repo and --org may be specified")
}
// support `-R, --repo` override
opts.BaseRepo = f.BaseRepo
if opts.Limit < 1 {
return cmdutil.FlagErrorf("invalid limit: %v", opts.Limit)
}
if runF != nil {
return runF(opts)
}
return listRun(opts)
},
}
cmd.Flags().IntVarP(&opts.Limit, "limit", "L", 30, "Maximum number of rulesets to list")
cmd.Flags().StringVarP(&opts.Organization, "org", "o", "", "List organization-wide rulesets for the provided organization")
cmd.Flags().BoolVarP(&opts.IncludeParents, "parents", "p", true, "Whether to include rulesets configured at higher levels that also apply")
cmd.Flags().BoolVarP(&opts.WebMode, "web", "w", false, "Open the list of rulesets in the web browser")
return cmd
}
func listRun(opts *ListOptions) error {
httpClient, err := opts.HttpClient()
if err != nil {
return err
}
var repoI ghrepo.Interface
// only one of the repo or org context is necessary
if opts.Organization == "" {
var repoErr error
repoI, repoErr = opts.BaseRepo()
if repoErr != nil {
return repoErr
}
}
hostname, _ := ghAuth.DefaultHost()
if opts.WebMode {
var rulesetURL string
if opts.Organization != "" {
rulesetURL = fmt.Sprintf("%sorganizations/%s/settings/rules", ghinstance.HostPrefix(hostname), opts.Organization)
} else {
rulesetURL = ghrepo.GenerateRepoURL(repoI, "rules")
}
if opts.IO.IsStdoutTTY() {
fmt.Fprintf(opts.IO.Out, "Opening %s in your browser.\n", text.DisplayURL(rulesetURL))
}
return opts.Browser.Browse(rulesetURL)
}
var result *shared.RulesetList
if opts.Organization != "" {
result, err = shared.ListOrgRulesets(httpClient, opts.Organization, opts.Limit, hostname, opts.IncludeParents)
} else {
result, err = shared.ListRepoRulesets(httpClient, repoI, opts.Limit, opts.IncludeParents)
}
if err != nil {
return err
}
if result.TotalCount == 0 {
return shared.NoRulesetsFoundError(opts.Organization, repoI, opts.IncludeParents)
}
opts.IO.DetectTerminalTheme()
if err := opts.IO.StartPager(); err == nil {
defer opts.IO.StopPager()
} else {
fmt.Fprintf(opts.IO.ErrOut, "failed to start pager: %v\n", err)
}
cs := opts.IO.ColorScheme()
if opts.IO.IsStdoutTTY() {
parentsMsg := ""
if opts.IncludeParents {
parentsMsg = " and its parents"
}
inMsg := fmt.Sprintf("%s%s", shared.EntityName(opts.Organization, repoI), parentsMsg)
fmt.Fprintf(opts.IO.Out, "\nShowing %d of %d rulesets in %s\n\n", len(result.Rulesets), result.TotalCount, inMsg)
}
tp := tableprinter.New(opts.IO, tableprinter.WithHeader("ID", "NAME", "SOURCE", "STATUS", "RULES"))
for _, rs := range result.Rulesets {
tp.AddField(strconv.Itoa(rs.DatabaseId), tableprinter.WithColor(cs.Cyan))
tp.AddField(rs.Name, tableprinter.WithColor(cs.Bold))
tp.AddField(shared.RulesetSource(rs))
tp.AddField(strings.ToLower(rs.Enforcement))
tp.AddField(strconv.Itoa(rs.Rules.TotalCount))
tp.EndRow()
}
return tp.Render()
}