diff --git a/doc/man_docs.go b/doc/man_docs.go index 560bc20c7..b0788310b 100644 --- a/doc/man_docs.go +++ b/doc/man_docs.go @@ -140,6 +140,14 @@ func fillHeader(header *GenManHeader, name string, disableAutoGen bool) error { return nil } +// escapeAngleBrackets escapes < and > characters to prevent md2man from +// interpreting them as HTML tags. See https://github.com/spf13/cobra/issues/2330 +func escapeAngleBrackets(s string) string { + s = strings.ReplaceAll(s, "<", `\<`) + s = strings.ReplaceAll(s, ">", `\>`) + return s +} + func manPreamble(buf io.StringWriter, header *GenManHeader, cmd *cobra.Command, dashedName string) { description := cmd.Long if len(description) == 0 { @@ -149,11 +157,11 @@ func manPreamble(buf io.StringWriter, header *GenManHeader, cmd *cobra.Command, cobra.WriteStringAndCheck(buf, fmt.Sprintf(`%% "%s" "%s" "%s" "%s" "%s" # NAME `, header.Title, header.Section, header.date, header.Source, header.Manual)) - cobra.WriteStringAndCheck(buf, fmt.Sprintf("%s \\- %s\n\n", dashedName, cmd.Short)) + cobra.WriteStringAndCheck(buf, fmt.Sprintf("%s \\- %s\n\n", dashedName, escapeAngleBrackets(cmd.Short))) cobra.WriteStringAndCheck(buf, "# SYNOPSIS\n") - cobra.WriteStringAndCheck(buf, fmt.Sprintf("**%s**\n\n", cmd.UseLine())) + cobra.WriteStringAndCheck(buf, fmt.Sprintf("**%s**\n\n", escapeAngleBrackets(cmd.UseLine()))) cobra.WriteStringAndCheck(buf, "# DESCRIPTION\n") - cobra.WriteStringAndCheck(buf, description+"\n\n") + cobra.WriteStringAndCheck(buf, escapeAngleBrackets(description)+"\n\n") } func manPrintFlags(buf io.StringWriter, flags *pflag.FlagSet) { @@ -180,7 +188,7 @@ func manPrintFlags(buf io.StringWriter, flags *pflag.FlagSet) { format += "]" } format += "\n\t%s\n\n" - cobra.WriteStringAndCheck(buf, fmt.Sprintf(format, flag.DefValue, flag.Usage)) + cobra.WriteStringAndCheck(buf, fmt.Sprintf(format, flag.DefValue, escapeAngleBrackets(flag.Usage))) }) } diff --git a/doc/man_docs_test.go b/doc/man_docs_test.go index ae6c8e533..8b6baf8f7 100644 --- a/doc/man_docs_test.go +++ b/doc/man_docs_test.go @@ -214,6 +214,31 @@ func assertNextLineEquals(scanner *bufio.Scanner, expectedLine string) error { return fmt.Errorf("hit EOF before finding %v", expectedLine) } +func TestGenManAngleBrackets(t *testing.T) { + // Test that angle brackets are preserved in man page output. + // See https://github.com/spf13/cobra/issues/2330 + cmd := &cobra.Command{ + Use: "cmd [:]", + Short: "A command with brackets", + Long: "This is a longer description with values.", + Run: emptyRun, + } + cmd.Flags().StringP("config", "c", "", "Path to file") + + buf := new(bytes.Buffer) + if err := GenMan(cmd, nil, buf); err != nil { + t.Fatal(err) + } + output := buf.String() + + // Verify angle brackets are preserved in the rendered man page + checkStringContains(t, output, "") + checkStringContains(t, output, "") + checkStringContains(t, output, "") + checkStringContains(t, output, "") + checkStringContains(t, output, "") +} + func BenchmarkGenManToFile(b *testing.B) { file, err := os.CreateTemp("", "") if err != nil {