Autogenerate md documentation for kubectl

This does away with the giant dump from cobra for kubectl and instead
generates md files which contain similar information, but one per verb.
This might work well as part of the cobra project, instead of doing it
in kube, but this gets us nice, linked, documentation right now.  If
people like it, I will try to get something similar into cobra.
This commit is contained in:
Eric Paris
2015-01-31 14:08:59 -05:00
parent 9e9fb9457f
commit a6beb2e7df
32 changed files with 1553 additions and 1120 deletions

View File

@@ -17,42 +17,156 @@ limitations under the License.
package main
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
func mergeFlags(old, newFlags *pflag.FlagSet) *pflag.FlagSet {
newFlags.VisitAll(func(f *pflag.Flag) {
if old.Lookup(f.Name) == nil {
old.AddFlag(f)
}
})
return old
}
func parentFlags(c *cobra.Command) *pflag.FlagSet {
if !c.HasParent() {
return pflag.NewFlagSet("empty", pflag.ExitOnError)
}
return mergeFlags(c.Parent().PersistentFlags(), parentFlags(c.Parent()))
}
func myFlags(c *cobra.Command) *pflag.FlagSet {
myFlags := c.Flags()
parentFlags := parentFlags(c)
if c.HasPersistentFlags() {
c.PersistentFlags().VisitAll(func(f *pflag.Flag) {
if c.Flags().Lookup(f.Name) == nil &&
parentFlags.Lookup(f.Name) == nil {
myFlags.AddFlag(f)
}
})
}
return myFlags
}
func printOptions(out *bytes.Buffer, command *cobra.Command, name string) {
flags := myFlags(command)
flags.SetOutput(out)
if command.Runnable() {
fmt.Fprintf(out, "%s\n\n", command.UseLine())
}
if flags.HasFlags() {
fmt.Fprintf(out, "### Options\n\n```\n")
flags.PrintDefaults()
fmt.Fprintf(out, "```\n\n")
}
parentFlags := parentFlags(command)
parentFlags.SetOutput(out)
if parentFlags.HasFlags() {
fmt.Fprintf(out, "### Options inherrited from parent commands\n\n```\n")
parentFlags.PrintDefaults()
fmt.Fprintf(out, "```\n\n")
}
}
func genMarkdown(command *cobra.Command, parent, docsDir string) {
dparent := strings.Replace(parent, " ", "-", -1)
name := command.Name()
dname := name
if len(parent) > 0 {
dname = dparent + "-" + name
name = parent + " " + name
}
out := new(bytes.Buffer)
short := command.Short
long := command.Long
if len(long) == 0 {
long = short
}
fmt.Fprintf(out, "## %s\n\n", name)
fmt.Fprintf(out, "%s\n\n", short)
fmt.Fprintf(out, "### Synopsis\n\n")
fmt.Fprintf(out, "%s\n\n", long)
printOptions(out, command, name)
if len(command.Commands()) > 0 || len(parent) > 0 {
fmt.Fprintf(out, "### SEE ALSO\n")
if len(parent) > 0 {
link := dparent + ".md"
fmt.Fprintf(out, "* [%s](%s)\n", dparent, link)
}
for _, c := range command.Commands() {
child := dname + "-" + c.Name()
link := child + ".md"
fmt.Fprintf(out, "* [%s](%s)\n", child, link)
genMarkdown(c, name, docsDir)
}
fmt.Fprintf(out, "\n")
}
filename := docsDir + dname + ".md"
outFile, err := os.Create(filename)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
defer outFile.Close()
_, err = outFile.Write(out.Bytes())
if err != nil {
fmt.Println(err)
os.Exit(1)
}
}
func main() {
out := os.Stdout
// use os.Args instead of "flags" because "flags" will mess up the man pages!
docsDir := "docs/man/man1/"
if len(os.Args) == 2 {
docsDir = os.Args[1]
} else if len(os.Args) > 2 {
fmt.Fprintf(os.Stderr, "usage: %s [output directory]\n", os.Args[0])
return
}
docsDir, err := filepath.Abs(docsDir)
if err != nil {
fmt.Fprintf(os.Stderr, err.Error())
return
}
stat, err := os.Stat(docsDir)
if err != nil {
fmt.Fprintf(os.Stderr, "output directory %s does not exist\n", docsDir)
return
}
if !stat.IsDir() {
fmt.Fprintf(os.Stderr, "output directory %s is not a directory\n", docsDir)
return
}
docsDir = docsDir + "/"
// Set environment variables used by kubectl so the output is consistent,
// regardless of where we run.
os.Setenv("HOME", "/home/username")
kubectl := cmd.NewFactory(nil).NewKubectlCommand(out)
fmt.Fprintf(out, "## %s\n\n", kubectl.Name())
fmt.Fprintf(out, "%s\n\n", kubectl.Short)
fmt.Fprintln(out, "### Commands\n")
kubectl := cmd.NewFactory(nil).NewKubectlCommand(ioutil.Discard)
genMarkdown(kubectl, "", docsDir)
for _, c := range kubectl.Commands() {
genMarkdown(c, nil, out)
}
}
func genMarkdown(command, parent *cobra.Command, out io.Writer) {
name := command.Name()
if parent != nil {
name = fmt.Sprintf("%s %s", parent.Name(), name)
}
fmt.Fprintf(out, "#### %s\n", name)
desc := command.Long
if len(desc) == 0 {
desc = command.Short
}
fmt.Fprintf(out, "%s\n\n", desc)
usage := command.UsageString()
fmt.Fprintf(out, "Usage:\n```\n%s\n```\n\n", usage[9:len(usage)-1])
for _, c := range command.Commands() {
genMarkdown(c, command, out)
genMarkdown(c, "kubectl", docsDir)
}
}