😎

commitコメントやissue、Wikiでも代替レンダラーを使えるように GitBucket を修正してプルリクした件

に公開

前回の記事

https://zenn.dev/yasumichi/articles/c3df6dde7f6838

概要

GitBucketのプラグインGitBucket Markdown Enhanced Pluginを開発中のところ、Wiki・Issues・コメントでも拡張Markdownを有効にしたいという要望を受けました。

調査したところ、GitBucket本体の改修が必要なことが判明したため、改修を行い、プルリクエストを行った記録です。

手始めに Wiki で代替レンダラーを使用できるように修正

src/main/twirl/gitbucket/core/wiki/page.scala.htmlを修正しました。

diff --git a/src/main/twirl/gitbucket/core/wiki/page.scala.html b/src/main/twirl/gitbucket/core/wiki/page.scala.html
index 855856ce..05a54085 100644
--- a/src/main/twirl/gitbucket/core/wiki/page.scala.html
+++ b/src/main/twirl/gitbucket/core/wiki/page.scala.html
@@ -82,16 +82,14 @@
     </div>
     <div class="wiki-main">
       <div class="markdown-body">
-        @helpers.markdown(
-          markdown = page.content,
-          repository = repository,
+        @helpers.renderMarkup(
+          filePath = page.name.split("/").toList,
+          fileContent = page.content,
           branch = "master",
+          repository = repository,
           enableWikiLink = true,
           enableRefsLink = false,
-          enableLineBreaks = false,
-          enableTaskList = false,
-          hasWritePermission = false,
-          pages = pages
+          enableAnchor = true
         )
       </div>
       @footer.map { footerPage =>

これで下の画像のように Wiki でも GitBucket Markdown Enhanced Plugin が使用され、PlantUML による図が表示できました。

しかしながら、編集モードにしてプレビューを行うとうまくいきません。

編集中のプレビューでも代替レンダラーを使用できるように修正

src/main/twirl/gitbucket/core/helper/preview.scala.htmlを修正しました。

diff --git a/src/main/twirl/gitbucket/core/helper/preview.scala.html b/src/main/twirl/gitbucket/core/helper/preview.scala.html
index 1276c68e..6a2cb0fc 100644
--- a/src/main/twirl/gitbucket/core/helper/preview.scala.html
+++ b/src/main/twirl/gitbucket/core/helper/preview.scala.html
@@ -57,6 +57,7 @@ $(function(){
   $('#preview@uid').click(function(){
     $('#preview-area@uid').html('<img src="@helpers.assets("/common/images/indicator.gif")"> Previewing...');
     $.post('@helpers.url(repository)/_preview', {
+      filename         : "temporary.md",
       content          : $('#content@uid').val(),
       enableWikiLink   : @enableWikiLink,
       enableRefsLink   : @enableRefsLink,

これでプレビューにもGitBucket Markdown Enhanced Plugin が使用されるようになりました。

Issue でも代替レンダラーを使用できるように修正

src/main/twirl/gitbucket/core/issues/commentlist.scala.html

diff --git a/src/main/twirl/gitbucket/core/issues/commentlist.scala.html b/src/main/twirl/gitbucket/core/issues/commentlist.scala.html
index a0240e9f..a99cf3bf 100644
--- a/src/main/twirl/gitbucket/core/issues/commentlist.scala.html
+++ b/src/main/twirl/gitbucket/core/issues/commentlist.scala.html
@@ -32,15 +32,14 @@
     }
   </div>
   <div class="panel-body markdown-body" id="commentContent-@comment.commentId">
-    @helpers.markdown(
-      markdown           = comment.content,
-      repository         = repository,
+    @helpers.renderMarkup(
+      filePath           = List("temporary.md"),
+      fileContent        = comment.content,
       branch             = repository.repository.defaultBranch,
+      repository         = repository,
       enableWikiLink     = false,
       enableRefsLink     = true,
-      enableLineBreaks   = true,
-      enableTaskList     = true,
-      hasWritePermission = isManageable
+      enableAnchor       = true
     )
   </div>
 </div>
@@ -58,15 +57,14 @@
       </span>
     </div>
     <div class="panel-body markdown-body" id="issueContent">
-      @helpers.markdown(
-        markdown           = issue.get.content getOrElse "No description provided.",
-        repository         = repository,
+      @helpers.renderMarkup(
+        filePath           = List("temporary.md"),
+        fileContent        = issue.get.content getOrElse "No description provided.",
         branch             = repository.repository.defaultBranch,
+        repository         = repository,
         enableWikiLink     = false,
         enableRefsLink     = true,
-        enableLineBreaks   = true,
-        enableTaskList     = true,
-        hasWritePermission = isManageable
+        enableAnchor       = true
       )
     </div>
   </div>
@@ -331,15 +329,14 @@
         }
       </div>
       <div class="panel-body markdown-body commit-commentContent-@comment.commentId">
-        @helpers.markdown(
-          markdown           = comment.content,
-          repository         = repository,
+        @helpers.renderMarkup(
+          filePath           = List("temporary.md"),
+          fileContent        = comment.content,
           branch             = repository.repository.defaultBranch,
+          repository         = repository,
           enableWikiLink     = false,
           enableRefsLink     = true,
-          enableLineBreaks   = true,
-          enableTaskList     = true,
-          hasWritePermission = isManageable
+          enableAnchor       = true
         )
       </div>
     </div>

これで issue にも GitBucket Markdown Enhanced Plugin が使用されるようになりました。

しかし、コメントを編集し更新するとうまくいきません。

改善するにはコントローラーの改修が必要でした。

IssuesController の修正

src/main/scala/gitbucket/core/controller/IssuesController.scalaを修正しました。

diff --git a/src/main/scala/gitbucket/core/controller/IssuesController.scala b/src/main/scala/gitbucket/core/controller/IssuesController.scala
index a4b04180..676a7684 100644
--- a/src/main/scala/gitbucket/core/controller/IssuesController.scala
+++ b/src/main/scala/gitbucket/core/controller/IssuesController.scala
@@ -8,6 +8,7 @@ import gitbucket.core.util.Implicits.*
 import gitbucket.core.util.*
 import gitbucket.core.view
 import gitbucket.core.view.Markdown
+import gitbucket.core.view.helpers
 import org.scalatra.forms.*
 import org.scalatra.{BadRequest, Ok}

@@ -274,17 +275,15 @@ trait IssuesControllerBase extends ControllerBase {
             org.json4s.jackson.Serialization.write(
               Map(
                 "title" -> x.title,
-                "content" -> Markdown.toHtml(
-                  markdown = x.content getOrElse "No description given.",
-                  repository = repository,
+                "content" -> helpers.renderMarkup(
+                  filePath = List("temporary.md"),
+                  fileContent = x.content getOrElse "No description given.",
                   branch = repository.repository.defaultBranch,
+                  repository = repository,
                   enableWikiLink = false,
                   enableRefsLink = true,
-                  enableAnchor = true,
-                  enableLineBreaks = true,
-                  enableTaskList = true,
-                  hasWritePermission = true
-                )
+                  enableAnchor = true
+                ).toString()
               )
             )
           }
@@ -303,17 +302,15 @@ trait IssuesControllerBase extends ControllerBase {
             contentType = formats("json")
             org.json4s.jackson.Serialization.write(
               Map(
-                "content" -> view.Markdown.toHtml(
-                  markdown = x.content,
-                  repository = repository,
+                "content" -> helpers.renderMarkup(
+                  filePath = List("temporary.md"),
+                  fileContent = x.content,
                   branch = repository.repository.defaultBranch,
+                  repository = repository,
                   enableWikiLink = false,
                   enableRefsLink = true,
-                  enableAnchor = true,
-                  enableLineBreaks = true,
-                  enableTaskList = true,
-                  hasWritePermission = true
-                )
+                  enableAnchor = true
+                ).toString()
               )
             )
           }

これで前項の問題が解消しました。

なお、helpers.renderMarkup() の結果を toString() メソッドで文字列に変換してから json4s に渡さないと java.lang.StackOverflowError が発生し、Internal Server Error となってしまいました。

commitコメントでも代替レンダラーを使用できるように修正

順番が前後していますが…

src/main/twirl/gitbucket/core/helper/commitcomment.scala.htmlを修正しました。

diff --git a/src/main/twirl/gitbucket/core/helper/commitcomment.scala.html b/src/main/twirl/gitbucket/core/helper/commitcomment.scala.html
index c0542ee6..0d097c95 100644
--- a/src/main/twirl/gitbucket/core/helper/commitcomment.scala.html
+++ b/src/main/twirl/gitbucket/core/helper/commitcomment.scala.html
@@ -21,15 +21,14 @@
         </span>
       </div>
       <div class="commit-commentContent-@comment.commentId">
-        @helpers.markdown(
-          markdown           = comment.content,
-          repository         = repository,
+        @helpers.renderMarkup(
+          filePath           = List("temporary.md"),
+          fileContent        = comment.content,
           branch             = repository.repository.defaultBranch,
+          repository         = repository,
           enableWikiLink     = false,
           enableRefsLink     = true,
-          enableLineBreaks   = true,
-          enableTaskList     = true,
-          hasWritePermission = hasWritePermission
+          enableAnchor       = true
         )
       </div>
     </div>

これでコミットコメントでも GitBucket Markdown Enhanced Plugin が使用されるようになりました。

ここまでの修正では既存の機能を壊してしまうことが判明

helpers.markdown() の呼び出しに使用していた hasWritePermission にあたるパラメーターが、helpers.renderMarkup() にはないことが気になっていたので調査したところ、既存の機能では、タスクリストのチェックボックスが、編集権限があると切り替え可能だったのですが、ここまでの修正ではその機能を殺してしまいました。

最終的にコミットを整理し、修正しなおしたのが以下のコミットです。

差分は長くなるので割愛しますが、編集権限がある場合は、<input type="checkbox"> の disabled 属性を削除するように修正しました。

            val re = "disabled[^<>]*>".r
            var content = helpers.renderMarkup(
                  filePath = List("temporary.md"),
                  fileContent = x.content getOrElse "No description given.",
                  branch = repository.repository.defaultBranch,
                  repository = repository,
                  enableWikiLink = false,
                  enableRefsLink = true,
                  enableAnchor = true
            ).toString()
            content = re.replaceAllIn(content, ">")

本家の MarkdownRenderer と GitBucket Markdown Enhanced Plugin では、微妙に生成する HTML が異なるので正規表現が微妙ですけど…

このコミットで最終的なプルリクエストを発行しました。

採用してもらえると良いのですが…

後日談(追記)

Copilot からの指摘

  • if~elseの分岐で重複コードが存在
  • twirl で非推奨のコードブロック @{ var commentContent = ... } が存在
    • 処理の共通化を示唆(たけぞうさん見解)

整形の必要性

たけぞうさんから、CI で書式に関するエラーが出ているので以下のコマンドを実行するようにとコメントを受けました。

sbt scalafmtAll

新たなプルリクエスト

追加事項

  • 編集権限がある場合にチェックボックスを有効化する処理を共通化(helpers.enableCheckbox()を定義)
    • 正規表現も見直し
    • 結果として分岐が減少
  /**
    * Utility method to enable checkboxes
    */
  def enableCheckbox(html: Html, enable: Boolean): Html = {
    if (enable) {
      val re = "(<input\\s+[^<>]*type=\"checkbox\"\\s+[^<>]*)\\s+disabled[^<>]*>".r
      Html(re.replaceAllIn(html.toString(), "$1>"))
    } else {
      html
    }
  }
GitHubで編集を提案

Discussion