kata-containers/tests/cmd/check-markdown/main.go
Chelsea Mafrica 8ad433d4ad tests: move markdown check tool to main repo
Move the tool as a dependency for static checks migration.

Fixes #8187

Signed-off-by: Bin Liu <bin@hyper.sh>
Signed-off-by: Chelsea Mafrica <chelsea.e.mafrica@intel.com>
Signed-off-by: Gabriela Cervantes <gabriela.cervantes.tellez@intel.com>
Signed-off-by: Ganesh Maharaj Mahalingam <ganesh.mahalingam@intel.com>
Signed-off-by: James O. D. Hunt <james.o.hunt@intel.com>
Signed-off-by: Julio Montes <julio.montes@intel.com>
2023-11-28 11:13:55 -08:00

349 lines
7.1 KiB
Go

//
// Copyright (c) 2019 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
package main
import (
"errors"
"fmt"
"os"
"time"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
type DataToShow int
const (
// Character used (after an optional filename) before a heading ID.
anchorPrefix = "#"
// Character used to signify an "absolute link path" which should
// expand to the value of the document root.
absoluteLinkPrefix = "/"
showLinks DataToShow = iota
showHeadings DataToShow = iota
textFormat = "text"
tsvFormat = "tsv"
defaultOutputFormat = textFormat
defaultSeparator = "\t"
)
var (
// set by the build
name = ""
version = ""
commit = ""
strict = false
// list entry character to use when generating TOCs
listPrefix = "*"
logger *logrus.Entry
errNeedFile = errors.New("need markdown file")
)
// Black Friday sometimes chokes on markdown (I know!!), so record how many
// extra headings we found.
var extraHeadings int
// Root directory used to handle "absolute link paths" that start with a slash
// to denote the "top directory", like this:
//
// [Foo](/absolute-link.md)
var docRoot string
var notes = fmt.Sprintf(`
NOTES:
- The document root is used to handle markdown references that begin with %q,
denoting that the path that follows is an "absolute path" from the specified
document root path.
- The order the document nodes are parsed internally is not known to
this program. This means that if multiple errors exist in the document,
running this tool multiple times will error one *one* of the errors, but not
necessarily the same one as last time.
LIMITATIONS:
- The default document root only works if this tool is run from the top-level
of a repository.
`, absoluteLinkPrefix)
var formatFlag = cli.StringFlag{
Name: "format",
Usage: "display in specified format ('help' to show all)",
Value: defaultOutputFormat,
}
var separatorFlag = cli.StringFlag{
Name: "separator",
Usage: fmt.Sprintf("use the specified separator character (%s format only)", tsvFormat),
Value: defaultSeparator,
}
var noHeaderFlag = cli.BoolFlag{
Name: "no-header",
Usage: "disable display of header (if format supports one)",
}
func init() {
logger = logrus.WithFields(logrus.Fields{
"name": name,
"source": "check-markdown",
"version": version,
"commit": commit,
"pid": os.Getpid(),
})
logger.Logger.Formatter = &logrus.TextFormatter{
TimestampFormat: time.RFC3339Nano,
//DisableColors: true,
}
// Write to stdout to avoid upsetting CI systems that consider stderr
// writes as indicating an error.
logger.Logger.Out = os.Stdout
}
func handleLogging(c *cli.Context) {
logLevel := logrus.InfoLevel
if c.GlobalBool("debug") {
logLevel = logrus.DebugLevel
}
logger.Logger.SetLevel(logLevel)
}
func handleDoc(c *cli.Context, createTOC bool) error {
handleLogging(c)
if c.NArg() == 0 {
return errNeedFile
}
fileName := c.Args().First()
if fileName == "" {
return errNeedFile
}
singleDocOnly := c.GlobalBool("single-doc-only")
doc := newDoc(fileName, logger)
doc.ShowTOC = createTOC
if createTOC {
// Only makes sense to generate a single TOC!
singleDocOnly = true
}
// Parse the main document first
err := doc.parse()
if err != nil {
return err
}
if singleDocOnly && len(docs) > 1 {
doc.Logger.Debug("Not checking referenced files at user request")
return nil
}
// Now handle all other docs that the main doc references.
// This requires care to avoid recursion.
for {
count := len(docs)
parsed := 0
for _, doc := range docs {
if doc.Parsed {
// Document has already been handled
parsed++
continue
}
if err := doc.parse(); err != nil {
return err
}
}
if parsed == count {
break
}
}
err = handleIntraDocLinks()
if err != nil {
return err
}
if !createTOC {
doc.Logger.Info("Checked file")
doc.showStats()
}
count := len(docs)
if count > 1 {
// Update to ignore main document
count--
doc.Logger.WithField("reference-document-count", count).Info("Checked referenced files")
for _, d := range docs {
if d.Name == doc.Name {
// Ignore main document
continue
}
fmt.Printf("\t%q\n", d.Name)
}
}
// Highlight blackfriday deficiencies
if !doc.ShowTOC && extraHeadings > 0 {
doc.Logger.WithField("extra-heading-count", extraHeadings).Debug("Found extra headings")
}
return nil
}
// commonListHandler is used to handle all list operations.
func commonListHandler(context *cli.Context, what DataToShow) error {
handleLogging(context)
handlers := NewDisplayHandlers(context.String("separator"), context.Bool("no-header"))
format := context.String("format")
if format == "help" {
availableFormats := handlers.Get()
for _, format := range availableFormats {
fmt.Fprintf(outputFile, "%s\n", format)
}
return nil
}
handler := handlers.find(format)
if handler == nil {
return fmt.Errorf("no handler for format %q", format)
}
if context.NArg() == 0 {
return errNeedFile
}
file := context.Args().Get(0)
return show(file, logger, handler, what)
}
func realMain() error {
cwd, err := os.Getwd()
if err != nil {
return err
}
docRoot = cwd
cli.VersionPrinter = func(c *cli.Context) {
fmt.Fprintln(os.Stdout, c.App.Version)
}
cli.AppHelpTemplate = fmt.Sprintf(`%s%s`, cli.AppHelpTemplate, notes)
app := cli.NewApp()
app.Name = name
app.Version = fmt.Sprintf("%s %s (commit %v)", name, version, commit)
app.Description = "Tool to check GitHub-Flavoured Markdown (GFM) format documents"
app.Usage = app.Description
app.UsageText = fmt.Sprintf("%s [options] file ...", app.Name)
app.Flags = []cli.Flag{
cli.BoolFlag{
Name: "debug, d",
Usage: "display debug information",
},
cli.StringFlag{
Name: "doc-root, r",
Usage: "specify document root",
Value: docRoot,
},
cli.BoolFlag{
Name: "single-doc-only, o",
Usage: "only check primary (specified) document",
},
cli.BoolFlag{
Name: "strict, s",
Usage: "enable strict mode",
},
}
app.Commands = []cli.Command{
{
Name: "check",
Usage: "perform tests on the specified document",
Description: "Exit code denotes success",
Action: func(c *cli.Context) error {
return handleDoc(c, false)
},
},
{
Name: "toc",
Usage: "display a markdown Table of Contents",
Action: func(c *cli.Context) error {
return handleDoc(c, true)
},
},
{
Name: "list",
Usage: "display particular parts of the document",
Subcommands: []cli.Command{
{
Name: "headings",
Usage: "display headings",
Flags: []cli.Flag{
formatFlag,
noHeaderFlag,
separatorFlag,
},
Action: func(c *cli.Context) error {
return commonListHandler(c, showHeadings)
},
},
{
Name: "links",
Usage: "display links",
Flags: []cli.Flag{
formatFlag,
noHeaderFlag,
separatorFlag,
},
Action: func(c *cli.Context) error {
return commonListHandler(c, showLinks)
},
},
},
},
}
return app.Run(os.Args)
}
func main() {
err := realMain()
if err != nil {
logger.Fatalf("%v", err)
}
}