Skip to content

Commit

Permalink
misc: improve flags help (#135)
Browse files Browse the repository at this point in the history
  • Loading branch information
MartinKolarik authored Sep 27, 2024
1 parent 6b5f095 commit ef7ddc9
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 51 deletions.
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,17 +96,14 @@ Additional Commands:
limits Show the current rate limits
version Display the version of your installed Globalping CLI
Flags:
-C, --ci disable real-time terminal updates and colors, suitable for CI and
scripting (default false)
Global Measurement Flags:
-F, --from string specify the probe locations as a comma-separated list; you may use:
- names of continents, regions, countries, US states, cities, or
networks
- [@1 | first, @2 ... @-2, @-1 | last | previous] to run with the probes
from previous measurements in this session
- an ID of a previous measurement to run with its probes
(default "world")
-h, --help help for globalping
-4, --ipv4 resolve names to IPv4 addresses
-6, --ipv6 resolve names to IPv6 addresses
-J, --json output results in JSON format (default false)
Expand All @@ -116,6 +113,11 @@ Flags:
--share print a link at the end of the results to visualize them online (default
false)
Global Flags:
-C, --ci disable real-time terminal updates and colors, suitable for CI and scripting
(default false)
-h, --help help for globalping
Use "globalping [command] --help" for more information about a command.
```

Expand Down
17 changes: 10 additions & 7 deletions cmd/dns.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import (
"github.com/jsdelivr/globalping-cli/globalping"
"github.com/jsdelivr/globalping-cli/view"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)

func (r *Root) initDNS() {
func (r *Root) initDNS(measurementFlags *pflag.FlagSet, localFlags *pflag.FlagSet) {
dnsCmd := &cobra.Command{
RunE: r.RunDNS,
Use: "dns [target] from [location | measurement ID | @1 | first | @-1 | last | previous]",
Expand Down Expand Up @@ -53,12 +54,14 @@ Examples:
}

// dns specific flags
flags := dnsCmd.Flags()
flags.StringVar(&r.ctx.Protocol, "protocol", r.ctx.Protocol, "specify the protocol to use for the DNS query: TCP or UDP (default \"udp\")")
flags.IntVar(&r.ctx.Port, "port", r.ctx.Port, "specify a non-standard port on the server to send the query to (default 53)")
flags.StringVar(&r.ctx.Resolver, "resolver", r.ctx.Resolver, "specify the hostname or IP address of the name server to use as the resolver (default defined by the probe)")
flags.StringVar(&r.ctx.QueryType, "type", r.ctx.QueryType, "specify the type of DNS query to perform (default \"A\")")
flags.BoolVar(&r.ctx.Trace, "trace", r.ctx.Trace, "enable tracing of the delegation path from the root name servers (default false)")
localFlags.BoolP("help", "h", false, "help for dns")
localFlags.StringVar(&r.ctx.Protocol, "protocol", r.ctx.Protocol, "specify the protocol to use for the DNS query: TCP or UDP (default \"udp\")")
localFlags.IntVar(&r.ctx.Port, "port", r.ctx.Port, "specify a non-standard port on the server to send the query to (default 53)")
localFlags.StringVar(&r.ctx.Resolver, "resolver", r.ctx.Resolver, "specify the hostname or IP address of the name server to use as the resolver (default defined by the probe)")
localFlags.StringVar(&r.ctx.QueryType, "type", r.ctx.QueryType, "specify the type of DNS query to perform (default \"A\")")
localFlags.BoolVar(&r.ctx.Trace, "trace", r.ctx.Trace, "enable tracing of the delegation path from the root name servers (default false)")
dnsCmd.Flags().AddFlagSet(measurementFlags)
dnsCmd.Flags().AddFlagSet(localFlags)

r.Cmd.AddCommand(dnsCmd)
}
Expand Down
25 changes: 14 additions & 11 deletions cmd/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ import (
"github.com/jsdelivr/globalping-cli/view"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)

func (r *Root) initHTTP() {
func (r *Root) initHTTP(measurementFlags *pflag.FlagSet, localFlags *pflag.FlagSet) {
httpCmd := &cobra.Command{
RunE: r.RunHTTP,
Use: "http [target] from [location | measurement ID | @1 | first | @-1 | last | previous]",
Expand Down Expand Up @@ -68,16 +69,18 @@ Examples:
}

// http specific flags
flags := httpCmd.Flags()
flags.StringVar(&r.ctx.Protocol, "protocol", r.ctx.Protocol, "specify the protocol to use: HTTP, HTTPS, or HTTP2 (default \"HTTP\")")
flags.IntVar(&r.ctx.Port, "port", r.ctx.Port, "specify the port to use (default 80 for HTTP, 443 for HTTPS and HTTP2)")
flags.StringVar(&r.ctx.Resolver, "resolver", r.ctx.Resolver, "specify the hostname or IP address of the name server to use for the DNS lookup (default defined by the probe)")
flags.StringVar(&r.ctx.Host, "host", r.ctx.Host, "specify the Host header to add to the request (default host's defined in command target)")
flags.StringVar(&r.ctx.Path, "path", r.ctx.Path, "specify the URL pathname (default \"/\")")
flags.StringVar(&r.ctx.Query, "query", r.ctx.Query, "specify a query string to add")
flags.StringVarP(&r.ctx.Method, "method", "X", r.ctx.Method, "specify the HTTP method to use: HEAD or GET (default \"HEAD\")")
flags.StringArrayVarP(&r.ctx.Headers, "header", "H", r.ctx.Headers, "add HTTP headers to the request in the format \"Key: Value\"; to add multiple headers, define the flag for each one separately")
flags.BoolVar(&r.ctx.Full, "full", r.ctx.Full, "enable full output when performing an HTTP GET request to display the status, headers, and body")
localFlags.BoolP("help", "h", false, "help for http")
localFlags.StringVar(&r.ctx.Protocol, "protocol", r.ctx.Protocol, "specify the protocol to use: HTTP, HTTPS, or HTTP2 (default \"HTTP\")")
localFlags.IntVar(&r.ctx.Port, "port", r.ctx.Port, "specify the port to use (default 80 for HTTP, 443 for HTTPS and HTTP2)")
localFlags.StringVar(&r.ctx.Resolver, "resolver", r.ctx.Resolver, "specify the hostname or IP address of the name server to use for the DNS lookup (default defined by the probe)")
localFlags.StringVar(&r.ctx.Host, "host", r.ctx.Host, "specify the Host header to add to the request (default host's defined in command target)")
localFlags.StringVar(&r.ctx.Path, "path", r.ctx.Path, "specify the URL pathname (default \"/\")")
localFlags.StringVar(&r.ctx.Query, "query", r.ctx.Query, "specify a query string to add")
localFlags.StringVarP(&r.ctx.Method, "method", "X", r.ctx.Method, "specify the HTTP method to use: HEAD or GET (default \"HEAD\")")
localFlags.StringArrayVarP(&r.ctx.Headers, "header", "H", r.ctx.Headers, "add HTTP headers to the request in the format \"Key: Value\"; to add multiple headers, define the flag for each one separately")
localFlags.BoolVar(&r.ctx.Full, "full", r.ctx.Full, "enable full output when performing an HTTP GET request to display the status, headers, and body")
httpCmd.Flags().AddFlagSet(measurementFlags)
httpCmd.Flags().AddFlagSet(localFlags)

r.Cmd.AddCommand(httpCmd)
}
Expand Down
13 changes: 8 additions & 5 deletions cmd/mtr.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import (
"github.com/jsdelivr/globalping-cli/globalping"
"github.com/jsdelivr/globalping-cli/view"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)

func (r *Root) initMTR() {
func (r *Root) initMTR(measurementFlags *pflag.FlagSet, localFlags *pflag.FlagSet) {
mtrCmd := &cobra.Command{
RunE: r.RunMTR,
Use: "mtr [target] from [location | measurement ID | @1 | first | @-1 | last | previous]",
Expand Down Expand Up @@ -46,10 +47,12 @@ Examples:
}

// mtr specific flags
flags := mtrCmd.Flags()
flags.StringVar(&r.ctx.Protocol, "protocol", r.ctx.Protocol, "specify the protocol to use for MTR: ICMP, TCP, or UDP (default \"icmp\")")
flags.IntVar(&r.ctx.Port, "port", r.ctx.Port, "specify the port to use for MTR; only applicable for the TCP protocol (default 53)")
flags.IntVar(&r.ctx.Packets, "packets", r.ctx.Packets, "specify the number of packets to send to each hop (default 3)")
localFlags.BoolP("help", "h", false, "help for mtr")
localFlags.StringVar(&r.ctx.Protocol, "protocol", r.ctx.Protocol, "specify the protocol to use for MTR: ICMP, TCP, or UDP (default \"icmp\")")
localFlags.IntVar(&r.ctx.Port, "port", r.ctx.Port, "specify the port to use for MTR; only applicable for the TCP protocol (default 53)")
localFlags.IntVar(&r.ctx.Packets, "packets", r.ctx.Packets, "specify the number of packets to send to each hop (default 3)")
mtrCmd.Flags().AddFlagSet(measurementFlags)
mtrCmd.Flags().AddFlagSet(localFlags)

r.Cmd.AddCommand(mtrCmd)
}
Expand Down
11 changes: 7 additions & 4 deletions cmd/ping.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ import (
"github.com/jsdelivr/globalping-cli/globalping"
"github.com/jsdelivr/globalping-cli/view"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)

func (r *Root) initPing() {
func (r *Root) initPing(measurementFlags *pflag.FlagSet, localFlags *pflag.FlagSet) {
pingCmd := &cobra.Command{
RunE: r.RunPing,
Use: "ping [target] from [location | measurement ID | @1 | first | @-1 | last | previous]",
Expand Down Expand Up @@ -51,9 +52,11 @@ Examples:
}

// ping specific flags
flags := pingCmd.Flags()
flags.IntVar(&r.ctx.Packets, "packets", r.ctx.Packets, "specify the number of ECHO_REQUEST packets to send (default 3)")
flags.BoolVar(&r.ctx.Infinite, "infinite", r.ctx.Infinite, "enable continuous pinging of the target until manually stopped (default false)")
localFlags.BoolP("help", "h", false, "help for ping")
localFlags.IntVar(&r.ctx.Packets, "packets", r.ctx.Packets, "specify the number of ECHO_REQUEST packets to send (default 3)")
localFlags.BoolVar(&r.ctx.Infinite, "infinite", r.ctx.Infinite, "enable continuous pinging of the target until manually stopped (default false)")
pingCmd.Flags().AddFlagSet(measurementFlags)
pingCmd.Flags().AddFlagSet(localFlags)

r.Cmd.AddCommand(pingCmd)
}
Expand Down
75 changes: 59 additions & 16 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import (
"github.com/spf13/cobra"
)

var flagGroups = map[string]*pflag.FlagSet{}

type Root struct {
printer *view.Printer
ctx *view.Context
Expand Down Expand Up @@ -119,31 +121,45 @@ For more information about the platform, tips, and best practices, visit our Git
root.Cmd.SetErr(printer.ErrWriter)

cobra.AddTemplateFunc("wrappedFlagUsages", wrappedFlagUsages)
cobra.AddTemplateFunc("isMeasurementCommand", isMeasurementCommand)
cobra.AddTemplateFunc("localMeasurementFlags", localMeasurementFlags)
cobra.AddTemplateFunc("globalMeasurementFlags", globalMeasurementFlags)

root.Cmd.SetUsageTemplate(usageTemplate)
root.Cmd.SetHelpTemplate(helpTemplate)

// Global flags
flags := root.Cmd.PersistentFlags()
flags.StringVarP(&ctx.From, "from", "F", ctx.From, `specify the probe locations as a comma-separated list; you may use:
root.Cmd.PersistentFlags().BoolVarP(&ctx.CIMode, "ci", "C", ctx.CIMode, "disable real-time terminal updates and colors, suitable for CI and scripting (default false)")

// Measurement flags
measurementFlags := pflag.NewFlagSet("measurements", pflag.ExitOnError)
measurementFlags.StringVarP(&ctx.From, "from", "F", ctx.From, `specify the probe locations as a comma-separated list; you may use:
- names of continents, regions, countries, US states, cities, or networks
- [@1 | first, @2 ... @-2, @-1 | last | previous] to run with the probes from previous measurements in this session
- an ID of a previous measurement to run with its probes
`)
flags.IntVarP(&ctx.Limit, "limit", "L", ctx.Limit, "define the number of probes to use")
flags.BoolVarP(&ctx.ToJSON, "json", "J", ctx.ToJSON, "output results in JSON format (default false)")
flags.BoolVarP(&ctx.CIMode, "ci", "C", ctx.CIMode, "disable real-time terminal updates and colors, suitable for CI and scripting (default false)")
flags.BoolVar(&ctx.ToLatency, "latency", ctx.ToLatency, "output only the latency stats; applicable only to dns, http, and ping commands (default false)")
flags.BoolVar(&ctx.Share, "share", ctx.Share, "print a link at the end of the results to visualize them online (default false)")
flags.BoolVarP(&ctx.Ipv4, "ipv4", "4", ctx.Ipv4, "resolve names to IPv4 addresses")
flags.BoolVarP(&ctx.Ipv6, "ipv6", "6", ctx.Ipv6, "resolve names to IPv6 addresses")
measurementFlags.IntVarP(&ctx.Limit, "limit", "L", ctx.Limit, "define the number of probes to use")
measurementFlags.BoolVarP(&ctx.ToJSON, "json", "J", ctx.ToJSON, "output results in JSON format (default false)")
measurementFlags.BoolVar(&ctx.ToLatency, "latency", ctx.ToLatency, "output only the latency stats; applicable only to dns, http, and ping commands (default false)")
measurementFlags.BoolVar(&ctx.Share, "share", ctx.Share, "print a link at the end of the results to visualize them online (default false)")
measurementFlags.BoolVarP(&ctx.Ipv4, "ipv4", "4", ctx.Ipv4, "resolve names to IPv4 addresses")
measurementFlags.BoolVarP(&ctx.Ipv6, "ipv6", "6", ctx.Ipv6, "resolve names to IPv6 addresses")

root.Cmd.AddGroup(&cobra.Group{ID: "Measurements", Title: "Measurement Commands:"})

root.initDNS()
root.initHTTP()
root.initMTR()
root.initPing()
root.initTraceroute()
flagGroups["globalping"] = root.Cmd.Flags()
flagGroups["globalping dns"] = pflag.NewFlagSet("dns", pflag.ExitOnError)
flagGroups["globalping http"] = pflag.NewFlagSet("http", pflag.ExitOnError)
flagGroups["globalping mtr"] = pflag.NewFlagSet("mtr", pflag.ExitOnError)
flagGroups["globalping ping"] = pflag.NewFlagSet("ping", pflag.ExitOnError)
flagGroups["globalping traceroute"] = pflag.NewFlagSet("traceroute", pflag.ExitOnError)
flagGroups["measurements"] = measurementFlags

root.initDNS(measurementFlags, flagGroups["globalping dns"])
root.initHTTP(measurementFlags, flagGroups["globalping http"])
root.initMTR(measurementFlags, flagGroups["globalping mtr"])
root.initPing(measurementFlags, flagGroups["globalping ping"])
root.initTraceroute(measurementFlags, flagGroups["globalping traceroute"])
root.initInstallProbe()
root.initVersion()
root.initHistory()
Expand All @@ -168,6 +184,18 @@ func wrappedFlagUsages(cmd *pflag.FlagSet) string {
return cmd.FlagUsagesWrapped(width - 1)
}

func isMeasurementCommand(name string) bool {
return flagGroups[name] != nil
}

func localMeasurementFlags(name string) string {
return wrappedFlagUsages(flagGroups[name])
}

func globalMeasurementFlags() string {
return wrappedFlagUsages(flagGroups["measurements"])
}

// Identical to the default cobra usage template,
// but utilizes wrappedFlagUsages to ensure flag usages don't wrap around
var usageTemplate = `
Expand All @@ -192,13 +220,28 @@ Available Commands:{{range $cmds}}{{if (or .IsAvailableCommand (eq .Name "help")
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if not .AllChildCommandsHaveGroup}}
Additional Commands:{{range $cmds}}{{if (and (eq .GroupID "") (or .IsAvailableCommand (eq .Name "help")))}}
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{end}}{{end}}{{if (eq .CommandPath "globalping")}}
Global Measurement Flags:
{{globalMeasurementFlags | trimTrailingWhitespaces}}
Global Flags:
{{wrappedFlagUsages .LocalFlags | trimTrailingWhitespaces}}{{else}}{{if isMeasurementCommand .CommandPath}}
Flags:
{{localMeasurementFlags .CommandPath | trimTrailingWhitespaces}}
Global Measurement Flags:
{{globalMeasurementFlags | trimTrailingWhitespaces}}{{if .HasAvailableInheritedFlags}}
Global Flags:
{{wrappedFlagUsages .InheritedFlags | trimTrailingWhitespaces}}{{end}}{{else}}{{if .HasAvailableLocalFlags}}
Flags:
{{wrappedFlagUsages .LocalFlags | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}}
Global Flags:
{{wrappedFlagUsages .InheritedFlags | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}}
{{wrappedFlagUsages .InheritedFlags | trimTrailingWhitespaces}}{{end}}{{end}}{{end}}{{if .HasHelpSubCommands}}
Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}}
{{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}}
Expand Down
11 changes: 7 additions & 4 deletions cmd/traceroute.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import (
"github.com/jsdelivr/globalping-cli/globalping"
"github.com/jsdelivr/globalping-cli/view"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)

func (r *Root) initTraceroute() {
func (r *Root) initTraceroute(measurementFlags *pflag.FlagSet, localFlags *pflag.FlagSet) {
var tracerouteCmd = &cobra.Command{
RunE: r.RunTraceroute,
Use: "traceroute [target] from [location | measurement ID | @1 | first | @-1 | last | previous]",
Expand Down Expand Up @@ -49,9 +50,11 @@ Examples:
}

// traceroute specific flags
flags := tracerouteCmd.Flags()
flags.StringVar(&r.ctx.Protocol, "protocol", r.ctx.Protocol, "specify the protocol to use for tracerouting: ICMP, TCP, or UDP (default \"icmp\")")
flags.IntVar(&r.ctx.Port, "port", r.ctx.Port, "specify the port to use for the traceroute; only applicable for the TCP protocol (default 80)")
localFlags.BoolP("help", "h", false, "help for traceroute")
localFlags.StringVar(&r.ctx.Protocol, "protocol", r.ctx.Protocol, "specify the protocol to use for tracerouting: ICMP, TCP, or UDP (default \"icmp\")")
localFlags.IntVar(&r.ctx.Port, "port", r.ctx.Port, "specify the port to use for the traceroute; only applicable for the TCP protocol (default 80)")
tracerouteCmd.Flags().AddFlagSet(measurementFlags)
tracerouteCmd.Flags().AddFlagSet(localFlags)

r.Cmd.AddCommand(tracerouteCmd)
}
Expand Down

0 comments on commit ef7ddc9

Please sign in to comment.