From 00debaccdd7a2d4f762fe37408eec2100bbcd6e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kol=C3=A1rik?= Date: Wed, 25 Sep 2024 15:08:56 +0200 Subject: [PATCH 1/2] misc: improve flags help --- README.md | 10 ++++--- cmd/dns.go | 16 +++++----- cmd/http.go | 24 ++++++++------- cmd/mtr.go | 12 ++++---- cmd/ping.go | 10 ++++--- cmd/root.go | 75 +++++++++++++++++++++++++++++++++++++---------- cmd/traceroute.go | 10 ++++--- 7 files changed, 106 insertions(+), 51 deletions(-) diff --git a/README.md b/README.md index ef30ae0..2aab34c 100644 --- a/README.md +++ b/README.md @@ -96,9 +96,7 @@ 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 @@ -106,7 +104,6 @@ Flags: 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) @@ -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. ``` diff --git a/cmd/dns.go b/cmd/dns.go index 288c07b..6a58d62 100644 --- a/cmd/dns.go +++ b/cmd/dns.go @@ -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]", @@ -53,12 +54,13 @@ 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.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) } diff --git a/cmd/http.go b/cmd/http.go index 0930b8c..3d9935c 100644 --- a/cmd/http.go +++ b/cmd/http.go @@ -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]", @@ -68,16 +69,17 @@ 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.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) } diff --git a/cmd/mtr.go b/cmd/mtr.go index 8b1c4c8..b27f67f 100644 --- a/cmd/mtr.go +++ b/cmd/mtr.go @@ -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]", @@ -46,10 +47,11 @@ 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.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) } diff --git a/cmd/ping.go b/cmd/ping.go index e8b2819..67befe0 100644 --- a/cmd/ping.go +++ b/cmd/ping.go @@ -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]", @@ -51,9 +52,10 @@ 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.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) } diff --git a/cmd/root.go b/cmd/root.go index 76d245b..14a9a18 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -18,6 +18,8 @@ import ( "github.com/spf13/cobra" ) +var flagGroups = map[string]*pflag.FlagSet{} + type Root struct { printer *view.Printer ctx *view.Context @@ -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() @@ -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 = ` @@ -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}} diff --git a/cmd/traceroute.go b/cmd/traceroute.go index da70210..4e0da24 100644 --- a/cmd/traceroute.go +++ b/cmd/traceroute.go @@ -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]", @@ -49,9 +50,10 @@ 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.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) } From 9847cf2f1a04deade4e53e929d52c5e863e84ed4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kol=C3=A1rik?= Date: Wed, 25 Sep 2024 15:27:05 +0200 Subject: [PATCH 2/2] fix: restore --help listing --- cmd/dns.go | 1 + cmd/http.go | 1 + cmd/mtr.go | 1 + cmd/ping.go | 1 + cmd/traceroute.go | 1 + 5 files changed, 5 insertions(+) diff --git a/cmd/dns.go b/cmd/dns.go index 6a58d62..415b447 100644 --- a/cmd/dns.go +++ b/cmd/dns.go @@ -54,6 +54,7 @@ Examples: } // dns specific flags + 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)") diff --git a/cmd/http.go b/cmd/http.go index 3d9935c..681b5f6 100644 --- a/cmd/http.go +++ b/cmd/http.go @@ -69,6 +69,7 @@ Examples: } // http specific flags + 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)") diff --git a/cmd/mtr.go b/cmd/mtr.go index b27f67f..9314152 100644 --- a/cmd/mtr.go +++ b/cmd/mtr.go @@ -47,6 +47,7 @@ Examples: } // mtr specific flags + 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)") diff --git a/cmd/ping.go b/cmd/ping.go index 67befe0..258543f 100644 --- a/cmd/ping.go +++ b/cmd/ping.go @@ -52,6 +52,7 @@ Examples: } // ping specific flags + 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) diff --git a/cmd/traceroute.go b/cmd/traceroute.go index 4e0da24..8020f18 100644 --- a/cmd/traceroute.go +++ b/cmd/traceroute.go @@ -50,6 +50,7 @@ Examples: } // traceroute specific flags + 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)