mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-09-03 10:17:46 +00:00
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:
@@ -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)
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user