🦁

gitbucket-commitgraphs-plugin を修正しようとしたらハマった話

に公開2件

概要

GitBucket のプラグイン gitbucket-commitgraphs-plugin は、開発者のコミット状況をグラフで表示するプラグインです。

グラフの描画には、Google Charts を利用しているため、インターネットに接続できないクローズド環境では、使用できません。

あまり需要はないかもしれませんが、個人的にクローズド環境に対応させたく、修正を試みたのですが、謎の依存関係にハマってしまったというお話です。

前回の記事

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

最終的な成果

本当は、本家にコントリビュートできるようにしたかったのですが、後述の事情により、GitBucket Commit Graphs Chart.js Plugin という別のリポジトリを作成しました。

バージョン v0.1.0 としてリリースしましたのでお試しいただけますと幸いです。

Google Charts の代わりに chart.js を同梱し、利用するようにしました。

オリジナル版では、以下のように表示されていました。

改造版では、以下のように表示されます。

なお、日付の表示は、ブラウザの言語設定により変わります。

謎の依存関係

gitbucket-commitgraphs-plugin を Fork し、clone してビルドしようとしたら、以下のエラーに遭遇しました。

[warn]  Note: Unresolved dependencies path:
[error] sbt.librarymanagement.ResolveException: Error downloading com.github.bkromhout:java-diff-utils:2.1.1
[error]   Not found
[error]   Not found
[error]   not found: C:\Users\username\.ivy2\local\com.github.bkromhout\java-diff-utils\2.1.1\ivys\ivy.xml
[error]   not found: https://jcenter.bintray.com/com/github/bkromhout/java-diff-utils/2.1.1/java-diff-utils-2.1.1.pom
[error]   not found: https://repo1.maven.org/maven2/com/github/bkromhout/java-diff-utils/2.1.1/java-diff-utils-2.1.1.pom
[error]   not found: https://dl.bintray.com/bkromhout/maven/com/github/bkromhout/java-diff-utils/2.1.1/java-diff-utils-2.1.1.pom
[error]         at lmcoursier.CoursierDependencyResolution.unresolvedWarningOrThrow(CoursierDependencyResolution.scala:258)
[error]         at lmcoursier.CoursierDependencyResolution.$anonfun$update$38(CoursierDependencyResolution.scala:227)
[error]         at scala.util.Either$LeftProjection.map(Either.scala:573)
[error]         at lmcoursier.CoursierDependencyResolution.update(CoursierDependencyResolution.scala:227)
[error]         at sbt.librarymanagement.DependencyResolution.update(DependencyResolution.scala:60)
[error]         at sbt.internal.LibraryManagement$.resolve$1(LibraryManagement.scala:53)
[error]         at sbt.internal.LibraryManagement$.$anonfun$cachedUpdate$12(LibraryManagement.scala:103)
[error]         at sbt.util.Tracked$.$anonfun$lastOutput$1(Tracked.scala:73)
[error]         at sbt.internal.LibraryManagement$.$anonfun$cachedUpdate$20(LibraryManagement.scala:116)
[error]         at scala.util.control.Exception$Catch.apply(Exception.scala:228)
[error]         at sbt.internal.LibraryManagement$.$anonfun$cachedUpdate$11(LibraryManagement.scala:116)
[error]         at sbt.internal.LibraryManagement$.$anonfun$cachedUpdate$11$adapted(LibraryManagement.scala:97)
[error]         at sbt.util.Tracked$.$anonfun$inputChangedW$1(Tracked.scala:219)
[error]         at sbt.internal.LibraryManagement$.cachedUpdate(LibraryManagement.scala:130)
[error]         at sbt.Classpaths$.$anonfun$updateTask0$5(Defaults.scala:3485)
[error]         at scala.Function1.$anonfun$compose$1(Function1.scala:49)
[error]         at sbt.internal.util.$tilde$greater.$anonfun$$u2219$1(TypeFunctions.scala:62)
[error]         at sbt.std.Transform$$anon$4.work(Transform.scala:68)
[error]         at sbt.Execute.$anonfun$submit$2(Execute.scala:282)
[error]         at sbt.internal.util.ErrorHandling$.wideConvert(ErrorHandling.scala:23)
[error]         at sbt.Execute.work(Execute.scala:291)
[error]         at sbt.Execute.$anonfun$submit$1(Execute.scala:282)
[error]         at sbt.ConcurrentRestrictions$$anon$4.$anonfun$submitValid$1(ConcurrentRestrictions.scala:265)
[error]         at sbt.CompletionService$$anon$2.call(CompletionService.scala:64)
[error]         at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
[error]         at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539)
[error]         at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
[error]         at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
[error]         at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
[error]         at java.base/java.lang.Thread.run(Thread.java:840)
[error] (update) sbt.librarymanagement.ResolveException: Error downloading com.github.bkromhout:java-diff-utils:2.1.1
[error]   Not found
[error]   Not found
[error]   not found: C:\Users\username\.ivy2\local\com.github.bkromhout\java-diff-utils\2.1.1\ivys\ivy.xml
[error]   not found: https://jcenter.bintray.com/com/github/bkromhout/java-diff-utils/2.1.1/java-diff-utils-2.1.1.pom
[error]   not found: https://repo1.maven.org/maven2/com/github/bkromhout/java-diff-utils/2.1.1/java-diff-utils-2.1.1.pom
[error]   not found: https://dl.bintray.com/bkromhout/maven/com/github/bkromhout/java-diff-utils/2.1.1/java-diff-utils-2.1.1.pom

