-
Notifications
You must be signed in to change notification settings - Fork 57
/
render-adoc.go
138 lines (124 loc) · 4.63 KB
/
render-adoc.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
package main
import (
"fmt"
"os"
"path/filepath"
"sort"
"strings"
"text/template"
"github.com/PuerkitoBio/goquery"
)
// render-adoc.go extracts information from groovydoc HTML pages, and renders
// the content into Antora templates. This process ensures that the final
// documentation reflects the source code accurately, without manual effort.
//
// Before running "go run render-adoc.go", ensure to run "./gradlew groovydoc".
// This should place HTML documents into "build/docs/groovydoc". From the pages
// describing the "Options" classes, the property information (name, type,
// description) is extracted, and then rendered into the Antora partials.
//
// render-adoc.go will process all templates (ending in .adoc.tmpl)
// automatically and assumes a corresponding "Options" class exists.
//
// This code is written in Go because it is extremly easy to write in Go.
// Also, most of ODS already requires Go so it is likely to be installed.
// On the flip side, it would be nice to have this in Groovy so that the
// shared library does not require another language to be built ... maybe an
// optimization for later?
const (
pathSep = string(os.PathSeparator)
globalComponentStagePrefix = "odsComponentStage"
adocTemplateSuffix = ".adoc.tmpl"
)
var (
groovydocComponentPath = filepath.FromSlash("build/docs/groovydoc/org/ods/component")
antoraPartialsPath = filepath.FromSlash("docs/modules/jenkins-shared-library/partials")
)
func main() {
adocTemplatePrefix := fmt.Sprintf(filepath.FromSlash("%s/%s"), antoraPartialsPath, globalComponentStagePrefix)
templateFiles, err := filepath.Glob(fmt.Sprintf(filepath.FromSlash("%s/*%s"), antoraPartialsPath, adocTemplateSuffix))
check(err)
for _, templateFile := range templateFiles {
stageName := strings.TrimPrefix(templateFile, adocTemplatePrefix)
stageName = strings.TrimSuffix(stageName, adocTemplateSuffix)
renderDocs(stageName)
}
}
func check(e error) {
if e != nil {
panic(e)
}
}
type option struct {
Name string
Type string
Description string
}
type docData struct {
Options []option
}
func extractType(rawType string) string {
cleanedType := strings.Replace(strings.TrimSpace(rawType), " ", " ", -1)
cleanedType = strings.Replace(cleanedType, "java.util.", "", -1)
cleanedType = strings.Replace(cleanedType, "java.lang.", "", -1)
return cleanedType
}
func getOptions(filename string) []option {
optionsDocFile, err := os.Open(filename)
check(err)
optionsDoc, err := goquery.NewDocumentFromReader(optionsDocFile)
check(err)
options := []option{}
optionsDoc.Find("div.details ul.blockListLast").Each(func(i int, s *goquery.Selection) {
optionType := extractType(s.Find("li.blockList h4").Nodes[0].FirstChild.Data)
optionName := strings.TrimSpace(s.Find("li.blockList h4 strong").Text())
optionDesc := strings.TrimSpace(s.Find("p").Text())
options = append(options, option{Name: optionName, Type: optionType, Description: optionDesc})
})
return options
}
func renderTemplate(targetFilename string, data docData) {
targetFile, err := os.Create(targetFilename)
check(err)
templateFilename := targetFilename + ".tmpl"
templateFileParts := strings.Split(templateFilename, "/")
templateDisplayname := templateFileParts[len(templateFileParts)-1]
_, err = targetFile.WriteString(
"// Document generated by render-adoc.go from " + templateDisplayname + "; DO NOT EDIT.\n\n",
)
check(err)
tmpl, err := template.ParseFiles(templateFilename)
check(err)
err = tmpl.Execute(targetFile, data)
check(err)
}
func mergeAndSortOptions(stageOptions, globalOptions []option) []option {
for _, globalOption := range globalOptions {
globalOptionOverriden := false
for _, stageOption := range stageOptions {
if globalOption.Name == stageOption.Name {
globalOptionOverriden = true
}
}
if !globalOptionOverriden {
stageOptions = append(stageOptions, globalOption)
}
}
sort.SliceStable(stageOptions, func(i, j int) bool {
return stageOptions[i].Name < stageOptions[j].Name
})
return stageOptions
}
// renderDocs looks for HTML documents of the general options and the
// stage-specific options, extracts the information into a struct, and
// executes the Asciidoctor template belonging to the given stage.
func renderDocs(stageName string) {
stageOptions := getOptions(fmt.Sprintf("%s/%sOptions.html", groovydocComponentPath, stageName))
globalOptions := getOptions(fmt.Sprintf("%s/Options.html", groovydocComponentPath))
stageOptions = mergeAndSortOptions(stageOptions, globalOptions)
data := docData{Options: stageOptions}
renderTemplate(
fmt.Sprintf("%s/%s%s.adoc", antoraPartialsPath, globalComponentStagePrefix, stageName),
data,
)
}