Merge pull request #11499 from JanetKuo/docs-example-syncer

Implement mungedocs example syncer
pull/6/head
Vish Kannan 2015-07-23 14:11:13 -07:00
commit a3985e36a9
4 changed files with 184 additions and 0 deletions

View File

@ -0,0 +1,115 @@
/*
Copyright 2015 The Kubernetes Authors All rights reserved.
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 (
"bytes"
"fmt"
"io/ioutil"
"path"
"regexp"
"strings"
)
const exampleMungeTag = "EXAMPLE"
// syncExamples updates all examples in markdown file.
//
// Finds the magic macro block tags, find the link to the example
// specified in the tags, and replaces anything between those with
// the content of the example, thereby syncing it.
//
// For example,
// <!-- BEGIN MUNGE: EXAMPLE ../../examples/guestbook/frontend-controller.yaml -->
//
// ```yaml
// foo:
// bar:
// ```
//
// [Download example](../../examples/guestbook/frontend-controller.yaml)
// <!-- END MUNGE: EXAMPLE -->
func syncExamples(filePath string, markdown []byte) ([]byte, error) {
// find the example syncer begin tag
header := beginMungeTag(fmt.Sprintf("%s %s", exampleMungeTag, `(([^ ])*.(yaml|json))`))
exampleLinkRE := regexp.MustCompile(header)
lines := splitLines(markdown)
updatedMarkdown, err := updateExampleMacroBlock(filePath, lines, exampleLinkRE, endMungeTag(exampleMungeTag))
if err != nil {
return updatedMarkdown, err
}
return updatedMarkdown, nil
}
// exampleContent retrieves the content of the file at linkPath
func exampleContent(filePath, linkPath, fileType string) (content string, err error) {
realRoot := path.Join(*rootDir, *repoRoot) + "/"
path := path.Join(realRoot, path.Dir(filePath), linkPath)
dat, err := ioutil.ReadFile(path)
if err != nil {
return content, err
}
content = fmt.Sprintf("\n```%s\n%s\n```\n\n[Download example](%s)", fileType, string(dat), linkPath)
return
}
// updateExampleMacroBlock sync the yaml/json example between begin tag and end tag
func updateExampleMacroBlock(filePath string, lines []string, beginMarkExp *regexp.Regexp, endMark string) ([]byte, error) {
var buffer bytes.Buffer
betweenBeginAndEnd := false
for _, line := range lines {
trimmedLine := strings.Trim(line, " \n")
if beginMarkExp.Match([]byte(trimmedLine)) {
if betweenBeginAndEnd {
return nil, fmt.Errorf("found second begin mark while updating macro blocks")
}
betweenBeginAndEnd = true
buffer.WriteString(line)
buffer.WriteString("\n")
match := beginMarkExp.FindStringSubmatch(line)
if len(match) < 4 {
return nil, fmt.Errorf("failed to parse the link in example header")
}
// match[0] is the entire expression; [1] is the link text and [3] is the file type (yaml or json).
linkText := match[1]
fileType := match[3]
example, err := exampleContent(filePath, linkText, fileType)
if err != nil {
return nil, err
}
buffer.WriteString(example)
} else if trimmedLine == endMark {
if !betweenBeginAndEnd {
return nil, fmt.Errorf("found end mark without being mark while updating macro blocks")
}
// Extra newline avoids github markdown bug where comment ends up on same line as last bullet.
buffer.WriteString("\n")
buffer.WriteString(line)
buffer.WriteString("\n")
betweenBeginAndEnd = false
} else {
if !betweenBeginAndEnd {
buffer.WriteString(line)
buffer.WriteString("\n")
}
}
}
if betweenBeginAndEnd {
return nil, fmt.Errorf("never found closing end mark while updating macro blocks")
}
return buffer.Bytes(), nil
}

View File

@ -0,0 +1,58 @@
/*
Copyright 2015 The Kubernetes Authors All rights reserved.
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 (
"testing"
"github.com/stretchr/testify/assert"
)
func Test_syncExamples(t *testing.T) {
var podExample = `apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
`
var cases = []struct {
in string
out string
}{
{"", ""},
{
"<!-- BEGIN MUNGE: EXAMPLE testdata/pod.yaml -->\n<!-- END MUNGE: EXAMPLE -->\n",
"<!-- BEGIN MUNGE: EXAMPLE testdata/pod.yaml -->\n\n```yaml\n" + podExample + "```\n\n[Download example](testdata/pod.yaml)\n<!-- END MUNGE: EXAMPLE -->\n",
},
{
"<!-- BEGIN MUNGE: EXAMPLE ../mungedocs/testdata/pod.yaml -->\n<!-- END MUNGE: EXAMPLE -->\n",
"<!-- BEGIN MUNGE: EXAMPLE ../mungedocs/testdata/pod.yaml -->\n\n```yaml\n" + podExample + "```\n\n[Download example](../mungedocs/testdata/pod.yaml)\n<!-- END MUNGE: EXAMPLE -->\n",
},
}
for _, c := range cases {
actual, err := syncExamples("mungedocs/filename.md", []byte(c.in))
assert.NoError(t, err)
if c.out != string(actual) {
t.Errorf("Expected example \n'%v' but got \n'%v'", c.out, string(actual))
}
}
}

View File

@ -52,6 +52,7 @@ Examples:
{"unversioned-warning", updateUnversionedWarning},
{"analytics", checkAnalytics},
{"kubectl-dash-f", checkKubectlFileTargets},
{"sync-examples", syncExamples},
}
availableMungeList = func() string {
names := []string{}

10
cmd/mungedocs/testdata/pod.yaml vendored Normal file
View File

@ -0,0 +1,10 @@
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80