java-diff-utils の groupId が変わってしまったのでダウンロードできないのだと思いますが、そもそもなぜ java-diff-utils への依存を拾ってくるのから、見当がつきません。

java-diff-utils という文字列でプロジェクトないを検索してみましたが、当該ログが引っかかるのみです。

依存関係ツリーを表示してみる

sbt dependencyTree で依存関係を調べてみました。

>sbt dependencyTree
WARNING: A terminally deprecated method in java.lang.System has been called
WARNING: System::setSecurityManager has been called by sbt.TrapExit$ (file:/C:/Users/username/.sbt/boot/scala-2.12.12/org.scala-sbt/sbt/1.4.4/run_2.12-1.4.4.jar)
WARNING: Please consider reporting this to the maintainers of sbt.TrapExit$
WARNING: System::setSecurityManager will be removed in a future release
[info] welcome to sbt 1.4.4 (Eclipse Adoptium Java 17.0.15)
[info] loading settings for project gitbucket-commitgraphs-plugin-build from plugins.sbt ...
[info] loading project definition from C:\Users\username\gitbucket-commitgraphs-plugin\project
[info] loading settings for project root from build.sbt ...
[info] set current project to gitbucket-commitgraphs-plugin (in build file:/C:/Users/username/gitbucket-commitgraphs-plugin/)
[info] me.huzi.gitbucket:gitbucket-commitgraphs-plugin_2.13:4.35.0 [S]
[info]   +-com.typesafe.play:twirl-api_2.13:1.4.2 [S]
[info]     +-org.scala-lang.modules:scala-xml_2.13:1.2.0 [S]
[info]
[success] Total time: 13 s, completed 2025/12/30 9:07:09

やはり、java-diff-utils は見つかりません。

スコープを変えて sbt runtime:dependencyTree を実行してみます。

>sbt runtime:dependencyTree
WARNING: A terminally deprecated method in java.lang.System has been called
WARNING: System::setSecurityManager has been called by sbt.TrapExit$ (file:/C:/Users/username/.sbt/boot/scala-2.12.12/org.scala-sbt/sbt/1.4.4/run_2.12-1.4.4.jar)
WARNING: Please consider reporting this to the maintainers of sbt.TrapExit$
WARNING: System::setSecurityManager will be removed in a future release
[info] welcome to sbt 1.4.4 (Eclipse Adoptium Java 17.0.15)
[info] loading settings for project gitbucket-commitgraphs-plugin-build from plugins.sbt ...
[info] loading project definition from C:\Users\username\gitbucket-commitgraphs-plugin\project
[info] loading settings for project root from build.sbt ...
[info] set current project to gitbucket-commitgraphs-plugin (in build file:/C:/Users/username/gitbucket-commitgraphs-plugin/)
[info] me.huzi.gitbucket:gitbucket-commitgraphs-plugin_2.13:4.35.0 [S]
[info]   +-com.typesafe.play:twirl-api_2.13:1.4.2 [S]
[info]     +-org.scala-lang.modules:scala-xml_2.13:1.2.0 [S]
[info]
[success] Total time: 0 s, completed 2025/12/30 9:08:05

さらに sbt test:dependencyTree を実行してみます。

