Skip to content

Commit

Permalink
allow getting parents, move list to shared package
Browse files Browse the repository at this point in the history
  • Loading branch information
vaindil committed Jul 5, 2023
1 parent 7f81645 commit 3e64192
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 45 deletions.
39 changes: 27 additions & 12 deletions pkg/cmd/ruleset/list/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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 {
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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 {
Expand All @@ -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)
}

Expand All @@ -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()
29 changes: 17 additions & 12 deletions pkg/cmd/ruleset/list/http.go → pkg/cmd/ruleset/shared/http.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
package list
package shared

import (
"fmt"
"net/http"

"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
Expand All @@ -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)
Expand All @@ -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)

Expand Down Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions pkg/cmd/ruleset/shared/shared.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ type Ruleset struct {
// Protected bool
// } `json:"repository_name"`
}
Source struct {
RepoOwner string
OrgOwner string
}
RulesGql struct {
TotalCount int
}
Expand Down
45 changes: 24 additions & 21 deletions pkg/cmd/ruleset/view/view.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -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)
Expand All @@ -134,14 +118,33 @@ 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)

switch rs.Enforcement {
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:
Expand All @@ -165,16 +168,16 @@ 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)
}
}

fmt.Fprintf(w, "\n%s\n", cs.Bold("Conditions"))
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)
Expand Down

0 comments on commit 3e64192

Please sign in to comment.