228 lines
5.4 KiB
Go
228 lines
5.4 KiB
Go
|
// +build ignore
|
||
|
|
||
|
/*
|
||
|
* Minio Go Library for Amazon S3 Compatible Cloud Storage
|
||
|
* Copyright 2015-2017 Minio, Inc.
|
||
|
*
|
||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
* you may not use this file except in compliance with the License.
|
||
|
* You may obtain a copy of the License at
|
||
|
*
|
||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing, software
|
||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
* See the License for the specific language governing permissions and
|
||
|
* limitations under the License.
|
||
|
*/
|
||
|
|
||
|
package main
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"io/ioutil"
|
||
|
"os"
|
||
|
"os/exec"
|
||
|
"path/filepath"
|
||
|
"strings"
|
||
|
"text/template"
|
||
|
|
||
|
"github.com/a8m/mark"
|
||
|
"github.com/gernest/wow"
|
||
|
"github.com/gernest/wow/spin"
|
||
|
"github.com/minio/cli"
|
||
|
)
|
||
|
|
||
|
func init() {
|
||
|
// Validate go binary.
|
||
|
if _, err := exec.LookPath("go"); err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var globalFlags = []cli.Flag{
|
||
|
cli.StringFlag{
|
||
|
Name: "m",
|
||
|
Value: "API.md",
|
||
|
Usage: "Path to markdown api documentation.",
|
||
|
},
|
||
|
cli.StringFlag{
|
||
|
Name: "t",
|
||
|
Value: "checker.go.template",
|
||
|
Usage: "Template used for generating the programs.",
|
||
|
},
|
||
|
cli.IntFlag{
|
||
|
Name: "skip",
|
||
|
Value: 2,
|
||
|
Usage: "Skip entries before validating the code.",
|
||
|
},
|
||
|
}
|
||
|
|
||
|
func runGofmt(path string) (msg string, err error) {
|
||
|
cmdArgs := []string{"-s", "-w", "-l", path}
|
||
|
cmd := exec.Command("gofmt", cmdArgs...)
|
||
|
stdoutStderr, err := cmd.CombinedOutput()
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
return string(stdoutStderr), nil
|
||
|
}
|
||
|
|
||
|
func runGoImports(path string) (msg string, err error) {
|
||
|
cmdArgs := []string{"-w", path}
|
||
|
cmd := exec.Command("goimports", cmdArgs...)
|
||
|
stdoutStderr, err := cmd.CombinedOutput()
|
||
|
if err != nil {
|
||
|
return string(stdoutStderr), err
|
||
|
}
|
||
|
return string(stdoutStderr), nil
|
||
|
}
|
||
|
|
||
|
func runGoBuild(path string) (msg string, err error) {
|
||
|
// Go build the path.
|
||
|
cmdArgs := []string{"build", "-o", "/dev/null", path}
|
||
|
cmd := exec.Command("go", cmdArgs...)
|
||
|
stdoutStderr, err := cmd.CombinedOutput()
|
||
|
if err != nil {
|
||
|
return string(stdoutStderr), err
|
||
|
}
|
||
|
return string(stdoutStderr), nil
|
||
|
}
|
||
|
|
||
|
func validatorAction(ctx *cli.Context) error {
|
||
|
if !ctx.IsSet("m") || !ctx.IsSet("t") {
|
||
|
return nil
|
||
|
}
|
||
|
docPath := ctx.String("m")
|
||
|
var err error
|
||
|
docPath, err = filepath.Abs(docPath)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
data, err := ioutil.ReadFile(docPath)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
templatePath := ctx.String("t")
|
||
|
templatePath, err = filepath.Abs(templatePath)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
skipEntries := ctx.Int("skip")
|
||
|
m := mark.New(string(data), &mark.Options{
|
||
|
Gfm: true, // Github markdown support is enabled by default.
|
||
|
})
|
||
|
|
||
|
t, err := template.ParseFiles(templatePath)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
tmpDir, err := ioutil.TempDir("", "md-verifier")
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
defer os.RemoveAll(tmpDir)
|
||
|
|
||
|
entryN := 1
|
||
|
for i := mark.NodeText; i < mark.NodeCheckbox; i++ {
|
||
|
if mark.NodeCode != mark.NodeType(i) {
|
||
|
m.AddRenderFn(mark.NodeType(i), func(node mark.Node) (s string) {
|
||
|
return ""
|
||
|
})
|
||
|
continue
|
||
|
}
|
||
|
m.AddRenderFn(mark.NodeCode, func(node mark.Node) (s string) {
|
||
|
p, ok := node.(*mark.CodeNode)
|
||
|
if !ok {
|
||
|
return
|
||
|
}
|
||
|
p.Text = strings.NewReplacer("<", "<", ">", ">", """, `"`, "&", "&").Replace(p.Text)
|
||
|
if skipEntries > 0 {
|
||
|
skipEntries--
|
||
|
return
|
||
|
}
|
||
|
|
||
|
testFilePath := filepath.Join(tmpDir, "example.go")
|
||
|
w, werr := os.Create(testFilePath)
|
||
|
if werr != nil {
|
||
|
panic(werr)
|
||
|
}
|
||
|
t.Execute(w, p)
|
||
|
w.Sync()
|
||
|
w.Close()
|
||
|
entryN++
|
||
|
|
||
|
msg, err := runGofmt(testFilePath)
|
||
|
if err != nil {
|
||
|
fmt.Printf("Failed running gofmt on %s, with (%s):(%s)\n", testFilePath, msg, err)
|
||
|
os.Exit(-1)
|
||
|
}
|
||
|
|
||
|
msg, err = runGoImports(testFilePath)
|
||
|
if err != nil {
|
||
|
fmt.Printf("Failed running gofmt on %s, with (%s):(%s)\n", testFilePath, msg, err)
|
||
|
os.Exit(-1)
|
||
|
}
|
||
|
|
||
|
msg, err = runGoBuild(testFilePath)
|
||
|
if err != nil {
|
||
|
fmt.Printf("Failed running gobuild on %s, with (%s):(%s)\n", testFilePath, msg, err)
|
||
|
fmt.Printf("Code with possible issue in %s:\n%s", docPath, p.Text)
|
||
|
fmt.Printf("To test `go build %s`\n", testFilePath)
|
||
|
os.Exit(-1)
|
||
|
}
|
||
|
|
||
|
// Once successfully built remove the test file
|
||
|
os.Remove(testFilePath)
|
||
|
return
|
||
|
})
|
||
|
}
|
||
|
|
||
|
w := wow.New(os.Stdout, spin.Get(spin.Moon), fmt.Sprintf(" Running validation tests in %s", tmpDir))
|
||
|
|
||
|
w.Start()
|
||
|
// Render markdown executes our checker on each code blocks.
|
||
|
_ = m.Render()
|
||
|
w.PersistWith(spin.Get(spin.Runner), " Successfully finished tests")
|
||
|
w.Stop()
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func main() {
|
||
|
app := cli.NewApp()
|
||
|
app.Action = validatorAction
|
||
|
app.HideVersion = true
|
||
|
app.HideHelpCommand = true
|
||
|
app.Usage = "Validates code block sections inside API.md"
|
||
|
app.Author = "Minio.io"
|
||
|
app.Flags = globalFlags
|
||
|
// Help template for validator
|
||
|
app.CustomAppHelpTemplate = `NAME:
|
||
|
{{.Name}} - {{.Usage}}
|
||
|
|
||
|
USAGE:
|
||
|
{{.Name}} {{if .VisibleFlags}}[FLAGS] {{end}}COMMAND{{if .VisibleFlags}} [COMMAND FLAGS | -h]{{end}} [ARGUMENTS...]
|
||
|
|
||
|
COMMANDS:
|
||
|
{{range .VisibleCommands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}}
|
||
|
{{end}}{{if .VisibleFlags}}
|
||
|
FLAGS:
|
||
|
{{range .VisibleFlags}}{{.}}
|
||
|
{{end}}{{end}}
|
||
|
TEMPLATE:
|
||
|
Validator uses Go's 'text/template' formatting so you need to ensure
|
||
|
your template is formatted correctly, check 'docs/checker.go.template'
|
||
|
|
||
|
USAGE:
|
||
|
go run docs/validator.go -m docs/API.md -t /tmp/mycode.go.template
|
||
|
|
||
|
`
|
||
|
app.Run(os.Args)
|
||
|
|
||
|
}
|