>sbt test:dependencyTree
WARNING: A terminally deprecated method in java.lang.System has been called
WARNING: System::setSecurityManager has been called by sbt.TrapExit$ (file:/C:/Users/username/.sbt/boot/scala-2.12.12/org.scala-sbt/sbt/1.4.4/run_2.12-1.4.4.jar)
WARNING: Please consider reporting this to the maintainers of sbt.TrapExit$
WARNING: System::setSecurityManager will be removed in a future release
[info] welcome to sbt 1.4.4 (Eclipse Adoptium Java 17.0.15)
[info] loading settings for project gitbucket-commitgraphs-plugin-build from plugins.sbt ...
[info] loading project definition from C:\Users\username\gitbucket-commitgraphs-plugin\project
[info] loading settings for project root from build.sbt ...
[info] set current project to gitbucket-commitgraphs-plugin (in build file:/C:/Users/username/gitbucket-commitgraphs-plugin/)
[info] me.huzi.gitbucket:gitbucket-commitgraphs-plugin_2.13:4.35.0 [S]
[info]   +-com.typesafe.play:twirl-api_2.13:1.4.2 [S]
[info]     +-org.scala-lang.modules:scala-xml_2.13:1.2.0 [S]
[info]
[success] Total time: 0 s, completed 2025/12/30 9:08:38

sbt compile:dependencyTree でも状況は変わりません。

>sbt compile:dependencyTree
WARNING: A terminally deprecated method in java.lang.System has been called
WARNING: System::setSecurityManager has been called by sbt.TrapExit$ (file:/C:/Users/username/.sbt/boot/scala-2.12.12/org.scala-sbt/sbt/1.4.4/run_2.12-1.4.4.jar)
WARNING: Please consider reporting this to the maintainers of sbt.TrapExit$
WARNING: System::setSecurityManager will be removed in a future release
[info] welcome to sbt 1.4.4 (Eclipse Adoptium Java 17.0.15)
[info] loading settings for project gitbucket-commitgraphs-plugin-build from plugins.sbt ...
[info] loading project definition from C:\Users\username\gitbucket-commitgraphs-plugin\project
[info] loading settings for project root from build.sbt ...
[info] set current project to gitbucket-commitgraphs-plugin (in build file:/C:/Users/username/gitbucket-commitgraphs-plugin/)
[info] me.huzi.gitbucket:gitbucket-commitgraphs-plugin_2.13:4.35.0 [S]
[info]   +-com.typesafe.play:twirl-api_2.13:1.4.2 [S]
[info]     +-org.scala-lang.modules:scala-xml_2.13:1.2.0 [S]
[info]
[success] Total time: 0 s, completed 2025/12/30 9:12:17

以下のバージョンを変えてみたり、キャッシュを削除したりしてみましたが、現象は変わりませんでした。

  • scala
  • sbt
  • sbt-twirl
  • sbt-gitbucket-plugin
  • jvm

解決できないので新規 sbt プロジェクトを作成し src 以下を移植してみる

gitbucket-commitgraphs-chartjs-plugin というディレクトリを作成し、以下のファイルを作成しました。

build.sbt
name := "gitbucket-commitgraphs-chartjs-plugin"
organization := "io.github.gitbucket"
version := "0.1.0"
scalaVersion := "2.13.16"
gitbucketVersion := "4.36.2"

lazy val root = (project in file(".")).enablePlugins(SbtTwirl)

project\plugins.sbt
addSbtPlugin("io.github.gitbucket" % "sbt-gitbucket-plugin" % "1.6.0")
addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.6")
addSbtPlugin("org.playframework.twirl" % "sbt-twirl" % "2.0.9")

gitbucket-commitgraphs-plugin から src をそくっりもらってきます。

それから、sbt compile を実行しました。

>sbt compile
[info] welcome to sbt 1.11.3 (Eclipse Adoptium Java 17.0.15)
[info] loading settings for project gitbucket-commitgraphs-chartjs-plugin-build from plugins.sbt...
[info] loading project definition from C:\Users\y_aka\projects\gitbucket-commitgraphs-chartjs-plugin\project
[info] loading settings for project root from build.sbt...
[info] set current project to gitbucket-commitgraphs-chartjs-plugin (in build file:/C:/Users/y_aka/projects/gitbucket-commitgraphs-chartjs-plugin/)
[info] Executing in batch mode. For better performance use sbt's shell
[info] compiling 7 Scala sources to C:\Users\y_aka\projects\gitbucket-commitgraphs-chartjs-plugin\target\scala-2.13\classes ...
[warn] 3 deprecations
[warn] 5 deprecations (since 4.36.0)
[warn] 8 deprecations in total; re-run with -deprecation for details
[warn] three warnings found
[success] Total time: 8 s, completed 2025/12/30 9:19:54

deprecations の警告は出るもののすんなりコンパイルが通りました。なんでやねん!

