JGitへのコントリビュートとGerritを使ってみた感想

GitBucketではバックエンドの実装にJGitを使っているのですが、gitリポジトリをzipファイルとしてダウンロードした際に、同じコミットでもダウンロードしたzipファイルのハッシュが同じ値にならないというレポートがあり、調べたところJGitのarchiveコマンド部分の実装に問題があることがわかったため*1、JGitにフィードバックを行ってみました。

こちらがGitBucketのユーザさんからいただいたレポートです。

github.com

JGitへのコントリビューション方法

さて、JGitはオープンソースプロジェクトではありますが、Eclipse Foundationで開発されているものなので、Apache Software Foundationと同じくコントリビューションは面倒そうな予感がします。GitHubリポジトリがあるのですがどうやらこちらはミラーのようで、プルリクエストを送っても受け付けてもらえないようです。

github.com

調べてみたところ、コントリビューター向けのガイドがありました。どうやらGerritでコードレビューの依頼をする必要があるようです。もちろん事前にアカウントを作成してCLAへの同意などを行っておく必要があります。*2

Gerritによるコードレビュー

Gerritは1コミット=1プルリクエストのような感じでレビューを行います。以下のようにrefs/for/masterにpushすることでレビュー依頼が登録されます。

git push ssh://username@git.eclipse.org:29418/jgit/jgit.git HEAD:refs/for/master

レビューを受けて修正する場合は元のコミットにamendするなどして新しいコミットをpushするわけですが、以前のコミットと紐づけるためにコミットメッセージにChange-Idを含めておく必要があります。また、修正者を明示するためにアカウントの情報をSigned-off-byに含めておく必要もあります。JGitのGerritではこれらのフィールドが含まれていないとpushがリジェクトされました。

実際にpushしたコメントはこんな感じです。

Set commit time to ZipArchiveEntry

Archived zip files for a same commit have different MD5 hash because
mdate and mdate in the header of zip entries are not specified. In
this case, Commons Compress sets an archived time.

In the original git implementation, it's set a commit time:
https://github.com/git/git/blob/e2b2d6a172b76d44cb7b1ddb12ea5bfac9613a44/archive.c#L378

By this fix, archive command sets the commit time to ZipArchiveEntry
when RevCommit is given as an archiving target.

Change-Id: I30dd8710e910cdf42d57742f8709e9803930a123
Signed-off-by: Naoki Takezoe <takezoe@gmail.com>

Change-Idはコミットフックで自動的に付与することができます。フックスクリプトは通常Gerritサーバで提供されており、JGitの場合は以下のようにしてローカルリポジトリにインストールすることができました。

scp -p -P 29418 username@git.eclipse.org:hooks/commit-msg .git/hooks/

これで修正をローカルリポジトリにコミットした時点でコミットメッセージにChange-Idが自動的に追加されます(修正時など、すでにChange-Idが付与されている場合は何も行いません)。

レビューで指摘があった場合は修正して再度pushすると新しいpatch setが作成されます。前述の通り、Gerritは1コミットが1プルリクエストに相当するのですが、patch setによって1コミット内のレビューや変更の履歴を管理することができます。もちろん途中のpatch setからやり直したりということもできます。

ちなみに以下が今回フィードバックした修正のレビューです。JGit 4.7でマージしていただけるようです。

https://git.eclipse.org/r/#/c/91116/

f:id:takezoe:20170218214029p:plain

感想

JGitは目立たないライブラリではありますが、JVMベースの様々な開発者向けツールで使用されており、もちろんGitBucketでも活用させていただいているので、ちょっとした修正ではありますが貢献できて嬉しいところです。

また、Gerritは初めて使ったのですが、綺麗なコミット履歴を保ちつつ、その過程も管理できるという点はメリットだと感じました。ただ、Gitの操作が複雑なだけでなく*3、Web UIも非常にわかりづらく初見だと何をしていいのかさっぱりわからないという敷居の高さがあります。

