Skip to content

Commit 6778f7f

Browse files
committed
Implement mungedocs example syncer
1 parent 9721efe commit 6778f7f

File tree

4 files changed

+184
-0
lines changed

4 files changed

+184
-0
lines changed

cmd/mungedocs/example_syncer.go

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/*
2+
Copyright 2015 The Kubernetes Authors All rights reserved.
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 main
18+
19+
import (
20+
"bytes"
21+
"fmt"
22+
"io/ioutil"
23+
"path"
24+
"regexp"
25+
"strings"
26+
)
27+
28+
const exampleMungeTag = "EXAMPLE"
29+
30+
// syncExamples updates all examples in markdown file.
31+
//
32+
// Finds the magic macro block tags, find the link to the example
33+
// specified in the tags, and replaces anything between those with
34+
// the content of the example, thereby syncing it.
35+
//
36+
// For example,
37+
// <!-- BEGIN MUNGE: EXAMPLE ../../examples/guestbook/frontend-controller.yaml -->
38+
//
39+
// ```yaml
40+
// foo:
41+
// bar:
42+
// ```
43+
//
44+
// [Download example](../../examples/guestbook/frontend-controller.yaml)
45+
// <!-- END MUNGE: EXAMPLE -->
46+
func syncExamples(filePath string, markdown []byte) ([]byte, error) {
47+
// find the example syncer begin tag
48+
header := beginMungeTag(fmt.Sprintf("%s %s", exampleMungeTag, `(([^ ])*.(yaml|json))`))
49+
exampleLinkRE := regexp.MustCompile(header)
50+
lines := splitLines(markdown)
51+
updatedMarkdown, err := updateExampleMacroBlock(filePath, lines, exampleLinkRE, endMungeTag(exampleMungeTag))
52+
if err != nil {
53+
return updatedMarkdown, err
54+
}
55+
return updatedMarkdown, nil
56+
}
57+
58+
// exampleContent retrieves the content of the file at linkPath
59+
func exampleContent(filePath, linkPath, fileType string) (content string, err error) {
60+
realRoot := path.Join(*rootDir, *repoRoot) + "/"
61+
path := path.Join(realRoot, path.Dir(filePath), linkPath)
62+
dat, err := ioutil.ReadFile(path)
63+
if err != nil {
64+
return content, err
65+
}
66+
content = fmt.Sprintf("\n```%s\n%s\n```\n\n[Download example](%s)", fileType, string(dat), linkPath)
67+
return
68+
}
69+
70+
// updateExampleMacroBlock sync the yaml/json example between begin tag and end tag
71+
func updateExampleMacroBlock(filePath string, lines []string, beginMarkExp *regexp.Regexp, endMark string) ([]byte, error) {
72+
var buffer bytes.Buffer
73+
betweenBeginAndEnd := false
74+
for _, line := range lines {
75+
trimmedLine := strings.Trim(line, " \n")
76+
if beginMarkExp.Match([]byte(trimmedLine)) {
77+
if betweenBeginAndEnd {
78+
return nil, fmt.Errorf("found second begin mark while updating macro blocks")
79+
}
80+
betweenBeginAndEnd = true
81+
buffer.WriteString(line)
82+
buffer.WriteString("\n")
83+
match := beginMarkExp.FindStringSubmatch(line)
84+
if len(match) < 4 {
85+
return nil, fmt.Errorf("failed to parse the link in example header")
86+
}
87+
// match[0] is the entire expression; [1] is the link text and [3] is the file type (yaml or json).
88+
linkText := match[1]
89+
fileType := match[3]
90+
example, err := exampleContent(filePath, linkText, fileType)
91+
if err != nil {
92+
return nil, err
93+
}
94+
buffer.WriteString(example)
95+
} else if trimmedLine == endMark {
96+
if !betweenBeginAndEnd {
97+
return nil, fmt.Errorf("found end mark without being mark while updating macro blocks")
98+
}
99+
// Extra newline avoids github markdown bug where comment ends up on same line as last bullet.
100+
buffer.WriteString("\n")
101+
buffer.WriteString(line)
102+
buffer.WriteString("\n")
103+
betweenBeginAndEnd = false
104+
} else {
105+
if !betweenBeginAndEnd {
106+
buffer.WriteString(line)
107+
buffer.WriteString("\n")
108+
}
109+
}
110+
}
111+
if betweenBeginAndEnd {
112+
return nil, fmt.Errorf("never found closing end mark while updating macro blocks")
113+
}
114+
return buffer.Bytes(), nil
115+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
Copyright 2015 The Kubernetes Authors All rights reserved.
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 main
18+
19+
import (
20+
"testing"
21+
22+
"github.com/stretchr/testify/assert"
23+
)
24+
25+
func Test_syncExamples(t *testing.T) {
26+
var podExample = `apiVersion: v1
27+
kind: Pod
28+
metadata:
29+
name: nginx
30+
spec:
31+
containers:
32+
- name: nginx
33+
image: nginx
34+
ports:
35+
- containerPort: 80
36+
`
37+
var cases = []struct {
38+
in string
39+
out string
40+
}{
41+
{"", ""},
42+
{
43+
"<!-- BEGIN MUNGE: EXAMPLE testdata/pod.yaml -->\n<!-- END MUNGE: EXAMPLE -->\n",
44+
"<!-- BEGIN MUNGE: EXAMPLE testdata/pod.yaml -->\n\n```yaml\n" + podExample + "```\n\n[Download example](testdata/pod.yaml)\n<!-- END MUNGE: EXAMPLE -->\n",
45+
},
46+
{
47+
"<!-- BEGIN MUNGE: EXAMPLE ../mungedocs/testdata/pod.yaml -->\n<!-- END MUNGE: EXAMPLE -->\n",
48+
"<!-- 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",
49+
},
50+
}
51+
for _, c := range cases {
52+
actual, err := syncExamples("mungedocs/filename.md", []byte(c.in))
53+
assert.NoError(t, err)
54+
if c.out != string(actual) {
55+
t.Errorf("Expected example \n'%v' but got \n'%v'", c.out, string(actual))
56+
}
57+
}
58+
}

cmd/mungedocs/mungedocs.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ Examples:
5252
{"unversioned-warning", updateUnversionedWarning},
5353
{"analytics", checkAnalytics},
5454
{"kubectl-dash-f", checkKubectlFileTargets},
55+
{"sync-examples", syncExamples},
5556
}
5657
availableMungeList = func() string {
5758
names := []string{}

cmd/mungedocs/testdata/pod.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
apiVersion: v1
2+
kind: Pod
3+
metadata:
4+
name: nginx
5+
spec:
6+
containers:
7+
- name: nginx
8+
image: nginx
9+
ports:
10+
- containerPort: 80

0 commit comments

Comments
 (0)