Google Charts の処理部分を調査する

手始めに src/main/twirl/me/huzi/gitbucket/commitgraphs/list.scala.html の28行目 から始まる以下の処理が、グラフの描画を担っています。

src/main/twirl/me/huzi/gitbucket/commitgraphs/list.scala.html
        google.setOnLoadCallback(function() {
          var data = new google.visualization.DataTable();
          data.addColumn('date',"Date");
          data.addColumn('number',"Commits");
          data.addRows([
            @cg.dailys.map { d =>
              @{"[new Date(%4d,%2d,%2d,0,0,0),  %d],".format(d.date.getYear()+1900,d.date.getMonth(),d.date.getDate(),d.commits)}
            }
          ]);

          var options = {
            legend: {position: "none"},
            vAxis: {minValue: 0}
          };

          var chart = new google.visualization.AreaChart(document.getElementById('chart_div_@i'));
          chart.draw(data, options);
        });

クライアントに渡される際には、以下のように展開されます。

        google.setOnLoadCallback(function() {
          var data = new google.visualization.DataTable();
          data.addColumn('date',"Date");
          data.addColumn('number',"Commits");
          data.addRows([
            
              [new Date(2025, 6, 7,0,0,0),  2],
            
              [new Date(2025, 7, 2,0,0,0),  16],
            
              [new Date(2025,10,15,0,0,0),  5],
            
              [new Date(2025,10,29,0,0,0),  1],
            
              [new Date(2025,11, 3,0,0,0),  2],
            
              [new Date(2025,11, 4,0,0,0),  1],
            
              [new Date(2025,11, 5,0,0,0),  1],
            
              [new Date(2025,11, 7,0,0,0),  6],
            
              [new Date(2025,11, 8,0,0,0),  2],
            
              [new Date(2025,11, 9,0,0,0),  2],
            
              [new Date(2025,11,10,0,0,0),  9],
            
              [new Date(2025,11,11,0,0,0),  1],
            
              [new Date(2025,11,15,0,0,0),  10],
            
              [new Date(2025,11,16,0,0,0),  4],
            
              [new Date(2025,11,17,0,0,0),  1],
            
              [new Date(2025,11,22,0,0,0),  2],
            
              [new Date(2025,11,23,0,0,0),  2],
            
              [new Date(2025,11,24,0,0,0),  2],
            
          ]);

          var options = {
            legend: {position: "none"},
            vAxis: {minValue: 0}
          };

          var chart = new google.visualization.AreaChart(document.getElementById('chart_div_0'));
          chart.draw(data, options);
        });

chart.js など必要なファイルを同梱するディレクトリを作成

ディレクトリ src/main/resources/commitgraphs-chartjs/assets を掘り、以下のファイルを配置しました。

chartjs-adapter-luxon は、luxonにより、X軸に表示する日付をブラウザのローケルに合わせて表示するための Date Adapters です。

assetsMappings を設定

src/main/scala/Plugin.scala で assetsMappings を override し、同梱したファイルを使えるようにします。

src/main/scala/Plugin.scala
override val assetsMappings = Seq("/commitgraphs-chartjs" -> "/commitgraphs-chartjs/assets")

chart.js を使用してグラフを描画するように修正

src/main/twirl/me/huzi/gitbucket/commitgraphs/list.scala.html を修正しました。

まず、同梱した JavaScript を読み込むようにします。

src/main/twirl/me/huzi/gitbucket/commitgraphs/list.scala.html
    <script src="@context.path/plugin-assets/commitgraphs-chartjs/chart.umd.min.js"></script>
    <script src="@context.path/plugin-assets/commitgraphs-chartjs/luxon.js"></script>
    <script src="@context.path/plugin-assets/commitgraphs-chartjs/chartjs-adapter-luxon.js"></script>

Google Charts では X軸の値とY軸の値が対になっていましたが、 chart.js では、X軸とY軸を別の配列で定義する必要があります。

まずは、X軸の値を定義します。

src/main/twirl/me/huzi/gitbucket/commitgraphs/list.scala.html
        var dateLabels@i = [
          @cg.dailys.map { d =>
            @{"new Date(%4d,%2d,%2d,0,0,0),".format(d.date.getYear()+1900,d.date.getMonth(),d.date.getDate()) }
          }
        ];

実はこの部分、Scala というか Java 的には、deprecations なコードが入っています。(java.util.Date の getYearなど)

次にY軸の値を定義するとともに先ほど定義したX軸を関連付けます。オリジナルに合わせて、グラフを塗りつぶすように fill を true にしています。

