summaryrefslogtreecommitdiff
path: root/docs/validator.go
diff options
context:
space:
mode:
Diffstat (limited to 'docs/validator.go')
-rw-r--r--docs/validator.go227
1 files changed, 227 insertions, 0 deletions
diff --git a/docs/validator.go b/docs/validator.go
new file mode 100644
index 0000000..7d5cbaa
--- /dev/null
+++ b/docs/validator.go
@@ -0,0 +1,227 @@
+// +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("&lt;", "<", "&gt;", ">", "&quot;", `"`, "&amp;", "&").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)
+
+}