|
|
|
|
@ -7,6 +7,7 @@ package cli
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
"encoding/json"
|
|
|
|
|
"errors"
|
|
|
|
|
"flag"
|
|
|
|
|
"fmt"
|
|
|
|
|
@ -217,8 +218,10 @@ func newRootCmd() *ffcli.Command {
|
|
|
|
|
return nil
|
|
|
|
|
})
|
|
|
|
|
rootfs.Lookup("socket").DefValue = localClient.Socket
|
|
|
|
|
jsonDocs := rootfs.Bool("json-docs", false, hidden+"print JSON-encoded docs for all subcommands and flags")
|
|
|
|
|
|
|
|
|
|
rootCmd := &ffcli.Command{
|
|
|
|
|
var rootCmd *ffcli.Command
|
|
|
|
|
rootCmd = &ffcli.Command{
|
|
|
|
|
Name: "tailscale",
|
|
|
|
|
ShortUsage: "tailscale [flags] <subcommand> [command flags]",
|
|
|
|
|
ShortHelp: "The easiest, most secure way to use WireGuard.",
|
|
|
|
|
@ -265,6 +268,9 @@ change in the future.
|
|
|
|
|
),
|
|
|
|
|
FlagSet: rootfs,
|
|
|
|
|
Exec: func(ctx context.Context, args []string) error {
|
|
|
|
|
if *jsonDocs {
|
|
|
|
|
return printJSONDocs(rootCmd)
|
|
|
|
|
}
|
|
|
|
|
if len(args) > 0 {
|
|
|
|
|
return fmt.Errorf("tailscale: unknown subcommand: %s", args[0])
|
|
|
|
|
}
|
|
|
|
|
@ -472,3 +478,54 @@ func colorableOutput() (w io.Writer, ok bool) {
|
|
|
|
|
}
|
|
|
|
|
return colorable.NewColorableStdout(), true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type commandDoc struct {
|
|
|
|
|
Name string
|
|
|
|
|
Desc string
|
|
|
|
|
Subcommands []commandDoc `json:",omitempty"`
|
|
|
|
|
Flags []flagDoc `json:",omitempty"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type flagDoc struct {
|
|
|
|
|
Name string
|
|
|
|
|
Desc string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func printJSONDocs(root *ffcli.Command) error {
|
|
|
|
|
docs := jsonDocsWalk(root)
|
|
|
|
|
return json.NewEncoder(os.Stdout).Encode(docs)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func jsonDocsWalk(cmd *ffcli.Command) *commandDoc {
|
|
|
|
|
res := &commandDoc{
|
|
|
|
|
Name: cmd.Name,
|
|
|
|
|
}
|
|
|
|
|
if cmd.LongHelp != "" {
|
|
|
|
|
res.Desc = cmd.LongHelp
|
|
|
|
|
} else if cmd.ShortHelp != "" {
|
|
|
|
|
res.Desc = cmd.ShortHelp
|
|
|
|
|
} else {
|
|
|
|
|
res.Desc = cmd.ShortUsage
|
|
|
|
|
}
|
|
|
|
|
if strings.HasPrefix(res.Desc, hidden) {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
if cmd.FlagSet != nil {
|
|
|
|
|
cmd.FlagSet.VisitAll(func(f *flag.Flag) {
|
|
|
|
|
if strings.HasPrefix(f.Usage, hidden) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
res.Flags = append(res.Flags, flagDoc{
|
|
|
|
|
Name: f.Name,
|
|
|
|
|
Desc: f.Usage,
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
for _, sub := range cmd.Subcommands {
|
|
|
|
|
subj := jsonDocsWalk(sub)
|
|
|
|
|
if subj != nil {
|
|
|
|
|
res.Subcommands = append(res.Subcommands, *subj)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return res
|
|
|
|
|
}
|
|
|
|
|
|