[#1396] cmd: Allow to autogenerate documentation for all commands
Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
This commit is contained in:
parent
c6325fdc91
commit
4761857fb3
6 changed files with 152 additions and 1 deletions
|
@ -5,6 +5,7 @@ Changelog for NeoFS Node
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
- Config examples for Inner ring application (#1358)
|
- Config examples for Inner ring application (#1358)
|
||||||
|
- Command for documentation generation for `neofs-cli`, `neofs-adm` and `neofs-lens` (#1396)
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Do not ask for contract wallet password twice (#1346)
|
- Do not ask for contract wallet password twice (#1346)
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"github.com/nspcc-dev/neofs-node/cmd/neofs-adm/internal/modules/storagecfg"
|
"github.com/nspcc-dev/neofs-node/cmd/neofs-adm/internal/modules/storagecfg"
|
||||||
"github.com/nspcc-dev/neofs-node/misc"
|
"github.com/nspcc-dev/neofs-node/misc"
|
||||||
"github.com/nspcc-dev/neofs-node/pkg/util/autocomplete"
|
"github.com/nspcc-dev/neofs-node/pkg/util/autocomplete"
|
||||||
|
"github.com/nspcc-dev/neofs-node/pkg/util/gendoc"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
@ -42,6 +43,7 @@ func init() {
|
||||||
rootCmd.AddCommand(storagecfg.RootCmd)
|
rootCmd.AddCommand(storagecfg.RootCmd)
|
||||||
|
|
||||||
rootCmd.AddCommand(autocomplete.Command("neofs-adm"))
|
rootCmd.AddCommand(autocomplete.Command("neofs-adm"))
|
||||||
|
rootCmd.AddCommand(gendoc.Command(rootCmd))
|
||||||
}
|
}
|
||||||
|
|
||||||
func Execute() error {
|
func Execute() error {
|
||||||
|
|
|
@ -18,6 +18,7 @@ import (
|
||||||
bearerCli "github.com/nspcc-dev/neofs-node/cmd/neofs-cli/modules/bearer"
|
bearerCli "github.com/nspcc-dev/neofs-node/cmd/neofs-cli/modules/bearer"
|
||||||
sessionCli "github.com/nspcc-dev/neofs-node/cmd/neofs-cli/modules/session"
|
sessionCli "github.com/nspcc-dev/neofs-node/cmd/neofs-cli/modules/session"
|
||||||
"github.com/nspcc-dev/neofs-node/misc"
|
"github.com/nspcc-dev/neofs-node/misc"
|
||||||
|
"github.com/nspcc-dev/neofs-node/pkg/util/gendoc"
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/bearer"
|
"github.com/nspcc-dev/neofs-sdk-go/bearer"
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/client"
|
"github.com/nspcc-dev/neofs-sdk-go/client"
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/owner"
|
"github.com/nspcc-dev/neofs-sdk-go/owner"
|
||||||
|
@ -93,6 +94,7 @@ func init() {
|
||||||
rootCmd.AddCommand(bearerCli.Cmd)
|
rootCmd.AddCommand(bearerCli.Cmd)
|
||||||
rootCmd.AddCommand(sessionCli.Cmd)
|
rootCmd.AddCommand(sessionCli.Cmd)
|
||||||
rootCmd.AddCommand(accountingCli.Cmd)
|
rootCmd.AddCommand(accountingCli.Cmd)
|
||||||
|
rootCmd.AddCommand(gendoc.Command(rootCmd))
|
||||||
}
|
}
|
||||||
|
|
||||||
func entryPoint(cmd *cobra.Command, _ []string) {
|
func entryPoint(cmd *cobra.Command, _ []string) {
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"github.com/nspcc-dev/neofs-node/cmd/neofs-lens/internal/commands/inspect"
|
"github.com/nspcc-dev/neofs-node/cmd/neofs-lens/internal/commands/inspect"
|
||||||
cmdlist "github.com/nspcc-dev/neofs-node/cmd/neofs-lens/internal/commands/list"
|
cmdlist "github.com/nspcc-dev/neofs-node/cmd/neofs-lens/internal/commands/list"
|
||||||
"github.com/nspcc-dev/neofs-node/misc"
|
"github.com/nspcc-dev/neofs-node/misc"
|
||||||
|
"github.com/nspcc-dev/neofs-node/pkg/util/gendoc"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -36,6 +37,7 @@ func init() {
|
||||||
command.AddCommand(
|
command.AddCommand(
|
||||||
cmdlist.Command,
|
cmdlist.Command,
|
||||||
inspect.Command,
|
inspect.Command,
|
||||||
|
gendoc.Command(command),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -26,6 +26,7 @@ require (
|
||||||
github.com/prometheus/client_golang v1.11.0
|
github.com/prometheus/client_golang v1.11.0
|
||||||
github.com/spf13/cast v1.3.1
|
github.com/spf13/cast v1.3.1
|
||||||
github.com/spf13/cobra v1.1.3
|
github.com/spf13/cobra v1.1.3
|
||||||
|
github.com/spf13/pflag v1.0.5
|
||||||
github.com/spf13/viper v1.8.1
|
github.com/spf13/viper v1.8.1
|
||||||
github.com/stretchr/testify v1.7.0
|
github.com/stretchr/testify v1.7.0
|
||||||
go.etcd.io/bbolt v1.3.6
|
go.etcd.io/bbolt v1.3.6
|
||||||
|
@ -80,7 +81,6 @@ require (
|
||||||
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
||||||
github.com/spf13/afero v1.6.0 // indirect
|
github.com/spf13/afero v1.6.0 // indirect
|
||||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
|
||||||
github.com/subosito/gotenv v1.2.0 // indirect
|
github.com/subosito/gotenv v1.2.0 // indirect
|
||||||
github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954 // indirect
|
github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954 // indirect
|
||||||
github.com/twmb/murmur3 v1.1.5 // indirect
|
github.com/twmb/murmur3 v1.1.5 // indirect
|
||||||
|
|
144
pkg/util/gendoc/gendoc.go
Normal file
144
pkg/util/gendoc/gendoc.go
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
package gendoc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"text/template"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/cobra/doc"
|
||||||
|
"github.com/spf13/pflag"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
gendocTypeFlag = "type"
|
||||||
|
|
||||||
|
gendocMarkdown = "md"
|
||||||
|
gendocMan = "man"
|
||||||
|
|
||||||
|
depthFlag = "depth"
|
||||||
|
extensionFlag = "extension"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Command returns command which generates user documentation for the argument.
|
||||||
|
func Command(rootCmd *cobra.Command) *cobra.Command {
|
||||||
|
gendocCmd := &cobra.Command{
|
||||||
|
Use: "gendoc <dir>",
|
||||||
|
Short: "Generate documentation for this command.",
|
||||||
|
Long: `Generate documentation for this command. If the template is not provided,
|
||||||
|
builtin cobra generator is used and each subcommand is placed in
|
||||||
|
a separate file in the same directory.
|
||||||
|
|
||||||
|
The last optional argument specifies the template to use with text/template.
|
||||||
|
In this case there is a number of helper functions which can be used:
|
||||||
|
replace STR FROM TO -- same as strings.ReplaceAll
|
||||||
|
join ARRAY SEPARATOR -- same as strings.Join
|
||||||
|
split STR SEPARATOR -- same as strings.Split
|
||||||
|
fullUse CMD -- slice of all command names starting from the parent
|
||||||
|
listFlags CMD -- list of command flags
|
||||||
|
`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
if len(args) == 0 {
|
||||||
|
_ = cmd.Usage()
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := os.MkdirAll(args[0], os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("can't create directory: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(args) == 2 {
|
||||||
|
data, err := ioutil.ReadFile(args[1])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("can't read the template '%s': %w", args[1], err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return generateTemplate(cmd, rootCmd, args[0], data)
|
||||||
|
}
|
||||||
|
|
||||||
|
typ, _ := cmd.Flags().GetString(gendocTypeFlag)
|
||||||
|
switch typ {
|
||||||
|
case gendocMarkdown:
|
||||||
|
return doc.GenMarkdownTree(rootCmd, args[0])
|
||||||
|
case gendocMan:
|
||||||
|
hdr := &doc.GenManHeader{
|
||||||
|
Section: "1",
|
||||||
|
Source: "NSPCC & Morphbits",
|
||||||
|
}
|
||||||
|
return doc.GenManTree(rootCmd, hdr, args[0])
|
||||||
|
default:
|
||||||
|
return errors.New("type must be 'md' or 'man'")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
ff := gendocCmd.Flags()
|
||||||
|
ff.StringP(gendocTypeFlag, "t", gendocMarkdown, "type for the documentation ('md' or 'man')")
|
||||||
|
ff.Int(depthFlag, 1, "if template is specified, unify all commands starting from depth in a single file. Default: 1.")
|
||||||
|
ff.StringP(extensionFlag, "e", "", "if the template is specified, string to append to the output file names")
|
||||||
|
return gendocCmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateTemplate(cmd *cobra.Command, rootCmd *cobra.Command, outDir string, tmpl []byte) error {
|
||||||
|
depth, _ := cmd.Flags().GetInt(depthFlag)
|
||||||
|
ext, _ := cmd.Flags().GetString(extensionFlag)
|
||||||
|
|
||||||
|
tm := template.New("doc")
|
||||||
|
tm.Funcs(template.FuncMap{
|
||||||
|
"replace": strings.ReplaceAll,
|
||||||
|
"split": strings.Split,
|
||||||
|
"join": strings.Join,
|
||||||
|
"fullUse": fullUse,
|
||||||
|
"listFlags": listFlags,
|
||||||
|
})
|
||||||
|
|
||||||
|
tm, err := tm.Parse(string(tmpl))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return visit(rootCmd, outDir, ext, depth, tm)
|
||||||
|
}
|
||||||
|
|
||||||
|
func visit(rootCmd *cobra.Command, outDir string, ext string, depth int, tm *template.Template) error {
|
||||||
|
if depth == 0 {
|
||||||
|
name := strings.Join(fullUse(rootCmd), "-")
|
||||||
|
name = strings.TrimSpace(name)
|
||||||
|
name = strings.ReplaceAll(name, " ", "-")
|
||||||
|
name = filepath.Join(outDir, name) + ext
|
||||||
|
f, err := os.Create(name)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("can't create file '%s': %w", name, err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
return tm.Execute(f, rootCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range rootCmd.Commands() {
|
||||||
|
err := visit(c, outDir, ext, depth-1, tm)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func fullUse(c *cobra.Command) []string {
|
||||||
|
if c == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return append(fullUse(c.Parent()), c.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
func listFlags(c *cobra.Command) []*pflag.Flag {
|
||||||
|
var res []*pflag.Flag
|
||||||
|
c.Flags().VisitAll(func(f *pflag.Flag) {
|
||||||
|
res = append(res, f)
|
||||||
|
})
|
||||||
|
return res
|
||||||
|
}
|
Loading…
Reference in a new issue