最近は企業が主体となって開発しているOSSや、ASFのプロダクトなどでもGitHubでのプルリクエストを受け付けてくれるものが増えてきていますが、golangなどGerritを使ってレビューを行っているプロジェクトもあります。今回の作業はGerritの使い方も含め勉強になりました。

Gerritの使い方を手取り足取り教えてくれた弊社ターミナル部部長である@tanacasinoに感謝の意を表したいと思います。ありがとうありがとう。

*1:zipファイルのエントリにファイルのタイムスタンプを設定していなかったため、zipファイル作成時のタイムスタンプが使用されてしまっていたのが原因でした。

*2:GerritはGoogleが開発したGitベースのコードレビューシステムで、JGitを使用して実装されています。

*3:通常の操作を行うだけでもそれなりのGit力を要求されますし、patch setの操作や複数のレビューが相互に関連する大きな修正の場合はかなり複雑な操作が必要になりそうです。

GitHubをパトロールするためのWebアプリを作ってみた

GitHubのトップページではウォッチしているリポジトリのアクティビティがタイムラインのように表示されるのですが、ウォッチしているリポジトリ数が多いと見落としてしまいがちです。そこで、自分がウォッチしているリポジトリの中から選択したリポジトリのアクティビティのみ表示するタイムラインリーダーのようなものを作ってみました。

f:id:takezoe:20170218005503j:plain

こんな感じで選択したリポジトリのアクティビティを一覧表示し、未読管理も行うことができます。キーボードショートカットも装備しているのでRSSリーダーのような感じで使うことができます。

GitHubのタイムラインはRSSフィードも提供されているのですが、リポジトリごとにRSSリーダーに登録するのは面倒ですし、GitHubAPIから取得できる情報と比べるとRSSフィードに含まれる情報量は多くはありません。このアプリケーションではウォッチしているリポジトリの中から選択するだけで購読できたり、イシューのMarkdownをHTMLに変換して表示するなどGitHubに最適化されています。

今のところコードを公開する予定はないのですが、このアプリケーションはもちろんScalaで書いており、サーバサイドはRestyを使っています。データは最初はElasticsearchに入れていたのですが、低スペックなVPSだとElasticsearchを動かすのが厳しいこと、個人で使うものなのでそれほどのデータ量にならないだろう(&データが増えたら消せばよい)ということで途中からPostgreSQLに移行しました。

フロントはVue.jsを使っているのですが、HTML1枚なのでwebpackなどは使わずベタ書き+WebJarsで済ませています。機能が増えてきたらコンポーネント化のためにwebpackを導入する必要が出てくるかもしれません。Vue.jsはシンプルなAngular1という感じでちょっとしたものを作るには非常に便利でいいですね。趣味で作っているものなのでフロントをScala.jsで書いてみるのも面白いかもしれません。

まだ不足している機能も多いので少しずつ手を入れていこうと思いますが、ひとまずこれで気になるリポジトリのアクティビティを見逃すことはなくなりそうです!