src/main/twirl/me/huzi/gitbucket/commitgraphs/list.scala.html
        var data@i = {
          labels: dateLabels@i,
          datasets: [{
            label: 'Commits',
            data: [
              @cg.dailys.map { d =>
                @{"%d,".format(d.commits)}
              }
            ],
            fill: true
          }]
        };

グラフの設定を定義します。

src/main/twirl/me/huzi/gitbucket/commitgraphs/list.scala.html
        var lineConfig@i = {
          type: 'line',
          data: data@i,
          options: {
            responsive: true,
            locale: navigator.language,
            scales: {
              x: {
                  type: 'time',
                  time: {
                      unit: 'day'
                  }
              },
              y: {
                beginAtZero: true
              }
            },
            plugins: {
              legend: {
                position: 'top',
              },
              title: {
                display: true,
                text: 'Commits over Time'
              }
            }
          },
        };

ポイントは以下の通り。

  • locale を navigator.language から取得
  • X軸の type を time にすることで日付間隔を調整

Copilot からサジェストされた plugins はなくても良かったかな…

以上から、グラフを描画します。

src/main/twirl/me/huzi/gitbucket/commitgraphs/list.scala.html
        var myChart@i = new Chart(
          document.getElementById('chart_div_@i'),
          lineConfig@i
        );

オリジナルのまま、タグの id をそのまま使ってますが、div から canvas に変えたので合わせた方が良かったかな…

CommitGraphsSettingsService の deprecations な記述を修正

src\main\scala\me\huzi\gitbucket\commitgraphs\service\CommitGraphsSettingsService.scala に deprecations な記述があると警告が出ました。

例えば、以下の部分の2行目にある defining という記述です。元々、GitBucket に用意されていたシンタックスシュガーのようです。

src\main\scala\me\huzi\gitbucket\commitgraphs\service\CommitGraphsSettingsService.scala
  def saveCommitGraphsSettings(settings: CommitGraphsSettings): Unit =
    defining(new java.util.Properties()) { props =>
      props.setProperty(CommitGraphsGitCommand, settings.CommitGraphsGitCommand)
      Using.resource(new java.io.FileOutputStream(CommitGraphsConf)) { out =>
        props.store(out, null)
      }
    }

改造版 では、以下のように修正しています。

src\main\scala\me\huzi\gitbucket\commitgraphs\service\CommitGraphsSettingsService.scala
  def saveCommitGraphsSettings(settings: CommitGraphsSettings): Unit = {
    val props = new java.util.Properties()
    props.setProperty(CommitGraphsGitCommand, settings.CommitGraphsGitCommand)
    Using.resource(new java.io.FileOutputStream(CommitGraphsConf)) { out =>
      props.store(out, null)
    }
  }
GitHubで編集を提案

Discussion

kenji yoshidakenji yoshida

https://github.com/gitbucket/gitbucket/issues/2676

com.github.bkromhout の java-diff-utils は昔のgitbucketが依存していたけれどjcenterという、もう今となっては閉鎖した場所に置かれていたものです。以下のようにいくつかupdateしたら手元では依存解決成功しました。

変なcacheが効いていたか、sbtのreload忘れた、とかだと思います

--- a/build.sbt
+++ b/build.sbt
@@ -8,7 +8,7 @@ organization := Organization
 name := ProjectName
 version := ProjectVersion
 scalaVersion := "2.13.1"
-gitbucketVersion := "4.35.0"
+gitbucketVersion := "4.44.0"
 
 scalacOptions := Seq("-deprecation", "-feature", "-language:postfixOps", "-Ydelambdafy:method", "-target:jvm-1.8")
 javacOptions in compile ++= Seq("-target", "8", "-source", "8")
diff --git a/project/build.properties b/project/build.properties
index fc08cae..01a16ed 100644
--- a/project/build.properties
+++ b/project/build.properties
@@ -1 +1 @@
-sbt.version = 1.4.4
+sbt.version = 1.11.7
diff --git a/project/plugins.sbt b/project/plugins.sbt
index 009bd1e..599fe89 100644
--- a/project/plugins.sbt
+++ b/project/plugins.sbt
@@ -1,4 +1,4 @@
 logLevel := Level.Warn
 
 addSbtPlugin("com.typesafe.sbt" % "sbt-twirl" % "1.4.2")
-addSbtPlugin("io.github.gitbucket" % "sbt-gitbucket-plugin" % "1.5.0")
+addSbtPlugin("io.github.gitbucket" % "sbt-gitbucket-plugin" % "1.6.0")
1