commit a5169896c9f629389d4effce43b784aa90204dcb
parent 4bcb477f41800862d0a7c265b40dfccf97180dcc
Author: Oliver Lowe <o@olowe.co>
Date: Mon, 17 Mar 2025 18:30:57 +1100
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!
Diffstat:
5 files changed, 150 insertions(+), 2 deletions(-)
diff --git a/cmd/Jira/Jira.go b/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) {
diff --git a/cmd/Jira/comment.jtf b/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
diff --git a/cmd/Jira/comment.md b/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
diff --git a/cmd/Jira/jtf.go b/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()
+}
diff --git a/cmd/Jira/jtf_test.go b/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))
+ }
+}