commit a5169896c9f629389d4effce43b784aa90204dcb from: Oliver Lowe date: Mon Mar 17 07:30:57 2025 UTC cmd/Jira: post comments using godoc comments (markdown subset) Rather than dealing with Atlassian's bespoke markup language. Version 3 of the Jira API does not even support that markup language! commit - 4bcb477f41800862d0a7c265b40dfccf97180dcc commit + a5169896c9f629389d4effce43b784aa90204dcb blob - 3ee0fe29e984c743a573219152d6c86e8d380abf blob + 9bdacfefd2524571c7fa026b8340643500b1b0de --- cmd/Jira/Jira.go +++ cmd/Jira/Jira.go @@ -1,7 +1,6 @@ package main import ( - "bytes" "errors" "flag" "fmt" @@ -197,7 +196,8 @@ func (w *awin) postComment() error { } elems := strings.Split(w.name(), "/") ikey := fmt.Sprintf("%s-%s", elems[0], elems[1]) - return f.Client.PostComment(ikey, bytes.NewReader(body)) + jtf := toJTF(string(body)) + return f.Client.PostComment(ikey, strings.NewReader(jtf)) } func newSearch(fsys fs.FS, query string) { blob - /dev/null blob + 11b174e2b5625023437a253062b6e96cf6111641 (mode 644) --- /dev/null +++ cmd/Jira/comment.jtf @@ -0,0 +1,20 @@ +h3. My comment + +{quote}Hello, world! this is a test{quote} + +A bit confused here. The code says something but I'm not sure that's so important. + +The reality is that it says this: + +{noformat}echo hello world! +{noformat} + +So it's not upper case according to [RFC 7666|http://example.com]. + +h3. Why this *matters* + +Because youse are all fucked m8. But to keep it short, without worrying about fmt.Println: + +* Who is your Daddy? +* And what does he do? +* Show me the money \ No newline at end of file blob - /dev/null blob + 9531ebe77170bfbb77267fda48ee20414ca6eeba (mode 644) --- /dev/null +++ cmd/Jira/comment.md @@ -0,0 +1,24 @@ +# My comment + +> Hello, world! +> this is a test + +A bit confused here. +The code says something but I'm not sure that's so important. + +The reality is that it says this: + + echo hello world! + +So it's not upper case according to [RFC 7666]. + +# Why this *matters* + +Because youse are all fucked m8. +But to keep it short, without worrying about [fmt.Println]: + + * Who is your Daddy? + * And what does he do? + * Show me the money + +[RFC 7666]: http://example.com blob - /dev/null blob + c813b41259ff09d03f1d9ebe7603203237b724fe (mode 644) --- /dev/null +++ cmd/Jira/jtf.go @@ -0,0 +1,77 @@ +package main + +import ( + "fmt" + "go/doc/comment" + "strings" +) + +// https://jira.atlassian.com/secure/WikiRendererHelpAction.jspa?section=all + +func toJTF(content string) string { + var p comment.Parser + doc := p.Parse(content) + buf := &strings.Builder{} + for _, block := range doc.Content { + switch v := block.(type) { + case *comment.Heading: + fmt.Fprintf(buf, "h3. %s\n", render(v.Text)) + case *comment.Paragraph: + fmt.Fprintln(buf, render(v.Text)) + case *comment.Code: + fmt.Fprintf(buf, "{noformat}%s{noformat}\n", v.Text) + case *comment.List: + fmt.Fprintln(buf, renderList(v)) + } + fmt.Fprintln(buf) + } + return strings.TrimSpace(buf.String()) +} + +func renderList(list *comment.List) string { + buf := &strings.Builder{} + prefix := "*" + for _, it := range list.Items { + if it.Number != "" { + prefix = "#" + } + for _, block := range it.Content { + // the block is known to be a paragraph + s := render(block.(*comment.Paragraph).Text) + fmt.Fprintln(buf, prefix, s) + } + } + return buf.String() +} + +func render(text []comment.Text) string { + buf := &strings.Builder{} + for _, txt := range text { + switch v := txt.(type) { + case comment.Plain: + s := strings.ReplaceAll(string(v), "\n", " ") + if strings.HasPrefix(s, "> ") { + s = strings.ReplaceAll(s, "> ", "") + fmt.Fprintf(buf, "{quote}%s{quote}", s) + } else { + buf.WriteString(s) + } + case comment.Italic: + fmt.Fprintf(buf, "*%s*", v) + case *comment.Link: + if v.Auto { + fmt.Fprintf(buf, "[%s]", v.URL) + } else { + title := render(v.Text) + fmt.Fprintf(buf, "[%s|%s]", title, v.URL) + } + case *comment.DocLink: + // we're not actually printing godoc, so treat + // any accidental DocLink as plain text. + buf.WriteString(render(v.Text)) + default: + fmt.Fprintf(buf, "%v", v) + } + } + return buf.String() +} blob - /dev/null blob + a666ea56f0c25005db773b0d9d1225ac7e10721c (mode 644) --- /dev/null +++ cmd/Jira/jtf_test.go @@ -0,0 +1,26 @@ +package main + +import ( + "hash/crc32" + "os" + "testing" +) + +func TestJTF(t *testing.T) { + b, err := os.ReadFile("comment.jtf") + if err != nil { + t.Fatal(err) + } + want := crc32.ChecksumIEEE(b) + + b, err = os.ReadFile("comment.md") + if err != nil { + t.Fatal(err) + } + rendered := toJTF(string(b)) + got := crc32.ChecksumIEEE([]byte(rendered)) + if want != got { + t.Errorf("unexepected content in rendered comment") + t.Logf("%s\n", string(rendered)) + } +}