Skip to content

Commit 185bc02

Browse files
Escape HTML in generated CLI docs (#4397)
I noticed on our docs, the generated CLI descriptions for some flags on `buf curl` were mis-rendering because of describing arguments like `<input>`. We need to HTML escape these. Additionally, remove the `include-front-matter` flag, which is no longer used (as our docs site is not a Docusaurus site any longer). Lastly, unconditionally add the `title:` frontmatter for now, which should help when rendering sub-flags: instead of rendering "Dep" or "Beta" (corresponding to `buf dep` and `buf beta`, respectively), we'll render the actual `buf beta` name, which should read better on the docs site.
1 parent 9e3337d commit 185bc02

2 files changed

Lines changed: 14 additions & 82 deletions

File tree

private/bufpkg/bufcobra/bufcobra.go

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
package bufcobra
1616

1717
import (
18-
"fmt"
1918
"regexp"
2019

2120
"buf.build/go/standard/xslices"
@@ -24,8 +23,7 @@ import (
2423
)
2524

2625
const (
27-
webpagesConfigFlagName = "config"
28-
includeFrontMatterFlagName = "include-front-matter"
26+
webpagesConfigFlagName = "config"
2927

3028
indexFileName = "index.md"
3129
markdownFileExtension = ".md"
@@ -40,13 +38,7 @@ func NewWebpagesCommand(name string, cobraCommand *cobra.Command) *cobra.Command
4038
Use: name,
4139
Hidden: true,
4240
Short: "Generate markdown files for CLI reference documentation.",
43-
Long: fmt.Sprintf(`Generate markdown files for CLI reference documentation.
44-
45-
By default, this generates markdown pages with the command name as a H1 title. For markdown
46-
files with Docusaurus compatible front matter, use --%s flag.`,
47-
includeFrontMatterFlagName,
48-
),
49-
Args: cobra.NoArgs,
41+
Args: cobra.NoArgs,
5042
RunE: func(command *cobra.Command, _ []string) error {
5143
return run(command.Flags(), cobraCommand)
5244
},
@@ -56,11 +48,6 @@ files with Docusaurus compatible front matter, use --%s flag.`,
5648
"",
5749
"Path to config file to use",
5850
)
59-
webpagesCommand.Flags().Bool(
60-
includeFrontMatterFlagName,
61-
false,
62-
"Include Docusaurus compatible front matter in generated markdown.",
63-
)
6451
return webpagesCommand
6552
}
6653

@@ -83,14 +70,9 @@ func run(
8370
command.Hidden = true
8471
}
8572
}
86-
includeFrontMatter, err := flags.GetBool(includeFrontMatterFlagName)
87-
if err != nil {
88-
return err
89-
}
9073
return generateMarkdownTree(
9174
cobraCommand,
9275
config,
9376
config.OutputDir,
94-
includeFrontMatter,
9577
)
9678
}

private/bufpkg/bufcobra/markdown.go

Lines changed: 12 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import (
2121
"html"
2222
"io"
2323
"os"
24-
"path"
2524
"path/filepath"
2625
"sort"
2726
"strings"
@@ -36,7 +35,6 @@ func generateMarkdownTree(
3635
command *cobra.Command,
3736
config *config,
3837
parentDirPath string,
39-
includeFrontMatter bool,
4038
) error {
4139
if !command.IsAvailableCommand() {
4240
return nil
@@ -58,14 +56,14 @@ func generateMarkdownTree(
5856
return err
5957
}
6058
defer file.Close()
61-
if err := generateMarkdownPage(command, config, file, includeFrontMatter); err != nil {
59+
if err := generateMarkdownPage(command, config, file); err != nil {
6260
return err
6361
}
6462
if command.HasSubCommands() {
6563
commands := command.Commands()
6664
orderCommands(config.WeightCommands, commands)
6765
for _, command := range commands {
68-
if err := generateMarkdownTree(command, config, dirPath, includeFrontMatter); err != nil {
66+
if err := generateMarkdownTree(command, config, dirPath); err != nil {
6967
return err
7068
}
7169
}
@@ -78,29 +76,21 @@ func generateMarkdownPage(
7876
command *cobra.Command,
7977
config *config,
8078
writer io.Writer,
81-
includeFrontMatter bool,
8279
) error {
8380
var err error
8481
p := func(format string, a ...any) {
8582
_, err = fmt.Fprintf(writer, format, a...)
8683
}
87-
if includeFrontMatter {
88-
p("---\n")
89-
p("id: %s\n", websitePageIDForCommand(command))
90-
p("title: %s\n", command.CommandPath())
91-
p("sidebar_label: %s\n", sidebarLabelForCommand(command, config.SidebarPathThreshold))
92-
p("sidebar_position: %d\n", websiteSidebarPosition(command, config.WeightCommands))
93-
p("slug: /%s\n", path.Join(config.SlugPrefix, websiteSlugForCommand(command)))
94-
p("---\n")
95-
} else {
96-
p("# %s\n", command.CommandPath())
97-
}
84+
p("---\n")
85+
p("title: %s\n", command.CommandPath())
86+
p("---\n")
87+
p("# %s\n", command.CommandPath())
9888
command.InitDefaultHelpCmd()
9989
command.InitDefaultHelpFlag()
10090
if command.Version != "" {
10191
p("version `%s`\n\n", command.Version)
10292
}
103-
p("%s\n\n", command.Short)
93+
p("%s\n\n", html.EscapeString(command.Short))
10494
if command.Runnable() {
10595
p("### Usage\n")
10696
p("```console\n$ %s\n```\n\n", command.UseLine())
@@ -139,7 +129,7 @@ func generateMarkdownPage(
139129
if child.HasSubCommands() {
140130
childRelPath = filepath.Join(child.Name(), indexFileName)
141131
}
142-
p("* [%s](./%s)\t - %s\n", child.CommandPath(), childRelPath, child.Short)
132+
p("* [%s](./%s)\t - %s\n", child.CommandPath(), childRelPath, html.EscapeString(child.Short))
143133
}
144134
p("\n")
145135
}
@@ -150,11 +140,11 @@ func generateMarkdownPage(
150140
if hasSubCommands(command) {
151141
// If the current command has sub-commands, the parent command is the index file in
152142
// the parent directory.
153-
p("* [%s](../%s)\t - %s\n", parentName, indexFileName, parent.Short)
143+
p("* [%s](../%s)\t - %s\n", parentName, indexFileName, html.EscapeString(parent.Short))
154144
} else {
155145
// If the current command is a leaf command, the parent command is the index file in
156146
// the current directory.
157-
p("* [%s](./%s)\t - %s\n", parentName, indexFileName, parent.Short)
147+
p("* [%s](./%s)\t - %s\n", parentName, indexFileName, html.EscapeString(parent.Short))
158148
}
159149
command.VisitParents(func(c *cobra.Command) {
160150
if c.DisableAutoGenTag {
@@ -165,10 +155,6 @@ func generateMarkdownPage(
165155
return err
166156
}
167157

168-
func websitePageIDForCommand(cmd *cobra.Command) string {
169-
return strings.ReplaceAll(cmd.CommandPath(), " ", "-")
170-
}
171-
172158
// hasSubCommands checks for whether a command has available sub-commands, not including help.
173159
func hasSubCommands(cmd *cobra.Command) bool {
174160
for _, command := range cmd.Commands() {
@@ -257,11 +243,11 @@ func writeFlags(f *pflag.FlagSet, writer io.Writer) error {
257243
}
258244
varname, usage := pflag.UnquoteUsage(flag)
259245
if varname != "" {
260-
p(" *%s*", varname)
246+
p(" *%s*", html.EscapeString(varname))
261247
}
262248
p(" {#%s}", flag.Name)
263249
p("\n")
264-
p("%s", usage)
250+
p("%s", html.EscapeString(usage))
265251
if flag.NoOptDefVal != "" {
266252
switch flag.Value.Type() {
267253
case "string":
@@ -285,39 +271,3 @@ func writeFlags(f *pflag.FlagSet, writer io.Writer) error {
285271
})
286272
return err
287273
}
288-
289-
// websiteSidebarPosition calculates the position of the given command in the website sidebar.
290-
func websiteSidebarPosition(cmd *cobra.Command, weights map[string]int) int {
291-
// Return 0 if the command has no parent
292-
if !cmd.HasParent() {
293-
return 0
294-
}
295-
siblings := cmd.Parent().Commands()
296-
orderCommands(weights, siblings)
297-
position := 0
298-
for _, sibling := range siblings {
299-
if isCommandVisible(sibling) {
300-
position++
301-
if sibling.CommandPath() == cmd.CommandPath() {
302-
return position
303-
}
304-
}
305-
}
306-
return -1
307-
}
308-
309-
// isCommandVisible checks if a command is visible (available, not an additional help topic, and not hidden).
310-
func isCommandVisible(command *cobra.Command) bool {
311-
return command.IsAvailableCommand() && !command.IsAdditionalHelpTopicCommand() && !command.Hidden
312-
}
313-
314-
func websiteSlugForCommand(command *cobra.Command) string {
315-
return strings.ReplaceAll(command.CommandPath(), " ", "/")
316-
}
317-
318-
func sidebarLabelForCommand(command *cobra.Command, maxSidebarLen int) string {
319-
if len(strings.Split(command.CommandPath(), " ")) > maxSidebarLen {
320-
return command.Name()
321-
}
322-
return command.CommandPath()
323-
}

0 commit comments

Comments
 (0)