Skip to content

Commit 94cc6af

Browse files
committed
Add possibility to generate subsectioned Values Tables
This adds `@section` comments, which will be parsed into semantic section info for valueRows. This allows to generate sectioned Values Tables by adding the templates `chart.valuesSectionedSection` and `chart.valuesSectionedSectionHTML` and thereby keeping compatability with default Values Tables
1 parent 913cc49 commit 94cc6af

File tree

5 files changed

+177
-4
lines changed

5 files changed

+177
-4
lines changed

pkg/document/model.go

+76-3
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ type valueRow struct {
2020
Default string
2121
AutoDescription string
2222
Description string
23+
Section string
2324
Column int
2425
LineNumber int
2526
Dependency string
@@ -30,17 +31,52 @@ type chartTemplateData struct {
3031
helm.ChartDocumentationInfo
3132
HelmDocsVersion string
3233
Values []valueRow
34+
Sections []section
3335
Files files
3436
}
3537

36-
func sortValueRows(valueRows []valueRow) {
38+
type section struct {
39+
SectionName string
40+
SectionItems []valueRow
41+
}
42+
43+
func sortValueRows(valueRows []valueRow) ([]valueRow, []section) {
3744
sortOrder := viper.GetString("sort-values-order")
3845

3946
if sortOrder != FileSortOrder && sortOrder != AlphaNumSortOrder {
4047
log.Warnf("Invalid sort order provided %s, defaulting to %s", sortOrder, AlphaNumSortOrder)
4148
sortOrder = AlphaNumSortOrder
4249
}
4350

51+
var valueRowsSectionSorted []section
52+
valueRowsSectionSorted = append(valueRowsSectionSorted, section{
53+
SectionName: "General",
54+
SectionItems: []valueRow{},
55+
})
56+
57+
for _, row := range valueRows {
58+
if row.Section == "" {
59+
valueRowsSectionSorted[0].SectionItems = append(valueRowsSectionSorted[0].SectionItems, row)
60+
continue
61+
}
62+
63+
containsSection := false
64+
for i, section := range valueRowsSectionSorted {
65+
if section.SectionName == row.Section {
66+
containsSection = true
67+
valueRowsSectionSorted[i].SectionItems = append(valueRowsSectionSorted[i].SectionItems, row)
68+
break
69+
}
70+
}
71+
72+
if !containsSection {
73+
valueRowsSectionSorted = append(valueRowsSectionSorted, section{
74+
SectionName: row.Section,
75+
SectionItems: []valueRow{row},
76+
})
77+
}
78+
}
79+
4480
sort.Slice(valueRows, func(i, j int) bool {
4581
// Globals sort above non-globals.
4682
if valueRows[i].IsGlobal != valueRows[j].IsGlobal {
@@ -73,6 +109,42 @@ func sortValueRows(valueRows []valueRow) {
73109
panic("cannot get here")
74110
}
75111
})
112+
113+
for _, section := range valueRowsSectionSorted {
114+
sort.Slice(section.SectionItems, func(i, j int) bool {
115+
// Globals sort above non-globals.
116+
if section.SectionItems[i].IsGlobal != section.SectionItems[j].IsGlobal {
117+
return section.SectionItems[i].IsGlobal
118+
}
119+
120+
// Group by dependency for non-globals.
121+
if !section.SectionItems[i].IsGlobal && !section.SectionItems[j].IsGlobal {
122+
// Values for the main chart sort above values for dependencies.
123+
if (section.SectionItems[i].Dependency == "") != (section.SectionItems[j].Dependency == "") {
124+
return section.SectionItems[i].Dependency == ""
125+
}
126+
127+
// Group dependency values together.
128+
if section.SectionItems[i].Dependency != section.SectionItems[j].Dependency {
129+
return section.SectionItems[i].Dependency < section.SectionItems[j].Dependency
130+
}
131+
}
132+
133+
// Sort the remaining values within the same section.SectionItems using the configured sort order.
134+
switch sortOrder {
135+
case FileSortOrder:
136+
if section.SectionItems[i].LineNumber == section.SectionItems[j].LineNumber {
137+
return section.SectionItems[i].Column < section.SectionItems[j].Column
138+
}
139+
return section.SectionItems[i].LineNumber < section.SectionItems[j].LineNumber
140+
case AlphaNumSortOrder:
141+
return section.SectionItems[i].Key < section.SectionItems[j].Key
142+
default:
143+
panic("cannot get here")
144+
}
145+
})
146+
}
147+
return valueRows, valueRowsSectionSorted
76148
}
77149

78150
func getUnsortedValueRows(document *yaml.Node, descriptions map[string]helm.ChartValueDescription) ([]valueRow, error) {
@@ -134,7 +206,7 @@ func getChartTemplateData(info helm.ChartDocumentationInfo, helmDocsVersion stri
134206
}
135207
}
136208

137-
sortValueRows(valuesTableRows)
209+
valueRowsSorted, valueRowsSectionSorted := sortValueRows(valuesTableRows)
138210

139211
files, err := getFiles(info.ChartDirectory)
140212
if err != nil {
@@ -144,7 +216,8 @@ func getChartTemplateData(info helm.ChartDocumentationInfo, helmDocsVersion stri
144216
return chartTemplateData{
145217
ChartDocumentationInfo: info,
146218
HelmDocsVersion: helmDocsVersion,
147-
Values: valuesTableRows,
219+
Values: valueRowsSorted,
220+
Sections: valueRowsSectionSorted,
148221
Files: files,
149222
}, nil
150223
}

pkg/document/template.go

+79
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,84 @@ func getValuesTableTemplates() string {
274274
return valuesSectionBuilder.String()
275275
}
276276

277+
func getValuesTableSectionedTemplates() string {
278+
valuesSectionBuilder := strings.Builder{}
279+
valuesSectionBuilder.WriteString(`{{ define "chart.valuesSectionedHeader" }}## Values{{ end }}`)
280+
281+
valuesSectionBuilder.WriteString(`{{ define "chart.valuesSectionedTable" }}`)
282+
valuesSectionBuilder.WriteString("{{ range .Sections }}")
283+
valuesSectionBuilder.WriteString("\n")
284+
valuesSectionBuilder.WriteString("\n### {{ .SectionName }}\n")
285+
valuesSectionBuilder.WriteString("| Key | Type | Default | Description |\n")
286+
valuesSectionBuilder.WriteString("|-----|------|---------|-------------|\n")
287+
valuesSectionBuilder.WriteString(" {{- range .SectionItems }}")
288+
valuesSectionBuilder.WriteString("\n| {{ .Key }} | {{ .Type }} | {{ if .Default }}{{ .Default }}{{ else }}{{ .AutoDefault }}{{ end }} | {{ if .Description }}{{ .Description }}{{ else }}{{ .AutoDescription }}{{ end }} |")
289+
valuesSectionBuilder.WriteString(" {{- end }}")
290+
valuesSectionBuilder.WriteString("{{ end }}")
291+
valuesSectionBuilder.WriteString("{{ end }}")
292+
293+
valuesSectionBuilder.WriteString(`{{ define "chart.valuesSectionedSection" }}`)
294+
valuesSectionBuilder.WriteString("{{ if .Values }}")
295+
valuesSectionBuilder.WriteString(`{{ template "chart.valuesSectionedHeader" . }}`)
296+
valuesSectionBuilder.WriteString("\n\n")
297+
valuesSectionBuilder.WriteString(`{{ template "chart.valuesSectionedTable" . }}`)
298+
valuesSectionBuilder.WriteString("{{ end }}")
299+
valuesSectionBuilder.WriteString("{{ end }}")
300+
301+
// For HTML tables
302+
valuesSectionBuilder.WriteString(`
303+
{{ define "chart.valueDefaultColumnRender" }}
304+
{{- $defaultValue := (default .Default .AutoDefault) -}}
305+
{{- $notationType := .NotationType }}
306+
{{- if (and (hasPrefix "` + "`" + `" $defaultValue) (hasSuffix "` + "`" + `" $defaultValue) ) -}}
307+
{{- $defaultValue = (toPrettyJson (fromJson (trimAll "` + "`" + `" (default .Default .AutoDefault) ) ) ) -}}
308+
{{- $notationType = "json" }}
309+
{{- end -}}
310+
<pre lang="{{ $notationType }}">
311+
{{- if (eq $notationType "tpl" ) }}
312+
{{ .Key }}: |
313+
{{- $defaultValue | nindent 2 }}
314+
{{- else }}
315+
{{ $defaultValue }}
316+
{{- end }}
317+
</pre>
318+
{{ end }}
319+
320+
{{ define "chart.valuesSectionedTableHtml" }}
321+
{{- range .Sections }}
322+
<h3>{{- .SectionName }}</h3>
323+
<table>
324+
<thead>
325+
<th>Key</th>
326+
<th>Type</th>
327+
<th>Default</th>
328+
<th>Description</th>
329+
</thead>
330+
<tbody>
331+
{{- range .SectionItems }}
332+
<tr>
333+
<td>{{ .Key }}</td>
334+
<td>{{ .Type }}</td>
335+
<td>{{ template "chart.valueDefaultColumnRender" . }}</td>
336+
<td>{{ if .Description }}{{ .Description }}{{ else }}{{ .AutoDescription }}{{ end }}</td>
337+
</tr>
338+
{{- end }}
339+
</tbody>
340+
</table>
341+
{{- end }}
342+
{{ end }}
343+
344+
{{ define "chart.valuesSectionedSectionHtml" }}
345+
{{ if .Sections }}
346+
{{ template "chart.valuesSectionedHeader" . }}
347+
{{ template "chart.valuesSectionedTableHtml" . }}
348+
{{ end }}
349+
{{ end }}
350+
`)
351+
352+
return valuesSectionBuilder.String()
353+
}
354+
277355
func getHelmDocsVersionTemplates() string {
278356
versionSectionBuilder := strings.Builder{}
279357
versionSectionBuilder.WriteString(`{{ define "helm-docs.version" }}{{ if .HelmDocsVersion }}{{ .HelmDocsVersion }}{{ end }}{{ end }}`)
@@ -350,6 +428,7 @@ func getDocumentationTemplates(chartDirectory string, chartSearchRoot string, te
350428
getSourceLinkTemplates(),
351429
getRequirementsTableTemplates(),
352430
getValuesTableTemplates(),
431+
getValuesTableSectionedTemplates(),
353432
getHomepageTemplate(),
354433
getMaintainersTemplate(),
355434
getHelmDocsVersionTemplates(),

pkg/document/values.go

+12
Original file line numberDiff line numberDiff line change
@@ -93,13 +93,19 @@ func parseNilValueType(key string, description helm.ChartValueDescription, autoD
9393
description.Default = "`nil`"
9494
}
9595

96+
section := description.Section
97+
if section == "" && autoDescription.Section != "" {
98+
section = autoDescription.Section
99+
}
100+
96101
return valueRow{
97102
Key: key,
98103
Type: t,
99104
AutoDefault: autoDescription.Default,
100105
Default: description.Default,
101106
AutoDescription: autoDescription.Description,
102107
Description: description.Description,
108+
Section: section,
103109
Column: column,
104110
LineNumber: lineNumber,
105111
}
@@ -179,6 +185,11 @@ func createValueRow(
179185
defaultValue = fmt.Sprintf("%s", value)
180186
}
181187

188+
section := description.Section
189+
if section == "" && autoDescription.Section != "" {
190+
section = autoDescription.Section
191+
}
192+
182193
return valueRow{
183194
Key: key,
184195
Type: defaultType,
@@ -187,6 +198,7 @@ func createValueRow(
187198
Default: defaultValue,
188199
AutoDescription: autoDescription.Description,
189200
Description: description.Description,
201+
Section: section,
190202
Column: column,
191203
LineNumber: lineNumber,
192204
}, nil

pkg/helm/chart_info.go

+4-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ var commentContinuationRegex = regexp.MustCompile("^\\s*#(\\s?)(.*)$")
2121
var defaultValueRegex = regexp.MustCompile("^\\s*# @default -- (.*)$")
2222
var valueTypeRegex = regexp.MustCompile("^\\((.*?)\\)\\s*(.*)$")
2323
var valueNotationTypeRegex = regexp.MustCompile("^\\s*#\\s+@notationType\\s+--\\s+(.*)$")
24+
var sectionRegex = regexp.MustCompile("^\\s*# @section -- (.*)$")
2425

2526
type ChartMetaMaintainer struct {
2627
Email string
@@ -57,6 +58,7 @@ type ChartRequirements struct {
5758
type ChartValueDescription struct {
5859
Description string
5960
Default string
61+
Section string
6062
ValueType string
6163
NotationType string
6264
}
@@ -268,9 +270,10 @@ func parseChartValuesFileComments(chartDirectory string, values *yaml.Node, lint
268270
// If we've already found a values comment, on the next line try and parse a custom default value. If we find one
269271
// that completes parsing for this key, add it to the list and reset to searching for a new key
270272
defaultCommentMatch := defaultValueRegex.FindStringSubmatch(currentLine)
273+
sectionCommentMatch := sectionRegex.FindStringSubmatch(currentLine)
271274
commentContinuationMatch := commentContinuationRegex.FindStringSubmatch(currentLine)
272275

273-
if len(defaultCommentMatch) > 1 || len(commentContinuationMatch) > 1 {
276+
if len(defaultCommentMatch) > 1 || len(sectionCommentMatch) > 1 || len(commentContinuationMatch) > 1 {
274277
commentLines = append(commentLines, currentLine)
275278
continue
276279
}

pkg/helm/comment.go

+6
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ func ParseComment(commentLines []string) (string, ChartValueDescription) {
5050
rawFlagMatch := rawDescriptionRegex.FindStringSubmatch(line)
5151
defaultCommentMatch := defaultValueRegex.FindStringSubmatch(line)
5252
notationTypeCommentMatch := valueNotationTypeRegex.FindStringSubmatch(line)
53+
sectionCommentMatch := sectionRegex.FindStringSubmatch(line)
5354

5455
if !isRaw && len(rawFlagMatch) == 1 {
5556
isRaw = true
@@ -66,6 +67,11 @@ func ParseComment(commentLines []string) (string, ChartValueDescription) {
6667
continue
6768
}
6869

70+
if len(sectionCommentMatch) > 1 {
71+
c.Section = sectionCommentMatch[1]
72+
continue
73+
}
74+
6975
commentContinuationMatch := commentContinuationRegex.FindStringSubmatch(line)
7076

7177
if isRaw {

0 commit comments

Comments
 (0)