From 61bea750928b291d5e7bca4ea87d64d503d8a7ac Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Thu, 23 Jan 2025 18:40:17 -0800 Subject: [PATCH] cmd/tailscale: fix, test some recent doc inconsistencies 3dabea0fc2c added some docs with inconsistent usage docs. This fixes them, and adds a test. It also adds some other tests and fixes other verb tense inconsistencies. Updates tailscale/corp#25278 Change-Id: I94c2a8940791bddd7c35c1c3d5fb791a317370c2 Signed-off-by: Brad Fitzpatrick --- cmd/tailscale/cli/cli_test.go | 43 ++++++++++++++++++++++++++++ cmd/tailscale/cli/configure_apple.go | 12 ++++---- cmd/tailscale/cli/debug.go | 8 +++--- cmd/tailscale/cli/dns.go | 2 +- cmd/tailscale/cli/exitnode.go | 2 +- cmd/tailscale/cli/metrics.go | 4 +-- cmd/tailscale/cli/network-lock.go | 19 ++++++------ cmd/tailscale/cli/switch.go | 2 +- cmd/tailscale/cli/syspolicy.go | 4 +-- 9 files changed, 68 insertions(+), 28 deletions(-) diff --git a/cmd/tailscale/cli/cli_test.go b/cmd/tailscale/cli/cli_test.go index dccb69876..6f43814e8 100644 --- a/cmd/tailscale/cli/cli_test.go +++ b/cmd/tailscale/cli/cli_test.go @@ -17,6 +17,7 @@ import ( qt "github.com/frankban/quicktest" "github.com/google/go-cmp/cmp" + "github.com/peterbourgon/ff/v3/ffcli" "tailscale.com/envknob" "tailscale.com/health/healthmsg" "tailscale.com/ipn" @@ -1525,3 +1526,45 @@ func TestHelpAlias(t *testing.T) { t.Fatalf("Run: %v", err) } } + +func TestDocs(t *testing.T) { + root := newRootCmd() + check := func(t *testing.T, c *ffcli.Command) { + shortVerb, _, ok := strings.Cut(c.ShortHelp, " ") + if !ok || shortVerb == "" { + t.Errorf("couldn't find verb+space in ShortHelp") + } else { + if strings.HasSuffix(shortVerb, ".") { + t.Errorf("ShortHelp shouldn't end in period; got %q", c.ShortHelp) + } + if b := shortVerb[0]; b >= 'a' && b <= 'z' { + t.Errorf("ShortHelp should start with upper-case letter; got %q", c.ShortHelp) + } + if strings.HasSuffix(shortVerb, "s") && shortVerb != "Does" { + t.Errorf("verb %q ending in 's' is unexpected, from %q", shortVerb, c.ShortHelp) + } + } + + name := t.Name() + wantPfx := strings.ReplaceAll(strings.TrimPrefix(name, "TestDocs/"), "/", " ") + switch name { + case "TestDocs/tailscale/completion/bash", + "TestDocs/tailscale/completion/zsh": + wantPfx = "" // special-case exceptions + } + if !strings.HasPrefix(c.ShortUsage, wantPfx) { + t.Errorf("ShortUsage should start with %q; got %q", wantPfx, c.ShortUsage) + } + } + + var walk func(t *testing.T, c *ffcli.Command) + walk = func(t *testing.T, c *ffcli.Command) { + t.Run(c.Name, func(t *testing.T) { + check(t, c) + for _, sub := range c.Subcommands { + walk(t, sub) + } + }) + } + walk(t, root) +} diff --git a/cmd/tailscale/cli/configure_apple.go b/cmd/tailscale/cli/configure_apple.go index edd9ec1ab..c0d99b90a 100644 --- a/cmd/tailscale/cli/configure_apple.go +++ b/cmd/tailscale/cli/configure_apple.go @@ -27,28 +27,28 @@ func sysExtCmd() *ffcli.Command { return &ffcli.Command{ Name: "sysext", ShortUsage: "tailscale configure sysext [activate|deactivate|status]", - ShortHelp: "Manages the system extension for macOS (Standalone variant)", + ShortHelp: "Manage the system extension for macOS (Standalone variant)", LongHelp: "The sysext set of commands provides a way to activate, deactivate, or manage the state of the Tailscale system extension on macOS. " + "This is only relevant if you are running the Standalone variant of the Tailscale client for macOS. " + "To access more detailed information about system extensions installed on this Mac, run 'systemextensionsctl list'.", Subcommands: []*ffcli.Command{ { Name: "activate", - ShortUsage: "tailscale sysext activate", + ShortUsage: "tailscale configure sysext activate", ShortHelp: "Register the Tailscale system extension with macOS.", LongHelp: "This command registers the Tailscale system extension with macOS. To run Tailscale, you'll also need to install the VPN configuration separately (run `tailscale configure vpn-config install`). After running this command, you need to approve the extension in System Settings > Login Items and Extensions > Network Extensions.", Exec: requiresStandalone, }, { Name: "deactivate", - ShortUsage: "tailscale sysext deactivate", + ShortUsage: "tailscale configure sysext deactivate", ShortHelp: "Deactivate the Tailscale system extension on macOS", LongHelp: "This command deactivates the Tailscale system extension on macOS. To completely remove Tailscale, you'll also need to delete the VPN configuration separately (use `tailscale configure vpn-config uninstall`).", Exec: requiresStandalone, }, { Name: "status", - ShortUsage: "tailscale sysext status", + ShortUsage: "tailscale configure sysext status", ShortHelp: "Print the enablement status of the Tailscale system extension", LongHelp: "This command prints the enablement status of the Tailscale system extension. If the extension is not enabled, run `tailscale sysext activate` to enable it.", Exec: requiresStandalone, @@ -69,14 +69,14 @@ func vpnConfigCmd() *ffcli.Command { Subcommands: []*ffcli.Command{ { Name: "install", - ShortUsage: "tailscale mac-vpn install", + ShortUsage: "tailscale configure mac-vpn install", ShortHelp: "Write the Tailscale VPN configuration to the macOS settings", LongHelp: "This command writes the Tailscale VPN configuration to the macOS settings. This is the entry that appears in System Settings > VPN. If you are running the Standalone variant of the client, you'll also need to install the system extension separately (run `tailscale configure sysext activate`).", Exec: requiresGUI, }, { Name: "uninstall", - ShortUsage: "tailscale mac-vpn uninstall", + ShortUsage: "tailscale configure mac-vpn uninstall", ShortHelp: "Delete the Tailscale VPN configuration from the macOS settings", LongHelp: "This command removes the Tailscale VPN configuration from the macOS settings. This is the entry that appears in System Settings > VPN. If you are running the Standalone variant of the client, you'll also need to deactivate the system extension separately (run `tailscale configure sysext deactivate`).", Exec: requiresGUI, diff --git a/cmd/tailscale/cli/debug.go b/cmd/tailscale/cli/debug.go index 04b343e76..f84dd25f0 100644 --- a/cmd/tailscale/cli/debug.go +++ b/cmd/tailscale/cli/debug.go @@ -289,7 +289,7 @@ var debugCmd = &ffcli.Command{ Name: "capture", ShortUsage: "tailscale debug capture", Exec: runCapture, - ShortHelp: "Streams pcaps for debugging", + ShortHelp: "Stream pcaps for debugging", FlagSet: (func() *flag.FlagSet { fs := newFlagSet("capture") fs.StringVar(&captureArgs.outFile, "o", "", "path to stream the pcap (or - for stdout), leave empty to start wireshark") @@ -315,13 +315,13 @@ var debugCmd = &ffcli.Command{ Name: "peer-endpoint-changes", ShortUsage: "tailscale debug peer-endpoint-changes ", Exec: runPeerEndpointChanges, - ShortHelp: "Prints debug information about a peer's endpoint changes", + ShortHelp: "Print debug information about a peer's endpoint changes", }, { Name: "dial-types", ShortUsage: "tailscale debug dial-types ", Exec: runDebugDialTypes, - ShortHelp: "Prints debug information about connecting to a given host or IP", + ShortHelp: "Print debug information about connecting to a given host or IP", FlagSet: (func() *flag.FlagSet { fs := newFlagSet("dial-types") fs.StringVar(&debugDialTypesArgs.network, "network", "tcp", `network type to dial ("tcp", "udp", etc.)`) @@ -342,7 +342,7 @@ var debugCmd = &ffcli.Command{ { Name: "go-buildinfo", ShortUsage: "tailscale debug go-buildinfo", - ShortHelp: "Prints Go's runtime/debug.BuildInfo", + ShortHelp: "Print Go's runtime/debug.BuildInfo", Exec: runGoBuildInfo, }, }, diff --git a/cmd/tailscale/cli/dns.go b/cmd/tailscale/cli/dns.go index 042ce1a94..402f0cedf 100644 --- a/cmd/tailscale/cli/dns.go +++ b/cmd/tailscale/cli/dns.go @@ -20,7 +20,7 @@ var dnsCmd = &ffcli.Command{ Name: "status", ShortUsage: "tailscale dns status [--all]", Exec: runDNSStatus, - ShortHelp: "Prints the current DNS status and configuration", + ShortHelp: "Print the current DNS status and configuration", LongHelp: dnsStatusLongHelp(), FlagSet: (func() *flag.FlagSet { fs := newFlagSet("status") diff --git a/cmd/tailscale/cli/exitnode.go b/cmd/tailscale/cli/exitnode.go index 941c6be8d..ad7a8ccee 100644 --- a/cmd/tailscale/cli/exitnode.go +++ b/cmd/tailscale/cli/exitnode.go @@ -41,7 +41,7 @@ func exitNodeCmd() *ffcli.Command { { Name: "suggest", ShortUsage: "tailscale exit-node suggest", - ShortHelp: "Suggests the best available exit node", + ShortHelp: "Suggest the best available exit node", Exec: runExitNodeSuggest, }}, (func() []*ffcli.Command { diff --git a/cmd/tailscale/cli/metrics.go b/cmd/tailscale/cli/metrics.go index d5fe9ad81..dbdedd5a6 100644 --- a/cmd/tailscale/cli/metrics.go +++ b/cmd/tailscale/cli/metrics.go @@ -33,13 +33,13 @@ https://tailscale.com/s/client-metrics Name: "print", ShortUsage: "tailscale metrics print", Exec: runMetricsPrint, - ShortHelp: "Prints current metric values in the Prometheus text exposition format", + ShortHelp: "Print current metric values in Prometheus text format", }, { Name: "write", ShortUsage: "tailscale metrics write ", Exec: runMetricsWrite, - ShortHelp: "Writes metric values to a file", + ShortHelp: "Write metric values to a file", LongHelp: strings.TrimSpace(` The 'tailscale metrics write' command writes metric values to a text file provided as its diff --git a/cmd/tailscale/cli/network-lock.go b/cmd/tailscale/cli/network-lock.go index 45f989f10..c77767074 100644 --- a/cmd/tailscale/cli/network-lock.go +++ b/cmd/tailscale/cli/network-lock.go @@ -191,8 +191,7 @@ var nlStatusArgs struct { var nlStatusCmd = &ffcli.Command{ Name: "status", ShortUsage: "tailscale lock status", - ShortHelp: "Outputs the state of tailnet lock", - LongHelp: "Outputs the state of tailnet lock", + ShortHelp: "Output the state of tailnet lock", Exec: runNetworkLockStatus, FlagSet: (func() *flag.FlagSet { fs := newFlagSet("lock status") @@ -293,8 +292,7 @@ func runNetworkLockStatus(ctx context.Context, args []string) error { var nlAddCmd = &ffcli.Command{ Name: "add", ShortUsage: "tailscale lock add ...", - ShortHelp: "Adds one or more trusted signing keys to tailnet lock", - LongHelp: "Adds one or more trusted signing keys to tailnet lock", + ShortHelp: "Add one or more trusted signing keys to tailnet lock", Exec: func(ctx context.Context, args []string) error { return runNetworkLockModify(ctx, args, nil) }, @@ -307,8 +305,7 @@ var nlRemoveArgs struct { var nlRemoveCmd = &ffcli.Command{ Name: "remove", ShortUsage: "tailscale lock remove [--re-sign=false] ...", - ShortHelp: "Removes one or more trusted signing keys from tailnet lock", - LongHelp: "Removes one or more trusted signing keys from tailnet lock", + ShortHelp: "Remove one or more trusted signing keys from tailnet lock", Exec: runNetworkLockRemove, FlagSet: (func() *flag.FlagSet { fs := newFlagSet("lock remove") @@ -448,7 +445,7 @@ func runNetworkLockModify(ctx context.Context, addArgs, removeArgs []string) err var nlSignCmd = &ffcli.Command{ Name: "sign", ShortUsage: "tailscale lock sign []\ntailscale lock sign ", - ShortHelp: "Signs a node or pre-approved auth key", + ShortHelp: "Sign a node or pre-approved auth key", LongHelp: `Either: - signs a node key and transmits the signature to the coordination server, or @@ -510,7 +507,7 @@ func runNetworkLockSign(ctx context.Context, args []string) error { var nlDisableCmd = &ffcli.Command{ Name: "disable", ShortUsage: "tailscale lock disable ", - ShortHelp: "Consumes a disablement secret to shut down tailnet lock for the tailnet", + ShortHelp: "Consume a disablement secret to shut down tailnet lock for the tailnet", LongHelp: strings.TrimSpace(` The 'tailscale lock disable' command uses the specified disablement @@ -539,7 +536,7 @@ func runNetworkLockDisable(ctx context.Context, args []string) error { var nlLocalDisableCmd = &ffcli.Command{ Name: "local-disable", ShortUsage: "tailscale lock local-disable", - ShortHelp: "Disables tailnet lock for this node only", + ShortHelp: "Disable tailnet lock for this node only", LongHelp: strings.TrimSpace(` The 'tailscale lock local-disable' command disables tailnet lock for only @@ -561,8 +558,8 @@ func runNetworkLockLocalDisable(ctx context.Context, args []string) error { var nlDisablementKDFCmd = &ffcli.Command{ Name: "disablement-kdf", ShortUsage: "tailscale lock disablement-kdf ", - ShortHelp: "Computes a disablement value from a disablement secret (advanced users only)", - LongHelp: "Computes a disablement value from a disablement secret (advanced users only)", + ShortHelp: "Compute a disablement value from a disablement secret (advanced users only)", + LongHelp: "Compute a disablement value from a disablement secret (advanced users only)", Exec: runNetworkLockDisablementKDF, } diff --git a/cmd/tailscale/cli/switch.go b/cmd/tailscale/cli/switch.go index 731492daa..af8b51326 100644 --- a/cmd/tailscale/cli/switch.go +++ b/cmd/tailscale/cli/switch.go @@ -20,7 +20,7 @@ import ( var switchCmd = &ffcli.Command{ Name: "switch", ShortUsage: "tailscale switch ", - ShortHelp: "Switches to a different Tailscale account", + ShortHelp: "Switch to a different Tailscale account", LongHelp: `"tailscale switch" switches between logged in accounts. You can use the ID that's returned from 'tailnet switch -list' to pick which profile you want to switch to. Alternatively, you diff --git a/cmd/tailscale/cli/syspolicy.go b/cmd/tailscale/cli/syspolicy.go index 0e903db39..a71952a9f 100644 --- a/cmd/tailscale/cli/syspolicy.go +++ b/cmd/tailscale/cli/syspolicy.go @@ -31,7 +31,7 @@ var syspolicyCmd = &ffcli.Command{ Name: "list", ShortUsage: "tailscale syspolicy list", Exec: runSysPolicyList, - ShortHelp: "Prints effective policy settings", + ShortHelp: "Print effective policy settings", LongHelp: "The 'tailscale syspolicy list' subcommand displays the effective policy settings and their sources (e.g., MDM or environment variables).", FlagSet: (func() *flag.FlagSet { fs := newFlagSet("syspolicy list") @@ -43,7 +43,7 @@ var syspolicyCmd = &ffcli.Command{ Name: "reload", ShortUsage: "tailscale syspolicy reload", Exec: runSysPolicyReload, - ShortHelp: "Forces a reload of policy settings, even if no changes are detected, and prints the result", + ShortHelp: "Force a reload of policy settings, even if no changes are detected, and prints the result", LongHelp: "The 'tailscale syspolicy reload' subcommand forces a reload of policy settings, even if no changes are detected, and prints the result.", FlagSet: (func() *flag.FlagSet { fs := newFlagSet("syspolicy reload")