add ruleset check command
This commit is contained in:
parent
5c5dcd2a80
commit
e4aa5ba84c
3 changed files with 118 additions and 50 deletions
|
|
@ -9,8 +9,11 @@ import (
|
|||
"github.com/MakeNowJust/heredoc"
|
||||
"github.com/cli/cli/v2/api"
|
||||
"github.com/cli/cli/v2/git"
|
||||
"github.com/cli/cli/v2/internal/browser"
|
||||
"github.com/cli/cli/v2/internal/config"
|
||||
"github.com/cli/cli/v2/internal/ghrepo"
|
||||
"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"
|
||||
|
|
@ -22,9 +25,11 @@ type CheckOptions struct {
|
|||
Config func() (config.Config, error)
|
||||
BaseRepo func() (ghrepo.Interface, error)
|
||||
Git *git.Client
|
||||
Browser browser.Browser
|
||||
|
||||
Branch string
|
||||
Default bool
|
||||
WebMode bool
|
||||
}
|
||||
|
||||
func NewCmdCheck(f *cmdutil.Factory, runF func(*CheckOptions) error) *cobra.Command {
|
||||
|
|
@ -32,33 +37,58 @@ func NewCmdCheck(f *cmdutil.Factory, runF func(*CheckOptions) error) *cobra.Comm
|
|||
IO: f.IOStreams,
|
||||
Config: f.Config,
|
||||
HttpClient: f.HttpClient,
|
||||
Browser: f.Browser,
|
||||
Git: f.GitClient,
|
||||
}
|
||||
cmd := &cobra.Command{
|
||||
Use: "check [<branch>]",
|
||||
Short: "Print rules that would apply to a given branch",
|
||||
Short: "View rules that would apply to a given branch",
|
||||
Long: heredoc.Doc(`
|
||||
TODO
|
||||
View information about GitHub rules that apply to a given branch.
|
||||
|
||||
The provided branch name does not need to exist, rules will be displayed that would apply
|
||||
to a branch with that name. All rules are returned regardless of where they are configured.
|
||||
|
||||
If no branch name is provided, then the current branch will be used.
|
||||
|
||||
The --default flag can be used to view rules that apply to the default branch of the current
|
||||
repository.
|
||||
`),
|
||||
Example: "TODO",
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
Example: heredoc.Doc(`
|
||||
# View all rules that apply to the current branch
|
||||
$ gh ruleset check
|
||||
|
||||
# View all rules that apply to a branch named "my-branch" in a different repository
|
||||
$ gh ruleset check my-branch --repo owner/repo
|
||||
|
||||
# View all rules that apply to the default branch in a different repository
|
||||
$ gh ruleset check --default --repo owner/repo
|
||||
|
||||
# View a ruleset configured in a different repository or any of its parents
|
||||
$ gh ruleset view 23 --repo owner/repo
|
||||
|
||||
# View an organization-level ruleset
|
||||
$ gh ruleset view 23 --org my-org
|
||||
`),
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
// support `-R, --repo` override
|
||||
opts.BaseRepo = f.BaseRepo
|
||||
|
||||
// TODO flag to do a push
|
||||
|
||||
if len(args) > 0 {
|
||||
opts.Branch = args[0]
|
||||
}
|
||||
|
||||
if runF != nil {
|
||||
return runF(opts)
|
||||
if err := cmdutil.MutuallyExclusive(
|
||||
"specify only one of `--default` or a branch name",
|
||||
opts.Branch != "",
|
||||
opts.Default,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if opts.Branch != "" && opts.Default {
|
||||
return cmdutil.FlagErrorf(
|
||||
"branch argument '%s' and --default mutually exclusive", opts.Branch)
|
||||
if runF != nil {
|
||||
return runF(opts)
|
||||
}
|
||||
|
||||
return checkRun(opts)
|
||||
|
|
@ -71,10 +101,6 @@ func NewCmdCheck(f *cmdutil.Factory, runF func(*CheckOptions) error) *cobra.Comm
|
|||
}
|
||||
|
||||
func checkRun(opts *CheckOptions) error {
|
||||
// TODO ask about pushing (if interactive)
|
||||
// TODO error if not interactive and --push not specified
|
||||
// TODO parsing for errors on push
|
||||
|
||||
httpClient, err := opts.HttpClient()
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -103,18 +129,33 @@ func checkRun(opts *CheckOptions) error {
|
|||
}
|
||||
}
|
||||
|
||||
var lol interface{}
|
||||
rawPath := fmt.Sprintf("rules?ref=%s%s", url.QueryEscape("refs/heads/"), url.QueryEscape(opts.Branch))
|
||||
rulesURL := ghrepo.GenerateRepoURL(repoI, rawPath)
|
||||
|
||||
if opts.WebMode {
|
||||
if opts.IO.IsStdoutTTY() {
|
||||
fmt.Fprintf(opts.IO.Out, "Opening %s in your browser.\n", text.DisplayURL(rulesURL))
|
||||
}
|
||||
|
||||
return opts.Browser.Browse(rulesURL)
|
||||
}
|
||||
|
||||
var rules []shared.RulesetRule
|
||||
|
||||
endpoint := fmt.Sprintf("repos/%s/%s/rules/branches/%s", repoI.RepoOwner(), repoI.RepoName(), url.PathEscape(opts.Branch))
|
||||
|
||||
if err = client.REST(repoI.RepoHost(), "GET", endpoint, nil, &lol); err != nil {
|
||||
if err = client.REST(repoI.RepoHost(), "GET", endpoint, nil, &rules); err != nil {
|
||||
return fmt.Errorf("GET %s failed: %w", endpoint, err)
|
||||
}
|
||||
|
||||
// TODO handle 404s gracefully
|
||||
// TODO actually parse JSON
|
||||
w := opts.IO.Out
|
||||
|
||||
fmt.Printf("DBG %#v\n", lol)
|
||||
fmt.Fprintf(w, "%d rules apply to branch %s in repo %s/%s\n", len(rules), opts.Branch, repoI.RepoOwner(), repoI.RepoName())
|
||||
|
||||
if len(rules) > 0 {
|
||||
fmt.Fprint(w, "\n")
|
||||
fmt.Fprint(w, shared.ParseRulesForDisplay(rules))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ package shared
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/cli/cli/v2/internal/ghrepo"
|
||||
"github.com/cli/cli/v2/pkg/cmdutil"
|
||||
|
|
@ -35,17 +37,22 @@ type RulesetREST struct {
|
|||
// TODO is this source field used?
|
||||
SourceType string `json:"source_type"`
|
||||
Source string
|
||||
Rules []struct {
|
||||
Type string
|
||||
Parameters map[string]interface{}
|
||||
}
|
||||
Links struct {
|
||||
Rules []RulesetRule
|
||||
Links struct {
|
||||
Html struct {
|
||||
Href string
|
||||
}
|
||||
} `json:"_links"`
|
||||
}
|
||||
|
||||
type RulesetRule struct {
|
||||
Type string
|
||||
Parameters map[string]interface{}
|
||||
RulesetSourceType string `json:"ruleset_source_type"`
|
||||
RulesetSource string `json:"ruleset_source"`
|
||||
RulesetId int `json:"ruleset_id"`
|
||||
}
|
||||
|
||||
// Returns the source of the ruleset in the format "owner/name (repo)" or "owner (org)"
|
||||
func RulesetSource(rs RulesetGraphQL) string {
|
||||
var level string
|
||||
|
|
@ -60,6 +67,50 @@ func RulesetSource(rs RulesetGraphQL) string {
|
|||
return fmt.Sprintf("%s (%s)", rs.Source.Owner, level)
|
||||
}
|
||||
|
||||
func ParseRulesForDisplay(rules []RulesetRule) string {
|
||||
var display strings.Builder
|
||||
|
||||
// sort keys for consistent responses
|
||||
sort.SliceStable(rules, func(i, j int) bool {
|
||||
return rules[i].Type < rules[j].Type
|
||||
})
|
||||
|
||||
for _, rule := range rules {
|
||||
display.WriteString(fmt.Sprintf("- %s", rule.Type))
|
||||
|
||||
if rule.Parameters != nil && len(rule.Parameters) > 0 {
|
||||
display.WriteString(": ")
|
||||
|
||||
// sort these keys too for consistency
|
||||
params := make([]string, 0, len(rule.Parameters))
|
||||
for p := range rule.Parameters {
|
||||
params = append(params, p)
|
||||
}
|
||||
sort.Strings(params)
|
||||
|
||||
for _, n := range params {
|
||||
display.WriteString(fmt.Sprintf("[%s: %v] ", n, rule.Parameters[n]))
|
||||
}
|
||||
}
|
||||
|
||||
// ruleset source info is only returned from the "get rules for a branch" endpoint
|
||||
if rule.RulesetSource != "" {
|
||||
display.WriteString(
|
||||
fmt.Sprintf(
|
||||
"\n (configured in ruleset %d from %s %s)\n",
|
||||
rule.RulesetId,
|
||||
strings.ToLower(rule.RulesetSourceType),
|
||||
rule.RulesetSource,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
display.WriteString("\n")
|
||||
}
|
||||
|
||||
return display.String()
|
||||
}
|
||||
|
||||
func NoRulesetsFoundError(orgOption string, repoI ghrepo.Interface, includeParents bool) error {
|
||||
entityName := EntityName(orgOption, repoI)
|
||||
parentsMsg := ""
|
||||
|
|
|
|||
|
|
@ -248,31 +248,7 @@ func viewRun(opts *ViewOptions) error {
|
|||
if len(rs.Rules) == 0 {
|
||||
fmt.Fprintf(w, "No rules configured\n")
|
||||
} else {
|
||||
// sort keys for consistent responses
|
||||
sort.SliceStable(rs.Rules, func(i, j int) bool {
|
||||
return rs.Rules[i].Type < rs.Rules[j].Type
|
||||
})
|
||||
|
||||
for _, rule := range rs.Rules {
|
||||
fmt.Fprintf(w, "- %s", rule.Type)
|
||||
|
||||
if rule.Parameters != nil && len(rule.Parameters) > 0 {
|
||||
fmt.Fprintf(w, ": ")
|
||||
|
||||
// sort these keys too for consistency
|
||||
params := make([]string, 0, len(rule.Parameters))
|
||||
for p := range rule.Parameters {
|
||||
params = append(params, p)
|
||||
}
|
||||
sort.Strings(params)
|
||||
|
||||
for _, n := range params {
|
||||
fmt.Fprintf(w, "[%s: %v] ", n, rule.Parameters[n])
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Fprint(w, "\n")
|
||||
}
|
||||
fmt.Fprint(w, shared.ParseRulesForDisplay(rs.Rules))
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue