feat: add helper to auto-generate cli docs (#40)

This commit is contained in:
Robert Kaussow 2023-12-21 09:13:00 +01:00 committed by GitHub
parent 6d2c362281
commit 4d62198740
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 194 additions and 0 deletions

99
docs/docs.go Normal file
View File

@ -0,0 +1,99 @@
package docs
import (
"bytes"
"embed"
"html/template"
"sort"
"strings"
"github.com/urfave/cli/v2"
)
type PluginArg struct {
Name string
EnvVars []string
Description string
Default string
}
type CliTemplate struct {
Name string
Version string
Description string
Usage string
UsageText string
GlobalArgs []*PluginArg
}
//go:embed templates
var templateFs embed.FS
// ToMarkdown creates a markdown string for the `*App`
// The function errors if either parsing or writing of the string fails.
func ToMarkdown(app *cli.App) (string, error) {
var w bytes.Buffer
tpls, err := template.New("cli").ParseFS(templateFs, "**/*.tmpl")
if err != nil {
return "", err
}
if err := tpls.ExecuteTemplate(&w, "markdown.md.tmpl", GetTemplateData(app)); err != nil {
return "", err
}
return w.String(), nil
}
func GetTemplateData(app *cli.App) *CliTemplate {
return &CliTemplate{
Name: app.Name,
Version: app.Version,
Description: prepareMultilineString(app.Description),
Usage: prepareMultilineString(app.Usage),
UsageText: prepareMultilineString(app.UsageText),
GlobalArgs: prepareArgsWithValues(app.VisibleFlags()),
}
}
func prepareMultilineString(s string) string {
return strings.TrimRight(
strings.TrimSpace(
strings.ReplaceAll(s, "\n", " "),
),
".\r\n\t",
)
}
func prepareArgsWithValues(flags []cli.Flag) []*PluginArg {
return parseFlags(flags)
}
func parseFlags(flags []cli.Flag) []*PluginArg {
args := make([]*PluginArg, 0)
for _, f := range flags {
flag, ok := f.(cli.DocGenerationFlag)
if !ok {
continue
}
modArg := &PluginArg{}
name := flag.GetEnvVars()[0]
name = strings.TrimPrefix(name, "PLUGIN_")
modArg.Name = strings.ToLower(strings.TrimSpace(name))
modArg.Description = flag.GetUsage()
modArg.Default = flag.GetDefaultText()
args = append(args, modArg)
}
sort.SliceStable(args, func(i, j int) bool {
return args[i].Name < args[j].Name
})
return args
}

67
docs/docs_test.go Normal file
View File

@ -0,0 +1,67 @@
package docs
import (
"bytes"
"os"
"testing"
"github.com/urfave/cli/v2"
)
func testApp() *cli.App {
app := &cli.App{
Name: "test",
Description: "test description",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "dummy-flag",
Usage: "dummy flag desc",
EnvVars: []string{"PLUGIN_DUMMY_FLAG"},
Value: "test",
},
&cli.StringFlag{
Name: "simpe-flag",
EnvVars: []string{"PLUGIN_X_SIMPLE_FLAG"},
},
&cli.StringFlag{
Name: "other.flag",
Usage: "other flag with desc",
EnvVars: []string{"PLUGIN_Z_OTHER_FLAG"},
},
},
}
return app
}
func testFileContent(t *testing.T, file string) string {
t.Helper()
data, err := os.ReadFile(file)
if err != nil {
t.Error(err)
}
data = bytes.ReplaceAll(data, []byte("\r\n"), []byte("\n"))
return string(data)
}
func TestToMarkdownFull(t *testing.T) {
tests := []struct {
name string
app *cli.App
want string
}{
{"normal branch", testApp(), "testdata/expected-doc-full.md"},
}
for _, tt := range tests {
want := testFileContent(t, tt.want)
t.Run(tt.name, func(t *testing.T) {
if got, _ := ToMarkdown(tt.app); got != want {
t.Errorf("got = %v, want %v", got, want)
}
})
}
}

15
docs/templates/markdown.md.tmpl vendored Normal file
View File

@ -0,0 +1,15 @@
{{ with .Name }}# {{ . }}{{ end }}
{{- with .Description }}
{{ . }}
{{- end }}
{{- if .GlobalArgs }}
## Parameters
{{ range $v := .GlobalArgs }}
**_{{ $v.Name }}_**{{ with $v.Default }} (defaut: {{ . }}){{ end }}{{ if $v.Description }}{{ "\\" }}{{ end }}
{{- with $v.Description }}
&emsp;{{ . }}
{{- end }}
{{ end -}}
{{ end -}}

13
docs/testdata/expected-doc-full.md vendored Normal file
View File

@ -0,0 +1,13 @@
# test
test description
## Parameters
**_dummy_flag_** (defaut: &#34;test&#34;)\
&emsp;dummy flag desc
**_x_simple_flag_**
**_z_other_flag_**\
&emsp;other flag with desc