Skip to content

Commit

Permalink
feat(doc): Update program to generate markdown docs for the CLI (#647)
Browse files Browse the repository at this point in the history
# Description
This brings support for using the markdown files in our community pages.
The new code assumes the doc renderer will be hugo: this removes quite a
bit of complexity.
Users who just want markdown can use this code as an inspiration.

## Checklist
(For exoscale contributors)

* [ ] Changelog updated (under *Unreleased* block)
* [ ] Testing

## Testing

<!--
Describe the tests you did
-->
  • Loading branch information
wdullaer authored Nov 4, 2024
1 parent ad822e9 commit 550b40f
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 83 deletions.
Empty file removed docs/.gitkeep
Empty file.
9 changes: 0 additions & 9 deletions docs/404.html

This file was deleted.

3 changes: 0 additions & 3 deletions docs/404.md

This file was deleted.

165 changes: 94 additions & 71 deletions docs/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import (
"io"
"log"
"os"
"path"
"path/filepath"
"runtime/debug"
"sort"
"strings"
"time"
Expand All @@ -20,21 +20,10 @@ import (
"github.com/exoscale/cli/cmd"
)

const frontmatter = `---
date: %s
title: %q
slug: %q
url: %s
description: %q
type: %s
---
`

func main() {

var flagError pflag.ErrorHandling
docCmd := pflag.NewFlagSet("", flagError)
var isHugo = docCmd.BoolP("is-hugo", "", true, "set false if you dont want to generate fot hugo (https://gohugo.io/)")
var manPage = docCmd.BoolP("man-page", "", false, "Generate exo manual pages")
var filesDir = docCmd.StringP("doc-path", "", "./website/content", "Path directory where you want generate doc files")
var help = docCmd.BoolP("help", "h", false, "Help about any command")
Expand Down Expand Up @@ -64,25 +53,7 @@ func main() {
return
}

filePrepender := func(filename string, cmd *cobra.Command) string {
now := time.Now().Format(time.RFC3339)
name := filepath.Base(filename)
base := strings.TrimSuffix(name, path.Ext(name))
url := fmt.Sprintf("/%s/", strings.ToLower(base))
slug := strings.ReplaceAll(base, "_", " ")
typeExo := `"command"`
if strings.Count(base, "_") > 1 {
typeExo = `"subcommand"`
}
return fmt.Sprintf(frontmatter, now, slug, base, url, cmd.Short, typeExo)
}

linkHandler := func(name string) string {
base := strings.TrimSuffix(name, path.Ext(name))
return fmt.Sprintf("/cli/%s/", strings.ToLower(base))
}

if err := exoGenMarkdownTreeCustom(cmd.RootCmd, *filesDir, filePrepender, linkHandler, *isHugo); err != nil {
if err := exoGenMarkdownTreeCustom(cmd.RootCmd, *filesDir); err != nil {
log.Fatal(err)
}

Expand All @@ -97,18 +68,25 @@ func main() {
// beginning cobra/doc custom src code
//

func exoGenMarkdownTreeCustom(cmd *cobra.Command, dir string, filePrepender func(string, *cobra.Command) string, linkHandler func(string) string, isHugo bool) error {
func exoGenMarkdownTreeCustom(cmd *cobra.Command, dir string) error {
for _, c := range cmd.Commands() {
if !c.IsAvailableCommand() || c.IsAdditionalHelpTopicCommand() {
continue
}
if err := exoGenMarkdownTreeCustom(c, dir, filePrepender, linkHandler, isHugo); err != nil {
if err := os.MkdirAll(filepath.Join(dir, cmd.Name()), 0750); err != nil {
return err
}
if err := exoGenMarkdownTreeCustom(c, filepath.Join(dir, cmd.Name())); err != nil {
return err
}
}

basename := strings.ReplaceAll(cmd.CommandPath(), " ", "_") + ".md"
filename := filepath.Join(dir, basename)
filename := ""
if cmd.HasSubCommands() {
filename = filepath.Join(dir, cmd.Name(), "_index.md")
} else {
filename = filepath.Join(dir, cmd.Name()+".md")
}
f, err := os.Create(filename)
if err != nil {
return err
Expand All @@ -117,11 +95,7 @@ func exoGenMarkdownTreeCustom(cmd *cobra.Command, dir string, filePrepender func

fmt.Printf("exo: create file : %s\n", filename)

if _, err := io.WriteString(f, filePrepender(filename, cmd)); err != nil {
return err
}

return exoGenMarkdownCustom(cmd, f, linkHandler, isHugo)
return exoGenMarkdownCustom(cmd, f)
}

type byName []*cobra.Command
Expand All @@ -130,31 +104,54 @@ func (s byName) Len() int { return len(s) }
func (s byName) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s byName) Less(i, j int) bool { return s[i].Name() < s[j].Name() }

func exoGenMarkdownCustom(cmd *cobra.Command, w io.Writer, linkHandler func(string) string, ishugo bool) error {
// readCommitTime will obtain the commit time out the build info, if any
// If the commit time cannot be found, will return the current time in the same format
func readCommitTime() string {
if buildInfo, ok := debug.ReadBuildInfo(); ok {
for _, setting := range buildInfo.Settings {
if setting.Key == "vcs.time" {
return setting.Value
}
}
}
return time.Now().Format(time.RFC3339)
}

var date string = readCommitTime()

func exoGenMarkdownCustom(cmd *cobra.Command, w io.Writer) error {
cmd.InitDefaultHelpCmd()
cmd.InitDefaultHelpFlag()

buf := new(bytes.Buffer)
name := cmd.CommandPath()

if !ishugo {
short := cmd.Short
buf.WriteString("## " + name + "\n\n")
buf.WriteString(short + "\n\n")
}

if ishugo {
buf.WriteString("<!--more-->\n\n")
linkTitle := cmd.Name()
title := name
if !cmd.HasParent() {
linkTitle = "Command Reference"
title = "Command Reference"
}
fmt.Fprintln(buf, "---")
fmt.Fprintln(buf, "date:", date)
fmt.Fprintln(buf, "linkTitle:", linkTitle)
fmt.Fprintln(buf, "title:", title)
fmt.Fprintln(buf, "description:", cmd.Short)
fmt.Fprintln(buf, "---")

long := cmd.Long
if len(long) != 0 {
buf.WriteString("### Synopsis\n\n")
buf.WriteString(long + "\n\n")
fmt.Fprintln(buf, "### Description")
fmt.Fprintln(buf)
fmt.Fprintln(buf, long)
fmt.Fprintln(buf)
}

if cmd.Runnable() {
buf.WriteString(fmt.Sprintf("```\n%s\n```\n\n", cmd.UseLine()))
fmt.Fprintln(buf, "```")
fmt.Fprintln(buf, cmd.UseLine())
fmt.Fprintln(buf, "```")
fmt.Fprintln(buf)
} else {
splitedLine := strings.Split(cmd.UseLine(), " ")

Expand All @@ -164,25 +161,35 @@ func exoGenMarkdownCustom(cmd *cobra.Command, w io.Writer, linkHandler func(stri

line := strings.Join(splitedLine, " ")

buf.WriteString(fmt.Sprintf("```\n%s\n```\n\n", line))
fmt.Fprintln(buf, "```")
fmt.Fprintln(buf, line)
fmt.Fprintln(buf, "```")
fmt.Fprintln(buf)
}

if len(cmd.Example) > 0 {
buf.WriteString("### Examples\n\n")
buf.WriteString(fmt.Sprintf("```\n%s\n```\n\n", cmd.Example))
fmt.Fprintln(buf, "### Examples")
fmt.Fprintln(buf)
fmt.Fprintln(buf, "```")
fmt.Fprintln(buf, cmd.Example)
fmt.Fprintln(buf, "```")
fmt.Fprintln(buf)
}

if err := printOptions(buf, cmd); err != nil {
return err
}
if hasSeeAlso(cmd) {
buf.WriteString("### SEE ALSO\n\n")
fmt.Fprintln(buf, "### Related Commands")
fmt.Fprintln(buf)
if cmd.HasParent() {
parent := cmd.Parent()
pname := parent.CommandPath()
link := pname + ".md"
link = strings.ReplaceAll(link, " ", "_")
buf.WriteString(fmt.Sprintf("* [%s](%s)\t - %s\n", pname, linkHandler(link), parent.Short))
pname := parent.Name()
link := "../"
if !cmd.HasSubCommands() {
link += pname
}
fmt.Fprintln(buf, renderRelatedLink(pname, link, parent.Short))
}

children := cmd.Commands()
Expand All @@ -193,43 +200,59 @@ func exoGenMarkdownCustom(cmd *cobra.Command, w io.Writer, linkHandler func(stri
continue
}
cname := name + " " + child.Name()
link := cname + ".md"
link = strings.ReplaceAll(link, " ", "_")
buf.WriteString(fmt.Sprintf("* [%s](%s)\t - %s\n\n", cname, linkHandler(link), child.Short))
link := child.Name()
fmt.Fprintln(buf, renderRelatedLink(cname, link, child.Short))
fmt.Fprintln(buf)
}
buf.WriteString("\n")
fmt.Fprintln(buf)
}

_, err := buf.WriteTo(w)
return err
}

func writeFlag(buffer *bytes.Buffer, flag *pflag.Flag) {
func renderRelatedLink(name, link, short string) string {
return fmt.Sprintf("* [%s]({{< ref \"%s\">}})\t - %s", name, link, short)
}

func tableEscape(s string) string {
return strings.ReplaceAll(html.EscapeString(s), "|", `\|`)
}

func writeFlag(buf *bytes.Buffer, flag *pflag.Flag) {
usage := strings.Replace(flag.Usage, "[required]", "(**required**)", 1)
if flag.Shorthand != "" {
buffer.WriteString(fmt.Sprintf("`--%s, -%s` - %s\n", flag.Name, flag.Shorthand, html.EscapeString(usage)))
fmt.Fprintf(buf, "|`--%s, -%s` | %s |\n", flag.Name, flag.Shorthand, tableEscape(usage))
return
}
buffer.WriteString(fmt.Sprintf("`--%s` - %s\n", flag.Name, html.EscapeString(usage)))
fmt.Fprintf(buf, "|`--%s` | %s |\n", flag.Name, tableEscape(usage))
}

func printOptions(buf *bytes.Buffer, cmd *cobra.Command) error {
flags := cmd.NonInheritedFlags()
if flags.HasAvailableFlags() {
buf.WriteString("### Options\n\n")
fmt.Fprintln(buf, "### Options")
fmt.Fprintln(buf)
fmt.Fprintln(buf, "| Option | Description |")
fmt.Fprintln(buf, "|---------|------------|")
flags.VisitAll(func(flag *pflag.Flag) {
writeFlag(buf, flag)
})
buf.WriteString("\n\n")
fmt.Fprintln(buf)
fmt.Fprintln(buf)
}

parentFlags := cmd.InheritedFlags()
if parentFlags.HasAvailableFlags() {
buf.WriteString("### Options inherited from parent commands\n\n")
fmt.Fprintln(buf, "### Options inherited from parent commands")
fmt.Fprintln(buf)
fmt.Fprintln(buf, "| Option | Description |")
fmt.Fprintln(buf, "|---------|------------|")
parentFlags.VisitAll(func(flag *pflag.Flag) {
writeFlag(buf, flag)
})
buf.WriteString("\n\n")
fmt.Fprintln(buf)
fmt.Fprintln(buf)
}
return nil
}
Expand Down

0 comments on commit 550b40f

Please sign in to comment.