allow getting parents, move list to shared package

This commit is contained in:
vaindil 2023-05-01 14:13:15 -04:00
parent 7f81645c78
commit 3e6419237d
4 changed files with 72 additions and 45 deletions

View file

@ -13,6 +13,7 @@ import (
"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"
"github.com/spf13/cobra"
@ -25,9 +26,10 @@ type ListOptions struct {
BaseRepo func() (ghrepo.Interface, error)
Browser browser.Browser
Limit int
WebMode bool
Organization string
Limit int
IncludeParents bool
WebMode bool
Organization string
}
func NewCmdList(f *cmdutil.Factory, runF func(*ListOptions) error) *cobra.Command {
@ -62,8 +64,9 @@ func NewCmdList(f *cmdutil.Factory, runF func(*ListOptions) error) *cobra.Comman
}
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")
cmd.Flags().BoolVarP(&opts.WebMode, "web", "w", false, "List rulesets in the web browser")
cmd.Flags().StringVarP(&opts.Organization, "org", "o", "", "List organization-wide rulesets for the provided organization")
cmd.Flags().BoolVarP(&opts.IncludeParents, "parents", "p", false, "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
}
@ -101,12 +104,12 @@ func listRun(opts *ListOptions) error {
return opts.Browser.Browse(rulesetURL)
}
var result *RulesetList
var result *shared.RulesetList
if opts.Organization != "" {
result, err = listOrgRulesets(httpClient, opts.Organization, opts.Limit, hostname)
result, err = shared.ListOrgRulesets(httpClient, opts.Organization, opts.Limit, hostname, opts.IncludeParents)
} else {
result, err = listRepoRulesets(httpClient, repoI, opts.Limit)
result, err = shared.ListRepoRulesets(httpClient, repoI, opts.Limit, opts.IncludeParents)
}
if err != nil {
@ -121,7 +124,11 @@ func listRun(opts *ListOptions) error {
}
if result.TotalCount == 0 {
msg := fmt.Sprintf("no rulesets found in %s", entityName)
parentsMsg := ""
if opts.IncludeParents {
parentsMsg = " or its parents"
}
msg := fmt.Sprintf("no rulesets found in %s%s", entityName, parentsMsg)
return cmdutil.NewNoResultsError(msg)
}
@ -139,17 +146,25 @@ func listRun(opts *ListOptions) error {
}
tp := tableprinter.New(opts.IO)
tp.HeaderRow("ID", "NAME", "STATUS", "TARGET")
tp.HeaderRow("ID", "NAME", "SOURCE", "STATUS", "TARGET", "RULES")
for _, rs := range result.Rulesets {
tp.AddField(strconv.Itoa(rs.Id))
tp.AddField(rs.Name, tableprinter.WithColor(cs.Bold))
var ownerString string
if rs.Source.RepoOwner != "" {
ownerString = fmt.Sprintf("%s (repo)", rs.Source.RepoOwner)
} else if rs.Source.OrgOwner != "" {
ownerString = fmt.Sprintf("%s (org)", rs.Source.OrgOwner)
} else {
ownerString = "(unknown)"
}
tp.AddField(ownerString)
tp.AddField(strings.ToLower(rs.Enforcement))
tp.AddField(strings.ToLower(rs.Target))
tp.AddField(strconv.Itoa(rs.RulesGql.TotalCount))
tp.EndRow()
}
return tp.Render()
}
// func getRulesets()

View file

@ -1,4 +1,4 @@
package list
package shared
import (
"fmt"
@ -6,14 +6,13 @@ import (
"github.com/cli/cli/v2/api"
"github.com/cli/cli/v2/internal/ghrepo"
"github.com/cli/cli/v2/pkg/cmd/ruleset/shared"
)
type RulesetResponse struct {
Level struct {
Rulesets struct {
TotalCount int
Nodes []shared.Ruleset
Nodes []Ruleset
PageInfo struct {
HasNextPage bool
EndCursor string
@ -24,21 +23,23 @@ type RulesetResponse struct {
type RulesetList struct {
TotalCount int
Rulesets []shared.Ruleset
Rulesets []Ruleset
}
func listRepoRulesets(httpClient *http.Client, repo ghrepo.Interface, limit int) (*RulesetList, error) {
func ListRepoRulesets(httpClient *http.Client, repo ghrepo.Interface, limit int, includeParents bool) (*RulesetList, error) {
variables := map[string]interface{}{
"owner": repo.RepoOwner(),
"repo": repo.RepoName(),
"owner": repo.RepoOwner(),
"repo": repo.RepoName(),
"includeParents": includeParents,
}
return listRulesets(httpClient, rulesetsQuery(false), variables, limit, repo.RepoHost())
}
func listOrgRulesets(httpClient *http.Client, orgLogin string, limit int, host string) (*RulesetList, error) {
func ListOrgRulesets(httpClient *http.Client, orgLogin string, limit int, host string, includeParents bool) (*RulesetList, error) {
variables := map[string]interface{}{
"login": orgLogin,
"login": orgLogin,
"includeParents": includeParents,
}
return listRulesets(httpClient, rulesetsQuery(true), variables, limit, host)
@ -48,7 +49,7 @@ func listRulesets(httpClient *http.Client, query string, variables map[string]in
pageLimit := min(limit, 100)
res := RulesetList{
Rulesets: []shared.Ruleset{},
Rulesets: []Ruleset{},
}
client := api.NewClientFromHTTP(httpClient)
@ -90,16 +91,20 @@ func rulesetsQuery(org bool) string {
level = "repository(owner: $owner, name: $repo)"
}
str := fmt.Sprintf("query RulesetList($limit: Int!, $endCursor: String, %s) { level: %s {", args, level)
str := fmt.Sprintf("query RulesetList($limit: Int!, $endCursor: String, $includeParents: Boolean, %s) { level: %s {", args, level)
return str + `
rulesets(first: $limit, after: $endCursor) {
rulesets(first: $limit, after: $endCursor, includeParents: $includeParents) {
totalCount
nodes {
id: databaseId
name
target
enforcement
source {
... on Repository { repoOwner: nameWithOwner }
... on Organization { orgOwner: login }
}
# conditions {
# refName {
# include

View file

@ -21,6 +21,10 @@ type Ruleset struct {
// Protected bool
// } `json:"repository_name"`
}
Source struct {
RepoOwner string
OrgOwner string
}
RulesGql struct {
TotalCount int
}

View file

@ -81,7 +81,7 @@ func NewCmdView(f *cmdutil.Factory, runF func(*ViewOptions) error) *cobra.Comman
}
cmd.Flags().BoolVarP(&opts.WebMode, "web", "w", false, "Open the ruleset in the browser")
cmd.Flags().StringVarP(&opts.Organization, "org", "o", "", "Organization name if the ID ")
cmd.Flags().StringVarP(&opts.Organization, "org", "o", "", "Organization name if the provided ID is an organization-level ruleset")
return cmd
}
@ -104,22 +104,6 @@ func viewRun(opts *ViewOptions) error {
hostname, _ := cfg.DefaultHost()
if opts.WebMode {
// TODO need to validate ruleset's existence before opening
var rulesetURL string
if opts.Organization != "" {
rulesetURL = fmt.Sprintf("%sorganizations/%s/settings/rules/%s", ghinstance.HostPrefix(hostname), opts.Organization, opts.ID)
} else {
rulesetURL = ghrepo.GenerateRepoURL(repoI, "settings/rules/%s", opts.ID)
}
if opts.IO.IsStdoutTTY() {
fmt.Fprintf(opts.IO.Out, "Opening %s in your browser.\n", text.DisplayURL(rulesetURL))
}
return opts.Browser.Browse(rulesetURL)
}
var rs *shared.Ruleset
if opts.Organization != "" {
rs, err = viewOrgRuleset(httpClient, opts.Organization, opts.ID, hostname)
@ -134,6 +118,25 @@ func viewRun(opts *ViewOptions) error {
cs := opts.IO.ColorScheme()
w := opts.IO.Out
if opts.WebMode {
if rs != nil {
var rulesetURL string
if opts.Organization != "" {
rulesetURL = fmt.Sprintf("%sorganizations/%s/settings/rules/%s", ghinstance.HostPrefix(hostname), opts.Organization, opts.ID)
} else {
rulesetURL = ghrepo.GenerateRepoURL(repoI, "settings/rules/%s", opts.ID)
}
if opts.IO.IsStdoutTTY() {
fmt.Fprintf(opts.IO.Out, "Opening %s in your browser.\n", text.DisplayURL(rulesetURL))
}
return opts.Browser.Browse(rulesetURL)
} else {
fmt.Fprintf(w, "ruleset not found\n")
}
}
fmt.Fprintf(w, "\n%s\n", cs.Bold(rs.Name))
fmt.Fprintf(w, "ID: %d\n", rs.Id)
@ -141,7 +144,7 @@ func viewRun(opts *ViewOptions) error {
case "disabled":
fmt.Fprintf(w, "%s\n", cs.Red("Disabled"))
case "evaluate":
fmt.Fprintf(w, "%s\n", cs.Yellow("Evaluate Mode (not being enforced)"))
fmt.Fprintf(w, "%s\n", cs.Yellow("Evaluate Mode (not enforced)"))
case "active":
fmt.Fprintf(w, "%s\n", cs.Green("Active"))
default:
@ -165,7 +168,7 @@ func viewRun(opts *ViewOptions) error {
fmt.Fprintf(w, "Actor types allowed to bypass:\n")
for name, count := range types {
fmt.Fprintf(w, "- %s: %d actors\n", name, count)
fmt.Fprintf(w, "- %s: %d configured\n", name, count)
}
}
@ -173,8 +176,8 @@ func viewRun(opts *ViewOptions) error {
if len(rs.Conditions) == 0 {
fmt.Fprintf(w, "No conditions configured\n")
} else {
// sort keys for consistent responses, mismatched types don't allow this to be broken
// into a separate function
// sort keys for consistent responses, can't make a separate function due to
// mismatched types
keys := make([]string, 0, len(rs.Conditions))
for key := range rs.Conditions {
keys = append(keys, key)