シンプルなScala用ビルドツール「cbt (Chris's Build Tool)」を試してみる

Slickのコミッタとして有名なJan Christopher Vogtさんが作っているcbt (Chris’s Build Tool) というScala用のビルドツールがあります。*1

github.com

昨年参加したScala Days NewYork 2017でもセッションがあり、興味本位で参加したのですが、sbtのdisなどもありなかなか楽しい発表でした。*2

www.youtube.com

cbtの特徴

Scalaにはsbtという標準的なビルドツールが存在するわけですが、非常に難解なsbtと異なり、cbtは既存のScalaの知識だけでカスタマイズができるという大きな特徴があります。

たとえばコンパイルの前後に処理を挟む場合、compileメソッドをオーバーライドして前後に独自のScalaコードを記述し、super.compileを呼び出せばよいだけです。タスクはただのメソッドなので別のタスクを呼ぶ場合もメソッドを呼び出すだけですし、以下のようにメソッドを定義するだけで独自タスクも簡単に定義することができます。

class Build(val context: Context) extends BaseBuild{
  ...
  def hello = println("Hello CBT!!")
}

cbtを使ってみる

まずはGitHubリポジトリをクローンし、リポジトリのルートディレクトリに環境変数PATHを通します。*3

$ cd ~/
$ git clone https://github.com/cvogt/cbt.git
$ export PATH=$PATH:~/cbt

続いて適当なディレクトリを掘って動作を確認してみます。cbt toolsはファイルの雛形などを生成する便利ツールです。

$ mkdir my-project
$ cd my-project
$ cbt tools createMain

以下のような内容でMain.scalaが生成されます。

object Main{
  def main( args: Array[String] ): Unit = {
    println( Console.GREEN ++ "Hello World" ++ Console.RESET )
  }
}

実行してみます。

$ cbt run
Compiling to /tmp/my-project/target/scala-2.11/classes
[warn] Pruning sources from previous analysis, due to incompatible CompileSetup.
[info] Compiling 1 Scala source to /tmp/my-project/target/scala-2.11/classes...
[info] Compile success at 2017/02/15 14:19:40 [0.457s]
Hello World

このようにコンパイルや実行だけであればビルドファイルを作らなくても実行することができます。

ビルドファイルの作成

ビルドの設定などを行いたい場合はビルドファイルを作成する必要があります。

$ cbt tools createBuild

以下のような内容でbuild/build.scalaが生成されます。コメントでライブラリやマルチプトジェクト構成の場合の設定方法などが説明されています。

import cbt._
class Build(val context: Context) extends BaseBuild{
  override def dependencies =
    super.dependencies ++ // don't forget super.dependencies here for scala-library, etc.
    Seq(
      // source dependency
      // DirectoryDependency( projectDirectory ++ "/subProject" )
    ) ++
    // pick resolvers explicitly for individual dependencies (and their transitive dependencies)
    Resolver( mavenCentral, sonatypeReleases ).bind(
      // CBT-style Scala dependencies
      // ScalaDependency( "com.lihaoyi", "ammonite-ops", "0.5.5" )
      // MavenDependency( "com.lihaoyi", "ammonite-ops_2.11", "0.5.5" )

      // SBT-style dependencies
      // "com.lihaoyi" %% "ammonite-ops" % "0.5.5"
      // "com.lihaoyi" % "ammonite-ops_2.11" % "0.5.5"
    )
}

デイレクトリレイアウトはsbtと同様で、デフォルトではsrc/main/scalasrc/test/scalaなどにソースコードを配置することができます。

jarファイルにパッケージングしたいのでベーストレイトをPackageJars変更します。namegroupIdversionという3つのメソッドを実装する必要があります。

import cbt._
class Build(val context: Context) extends PackageJars{
  override def name = "my-project"
  override def groupId = "com.github.takezoe"
  override def version = "1.0.0"
  ...
}

このようにしておくとsbt packageでjarファイルを作成することができます。

まとめ

実際にcbtを使ってみた感じ、sbtと比べるとカスタマイズしやすそうで好印象です。ただ、現実的にはやはり以下のような点がネックになってきます。

  • IDEのサポートが期待できない
  • 既存のsbtプラグインが利用できない

特に既存のsbtプラグインが利用できないというのは大きな欠点です。例えばサーブレットベースのWebアプリケーションを開発するためのxsbt-web-pluginやsbtプラグインでテンプレートをScalaコードにコンパイルするTwirlなども使えません。もちろん開発環境をがっつりsbtに依存しているPlayアプリケーションの開発を行うこともできません。

現時点ではネタ感を拭えないのが正直なところですが、開発は活発に続けられていますし*4、選択肢があることは重要だと思うので、Chrisさんには是非とも頑張っていただきたいところです。

*1:自分の名前をつけてしまうセンスはどうなのかという話はここでは置いておきます。

*2:動画だけでなくスライドも公開されています。 https://docs.google.com/presentation/d/1dZ7f6cETFecfjiqQvVsYtXdUQxZzan8gS_XJZoWbF7M/

*3:あわせてnailgunを入れておくと起動が速くなったり、gpgを入れておくとpublishSignedタスクを実行できるようになったりするようです。Macの場合はbrewで入れておくとよいでしょう。

*4:Chrisさんのモチベーションがいつまで続くのかという不安はありますが…。

Scala Days 2017のタイムテーブルが公開されました

ここ数年は毎年アメリカとヨーロッパで開催されているScala Days、一昨年はサンフランシスコとアムステルダム、昨年はニューヨークとベルリンでしたが今年はシカゴとコペンハーゲンで開催されるようです。

www.lightbend.com

以下、シカゴとコペンハーゲンそれぞれのタイムテーブルです。

タイムテーブルをざっと眺めて気になったセッションを挙げてみました。個人的な趣味だったり、内容より人で見ているところもあるのでだいぶ偏りがありますが…。

Scala.jsカウボーイLi HaoyiさんのAnatomy of a full-stack Scala/Scala.js Web Appも気になりますね…。Scala.jsどうなんですかね…。

なお、コペンハーゲンではScala Daysの日程に合わせてTypelevel Summitも開催されるようです。Scala Daysの翌日なのであわせて参加される方も多いのではないでしょうか。

typelevel.org

第十八回 #渋谷java を開催しました

1月28日(土)に弊社オフィスで第十八回 #渋谷javaを開催しました。

f:id:takezoe:20170128224509j:plain

おや、なにか様子がおかしいですね…。謎のマークが…。

今回は以下の皆さんに発表していただきました。

  • セッション枠(20分)
    • ayato_p 「筋肉でテストを救う」
    • Ikuru K 「サムネイルを作る話」
  • 通常枠(10分)
    • lagénorhynque 「JavaからClojureへ」
    • Naoki Takezoe 「ZipkinとBraveでマイクロサービスの呼び出しを可視化してみよう」
    • Shunsuke Tadokoro 「Clojureに入門してHTTPサーバーを作ってみた」
    • shimamoto 「doobie0.4.0のdynamic SQLを試してみる」
    • ayato_p 「alter-var-rootをいつ使うべきか」

当日の様子や皆さんの発表内容については以下のtogetterまとめをご覧いただければと思います。

togetter.com

特に狙ったわけではないのですが、なんと7本のLTのうち5本がClojureのトーク、気づけば真面目にJavaの発表をしたのは自分だけという驚愕の事態でした。*1

ayato_pさんには毎回Clojureの実用的なお話をしていただいていますが、今回はlagénorhynqueさんのLisp / Clojureの入門的な発表や若者であるShunsuke TadokoroさんのClojureでHTTPサーバーを作ってみたという発表もあり、大変教育的な勉強会になったのではないかと思います。

これはScala勢としては次回はScalaの発表をしたいところですが、うっかり油断すると #渋谷java なのにJavaの発表が1つもない勉強会になってしまうので、次回に備えてJavaネタを温めておきたいと思います。

*1:最後のayato_pさんはLT枠に空きがあったので急遽発表していただけることになりました。

GitBucket 4.9をリリースしました

Scalaで実装されたオープンソースのGitサーバ、GitBucket 4.9をリリースしました。

https://github.com/takezoe/gitbucket/releases/tag/4.9

GitLFSサポート

GitLFSの実験的サポートを追加しました。この機能を使用するためにはGitBucketの設定画面でBase URLを適切に設定しておく必要があります。

f:id:takezoe:20170128105158p:plain

これだけでGitLFSが利用可能になります。GitLFSで管理されているファイルは GITBUCKET_HOME/repositories/<user>/<repository>/lfs 配下に格納され、リポジトリビューアでは以下のようにファイルの詳細画面にLFSラベルが表示されるようになります。

f:id:takezoe:20170128105206p:plain

将来的にはラージファイルの保存先としてローカルファイルシステムだけでなくクラウドストレージや分散ファイルシステムなどにも対応したいと考えています。

イシューとプルリクエストのテンプレート

リポジトリのルートディレクトリもしくは.gitbucketディレクトリにISSUE_TEMPLATEISSUE_TEMPLATE.mdISSUE_TEMPLATE.markdownなどの名前でファイルを作成しておくとイシューやプルリクエストの新規登録フォームの初期値として表示されるようになりました。

f:id:takezoe:20170129122639p:plain

イシューやプルリクエストの雛型や、注意事項などを記述しておくのに利用できるのではないかと思います。

ラベルの色をカラーコードで指定可能に

イシューやプルリクエストのラベルの色はこれまでカラーピッカーで選択することができましたが、カラーコードを直接入力することもできるようになりました。

f:id:takezoe:20170128105230p:plain

アカウントの説明を入力可能に

アカウントの説明を入力できるようになりました(個人アカウントの場合はBio、グループアカウントの場合はDescriptionというフィールドに入力できます)。入力した内容はアカウントの詳細画面に表示されます。

f:id:takezoe:20170128105239p:plain

スタンドアロンモードに–temp_dirオプションを追加

組み込みJettyでのスタンドアロンモードでの起動時に指定可能なオプションとして--temp_dirを追加しました。

これは組み込みJettyがgitbucket.warを展開するディレクトリを指定するためのものです。デフォルトでは GITBUCKET_HOME/tmp に展開されますが、都合が悪い場合はこのオプションで任意のディレクトリに展開するよう指定することができます。

イシュー関連のWeb APIの追加

新しく以下のWeb APIが追加されました。

この他にも様々な改善やバグフィックスを行っています。詳細についてはIssueの一覧をご覧ください。

第十八回 #渋谷java でZipkinとBraveの紹介をしました

最近RestyにBraveを組み込んだり、仕事でもZipkinを触っていたりするのでZipkinとBraveの概要について発表させていただきました。発表資料はこちらです。

www.slideshare.net

ZipkinはTwitter社が開発した分散トレーシングシステムで、特に多階層のマイクロサービスのボトルネックの解析に有用なツールです。BraveはJavaベースのWebアプリケーションからZipkinサーバにトレース情報を送信するためのライブラリです。

github.com

github.com

Braveは前のサービスからHTTPヘッダで渡されたトレース情報をServletFilterでThreadLocalに突っ込み、それをHTTPクライアントライブラリのInterceptorで次のサービス呼び出しのHTTPヘッダに付け加えるという感じの仕組みになっています。そのため次のサービスの呼び出しが別スレッドで行われているとトレース情報の紐付けができないという点に注意が必要です(以前Hystrixと組み合わせた時に少しハマりました)。

また、標準のZipkinサーバの代わりにGoogleオープンソースとして公開しているstackdriver-zipkinを使うとGoogle Cloud PlatformのStackdriver Traceというモニタリングサービスにトレース情報を送信することができます。実際に運用で試したことはないのですが、これを使うと自前でZipkinサーバを運用する必要がなくなりますし、一定量であれば無料で利用することができるので便利かもしれません。

github.com

Zipkinは各種言語向けのクライアントライブラリも揃っていますし、このように外部のモニタリングサービスでもZipkinがサポートされることで、Zipkinが独自のツールとしてだけでなく、仕様としてもある種標準的な位置付けになっていく可能性があるのではないでしょうか。

今回の発表にあたり、JavaサーブレットにBraveを組み込んでZipkinでトレース情報を参照するためのミニマムなサンプルプロジェクトを以下のGitHubリポジトリで公開していますので興味のある方は是非実際に動かしてみていただければと思います。

github.com