vitess.io/vitess@v0.16.2/go/cmd/internal/docgen/docgen.go (about) 1 /* 2 Copyright 2022 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 // Package docgen provides common logic for generating markdown doctrees from 18 // a root cobra.Command for the vitessio/website repository. 19 // 20 // Example usage: 21 // 22 // package main 23 // 24 // import ( 25 // "github.com/spf13/cobra" 26 // 27 // "vitess.io/vitess/go/cmd/internal/docgen" 28 // vtctldclient "vitess.io/vitess/go/cmd/vtctldclient/command" 29 // ) 30 // 31 // func main() { 32 // cmd := &cobra.Command{ 33 // RunE: func(cmd *cobra.Command, args []string) error { 34 // dir := cmd.Flags().Arg(0) 35 // return docgen.GenerateMarkdownTree(vtctldclient.Root, dir) 36 // } 37 // Args: cobra.ExactArgs(1), 38 // } 39 // 40 // cmd.Execute() 41 // } 42 package docgen 43 44 import ( 45 "errors" 46 "fmt" 47 "io/fs" 48 "os" 49 "path/filepath" 50 "strings" 51 52 "github.com/spf13/cobra" 53 "github.com/spf13/cobra/doc" 54 ) 55 56 // GenerateMarkdownTree generates a markdown doctree for the root cobra.Command 57 // written to `dir`. The root command is also renamed to _index.md to remain 58 // compatible with the vitessio/website content structure expectations. 59 func GenerateMarkdownTree(cmd *cobra.Command, dir string) error { 60 switch fi, err := os.Stat(dir); { 61 case errors.Is(err, fs.ErrNotExist): 62 if err := os.MkdirAll(dir, 0755); err != nil { 63 return err 64 } 65 case err != nil: 66 return err 67 case !fi.IsDir(): 68 return fmt.Errorf("%s exists but is not a directory", dir) 69 } 70 71 recursivelyDisableAutoGenTags(cmd) 72 if err := doc.GenMarkdownTreeCustom(cmd, dir, frontmatterFilePrepender, linkHandler); err != nil { 73 return err 74 } 75 76 rootDocPath := filepath.Join(dir, cmd.Name()+".md") 77 indexDocPath := filepath.Join(dir, "_index.md") 78 if err := os.Rename(rootDocPath, indexDocPath); err != nil { 79 return fmt.Errorf("failed to index doc (generated at %s) into proper position (%s): %w", rootDocPath, indexDocPath, err) 80 } 81 82 return nil 83 } 84 85 func recursivelyDisableAutoGenTags(root *cobra.Command) { 86 commands := []*cobra.Command{root} 87 for cmd := commands[0]; len(commands) > 0; cmd, commands = commands[0], commands[1:] { 88 cmd.DisableAutoGenTag = true 89 90 commands = append(commands, cmd.Commands()...) 91 } 92 } 93 94 const frontmatter = `--- 95 title: %s 96 series: %s 97 --- 98 ` 99 100 func frontmatterFilePrepender(filename string) string { 101 name := filepath.Base(filename) 102 base := strings.TrimSuffix(name, filepath.Ext(name)) 103 104 root, cmdName, ok := strings.Cut(base, "_") 105 if !ok { // no `_`, so not a subcommand 106 cmdName = root 107 } 108 109 return fmt.Sprintf(frontmatter, cmdName, root) 110 } 111 112 func linkHandler(filename string) string { 113 name := filepath.Base(filename) 114 base := strings.TrimSuffix(name, filepath.Ext(name)) 115 116 if _, _, ok := strings.Cut(base, "_"); !ok { 117 return "../" 118 } 119 120 return fmt.Sprintf("./%s/", strings.ToLower(base)) 121 }