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  }