GitBucket 4.5をリリースしました

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

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

テキストエリアにもファイルのドロップが可能に

イシューやWikiにファイルを添付する際、これまではテキストエリア下部の細いバーの部分にしかドロップできなかったのですが、テキストエリアの領域にもドロップできるようになりました。

f:id:takezoe:20160922115427p:plain

ダッシュボードで表示するイシュー、プルリクエストを選択可能に

ダッシュボードでは元々イシューやプルリクエストに対して以下の3種類のビューが提供されていました。

  • 自分が作成したもの
  • 自分がアサインされているもの
  • 自分が言及しているもの

しかしUI変更に伴って最近のバージョンでは自分が作成したものの一覧しか参照できないようになっていました。GitBucket 4.5では後者の2つの一覧も参照できるよう、表示を切り替えるためのスイッチを復活させました。

f:id:takezoe:20160922115828p:plain

HikariCPの設定が可能に

GitBucketは内部的にコネクションプーリングのためにHikariCPを使用しているのですが、これまではデフォルトの設定で動作するのみでユーザがHikariCPの設定を行うことができませんでした。GitBucket 4.5からはGITBUCKET_HOME/database.confでHikariCPの設定を行うことができるようになっています。

database.confはデフォルトでは以下のような内容でGitBucketの初回起動時に自動生成されますので、必要な項目のコメントを外して値を変更することでHikariCPの設定を行うことができます。設定の変更後はGitBucketの再起動が必要になります。

db {
  url = "jdbc:h2:${DatabaseHome};MVCC=true"
  user = "sa"
  password = "sa"
#  connectionTimeout = 30000
#  idleTimeout = 600000
#  maxLifetime = 1800000
#  minimumIdle = 10
#  maximumPoolSize = 10
}

クッキーのセキュリティ改善

セッションを維持するためのJSESSIONIDクッキーにhttp-only属性が付与されるようになりました。また、baseUrlの設定がhttps://xxxx隣っている場合はsecure属性も付与されるようになりました。

コミット数をコミット履歴ボタンに表示

UIの更新以降、リポジトリビューアからコミット数の表示が消えてしまっていましたが、今回のバージョンからコミット履歴ボタンにコミット数を表示するようにしました。

f:id:takezoe:20160929104333p:plain

バイルブラウザでの表示の改善

UIのAdminLTEへの移行時にモバイルブラウザへの対応が完全にできておらず、サイドメニューを利用できないといった不都合があったのですが、これらの不都合を改修したうえでモバイルブラウザでの表示を全体的に見直しました。

f:id:takezoe:20160922120811p:plain

f:id:takezoe:20160922120820p:plain

また、モバイルビューでもファイル検索ボタンとコミット履歴ボタンを表示するようにしました。

f:id:takezoe:20160929104412p:plain

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

Akka Typedを試してみる

MEAPが開始してから丸4年、Akka in Actionがようやく完成したそうです。

完成版のPDFをパラ見していたところ、最後の16章にこれから入る予定の新機能の解説があり、その中でAkka Typedが取り上げられていたので少し試してみました。

Akka TypedはAkkaのアクターが返すメッセージを型安全に扱うためのものです。

まずはAkka Typedを使わずに普通にアクターを実装してみます。以下は受信したメッセージに反応して送信元にメッセージを返却する簡単なアクターです。

import akka.actor._
import akka.pattern.ask
import akka.util.Timeout
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global

object Hello {
  final case class HelloMessage(name: String)
  final case class HelloResult(message: String)

  class HelloActor extends Actor {
    def receive = {
      case HelloMessage(name) => sender ! HelloResult(s"Hello $name!")
    }
  }
}

implicit val timeout = Timeout(5 seconds)

val system = ActorSystem("system")
val actor = system.actorOf(Props[Hello.HelloActor])
val f: Future[Any] = actor ? Hello.HelloMessage("Naoki")

f.mapTo[Hello.HelloResult].foreach { result =>
  println(result.message)
}

アクターに?で問い合わせた結果の型がFuture[Any]になっており、mapToで返却されるメッセージの型にキャストしています。この部分を型安全にしようというのがAkka Typedです。

同じ処理をAkka Typedで書き直してみます。

import akka.typed._
import akka.typed.ScalaDSL._
import akka.typed.AskPattern._
import akka.util.Timeout
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global

object TypedHello {
  final case class HelloMessage(name: String, replyTo: ActorRef[HelloResult])
  final case class HelloResult(message: String)

  val helloBahavior = ContextAware[HelloMessage] { ctx =>
    Static {
      case HelloMessage(name, replyTo) => replyTo ! HelloResult(s"Hello $name!")
    }
  }
}

implicit val timeout = Timeout(5 seconds)

val system: ActorSystem[TypedHello.HelloMessage] = ActorSystem("system", Props(TypedHello.helloBahavior))
val f: Future[Hello.HelloResult] = system ? (TypedHello.HelloMessage("Naoki", _: ActorRef[TypedHello.HelloResult]))

f.foreach { result =>
  println(result.message)
}

ちょっとわかりにくいんですが、アクターの中で行っていた処理はContextAwareの中で行うようになっており、送信メッセージに返信先の型付きActorRefを持たせ、その返信先アクターに対してメッセージを送信するようになっています。ActorSystemActorRef?メソッドなどはAkka標準のものではなくAkka Typedが提供するものを使用することで型安全になるようになっています。

個人的には効果の割に修正が多くてちょっと厳しいかなという気がします。Akka in Actionにも「型安全のメリットは大きいので将来的にAkka Typedが重要なモジュールになる可能性はあるけど、今後大きく変更する可能性があるからまだプロダクションで使うべきではない」というようなことが書かれていました。

Akka in Action

Akka in Action

16章にはAkka Typedの他にAkka Streams、Akka HTTP、Akka Distributed Dataがちょっとずつですが紹介されています。

しかしこの本、完成するまで4年かかったのか…。4年…。

Scalaでコンパイル時にSQLをバリデーションするマクロを作ってみた

以前からscala-jdbcというScala用のシンプルなJDBCラッパーを作っています。

github.com

このscala-jdbcコンパイル時にSQLのバリデーションを行うためのマクロを追加してみました。

たとえば以下のように少し間違ったSQLカラム名の後ろに余計なカンマが付いている)を記述したとします。

sqlc("SELECT USER_ID, FROM USER")

するとコンパイル時に以下のようなエラーが報告されます。

[error] /Users/takezoe/gitwork/scala-jdbc/src/test/scala/com/github/takezoe/scala/jdbc/validation/SqlValidationSpec.scala:30: Encountered " <S_IDENTIFIER> "USER "" at line 1, column 40.
[error] Was expecting one of:
...
[error]     
[error]     sqlc("SELECT USER_NAME, FROM USER")
[error]         ^

デフォルトではSQLシンタックスのみがチェックされますが(SQLのパースにはJSqlParserを使っています)、コンパイル時のカレントディレクトリにschema.jsonというファイルを作っておくことでテーブルやカラムの存在チェックを行うこともできます。schema.jsonは以下のような感じです。

{
  "tables":[
    {
      "name": "USER",
      "columns": [
        { "name": "USER_ID" },
        { "name": "USER_NAME" },
        { "name": "COMPANY_ID" }
      ]
    },
    {
      "name": "COMPANY",
      "columns": [
        { "name": "COMPANY_ID" },
        { "name": "COMPANY_NAME" }
      ]
    }
  ]
}

なお、このマクロはまだ実験的な機能なのでscala-jdbcのSNAPSHOTバージョンでのみ提供されています。build.sbtには以下の依存関係を追加します。

resolvers += "Sonatype OSS Snapshots" at "https://oss.sonatype.org/content/repositories/snapshots"

libraryDependencies += "com.github.takezoe" %% "scala-jdbc" % "1.0.3-SNAPSHOT"

もし不正なバリデーション結果に遭遇した場合はGitHubIssuesでレポートをいただければと思います。

Scala関西 Summit 2016に参加します

来る10月8日(土)に大阪で開催されるScala関西 Summit 2016ですが、弊社もスポンサーとしてサポートさせていただいています。

summit.scala-kansai.org

スポンサーセッションでは「Play2+SlickだけじゃないScalaのWeb/DBフレームワーク事情」という発表をさせていただきます。

Scalaフレームワークデファクトスタンダードと言えばPlay + Slickですが、どちらも使いにくい部分や、手放しで採用できない問題があり、他のフレームワークへの移行も視野に入れています。このセッションではPlayおよびSlickの抱える問題点と、その代替となり得るフレームワークについてお話しさせていただきたいと考えています。

また、スポンサーブースではScalaで開発しているオープンソースプロダクトの展示なども行う予定ですのでお気軽にお声がけいただければと思います!

この際なのでScala逆引きレシピやScalaパズルもよろしくお願いします!!

Scala逆引きレシピ

Scala逆引きレシピ

Scalaパズル 36の罠から学ぶベストプラクティス

Scalaパズル 36の罠から学ぶベストプラクティス

OracleのDavid BuckさんによるBytecode Verification

毎週金曜日に開催している社内勉強会ですが、今週は久々の外部ゲストとしてOracleのDavid Buckさんにお越しいただきBytecode Verificationについてお話しいただきました。

f:id:takezoe:20160903121051j:plain

Buckさんは今年もサンフランシスコで開催されるJavaOne 2016で3本のセッションに登壇されるそうで、そのうちの1セッションの内容を、しかも日本語でお話しいただけるという大変貴重な機会でした。

Bytecode Verificationが必要な理由

Bytecode VerificationはJavaVMが不正なバイトコードを検出するための機能ですが、普段その存在を意識することがほとんどないのではないかと思います。しかし実際のところ、以下のように不正なバイトコードに遭遇し得るシチュエーションには様々なものがあります。

Javaは古いコンパイラコンパイルされたクラスファイルでも動作させることができるのですが、バイトコードを操作するツールやライブラリがバイトコードに仕様の相違を理解せずに操作してしまいバイトコードが壊れてしまうというケースもあるのだそうです。

結論

結論としては非常にシンプルで「Bytecode Verificationはデフォルトで有効なので無効にしないでください」とのこと。

パフォーマンス面の理由からBytecode Verificationを無効にしているオープンソースプロジェクトもありますが、現代のコンピュータではほぼ無意味なので、余計なトラブルを防ぐためにも開発環境であろうとプロダクション環境であろうとBytecode Verificationを無効にするべきではないとのことでした。

壊れたバイトコードを読み込んだ場合、Bytecode Verificationが有効であればわかりやすいエラーメッセージが出るのですが、無効になっているとVMがクラッシュしたりします。こういった場合はコアダンプを見てもクラスファイルが壊れているのかVMのバグなのかの切り分けが非常に難しく、仕事柄いろいろとご苦労されてきた苦悩がありありと伝わってきましたw

日頃Javaを使っていてもJavaVMの中身まで触れる機会は中々ないので大変勉強になりました。マニアックな話題だったとは思うのですが、学びあり、笑いありの楽しい発表でしたので、JavaOne 2016に参加される方は是非Buckさんのセッションに足を運んでみていただければと思います。

豆知識

ちなみにデフォルトでは-Xverify:remoteが有効なのですが、以下のオプションを指定できます。

デフォルトがremoteなのは元々JavaはAppletでの利用が想定されており、アプリケーションのクラスファイルはリモートサーバから読み込んで実行することが前提だった時代の名残なのだそうです。Javaの歴史を感じますね〜。

Twitter製の大規模ソフトウェア向けビルドツール「Pants」を試してみる

PantsはTwitter社が開発した大規模ソフトウェア向けのビルドツールです。今年の5月に1.0がリリースされました。

github.com

主にPythonで書かれているようですが、JavaScalaPythonGolangのビルドに対応しており、Thriftのコード生成やMarkdownからのドキュメント生成などもサポートしているようです。

同種の大規模向けビルドツールにはGoogle製のBazelやFacebook製のBuckがあります。

github.com

github.com

いずれも元は社内用に開発されたツールがOSS化されたものですが、GoogleのBazelは2009年頃からGoogle社内で使われておりかなり歴史のあるツールのようです。

BuckやPantsはBazelを参考にしているようですが*1、BuckはAndroidアプリのビルドが高速だったり、Pantsは複数言語への対応やThriftのサポートなど各社のカラーが色濃く現れています。

大規模ソフトウェアのビルドの課題

そもそも大規模なソフトウェアのビルドにおける課題とは何なのでしょうか?大きく整理すると以下の2点が問題となるようです。

  • バージョン管理の煩雑さ
  • ビルドに時間がかかる

ソフトウェアの規模が大きくなるほどビルドに時間がかかるのは直感的に理解できると思いますが、バージョン管理の煩雑さというのはどういうことなのでしょうか。

大規模なソフトウェアでは機能単位でモジュール化したり、共通部分をライブラリ化するなどして開発の効率化を図りますが、ソフトウェアが大きくなればなるほどモジュールが増えていきます。それぞれのモジュールが独自にバージョンアップしていくと、同じライブラリを使っているモジュールでもモノによって異なるバージョンを使用していたりといった事態は普通に発生します。

そうすると最終的に複数のモジュールをまとめて1つの巨大なモジュールをビルドする際にライブラリのコンフリクトが発生してしまいます。規模が多くなればなるほどこのようなライブラリの整合性を取る作業は難しくなっていきます。

現在はGitHubなどの流行もありモジュール毎にリポジトリを作成する文化が一般的ですが、大規模なソフトウェアではこの問題を避けるため単一のリポジトリで全てのモジュールを管理するMono Repositoryが適しているとされています。Blaze、BuckそしてPantsといった大規模ソフトウェア向けのビルドツールはすべてこのMono Repositoryを前提としています。

Pantsを使ってみる

Pantsのインストールは簡単で、リポジトリのルートディレクトリで以下のコマンドを実行します。*2

$ curl -L -O https://pantsbuild.github.io/setup/pants && chmod +x pants && touch pants.ini

pantsコマンドが使用できるようになり、初回実行時にPythonや必要なライブラリなどが物凄い勢いでインストールされます。

$ ./pants -V
New python executable in /Users/naoki.takezoe/.cache/pants/setup/bootstrap-Darwin-x86_64/pants.ZQsYCJ/install/bin/python2.7
Also creating executable in /Users/naoki.takezoe/.cache/pants/setup/bootstrap-Darwin-x86_64/pants.ZQsYCJ/install/bin/python
Installing setuptools, pip, wheel...done.
Collecting pantsbuild.pants
  Downloading pantsbuild.pants-1.1.0.tar.gz (874kB)
    100% |████████████████████████████████| 880kB 1.4MB/s
...
Successfully installed Markdown-2.1.1 Pygments-1.4 ansicolors-1.0.2 cov-core-1.15.0 coverage-3.7.1 docutils-0.12 fasteners-0.14.1 futures-3.0.5 lmdb-0.89 monotonic-1.2 pantsbuild.pants-1.1.0 pathspec-0.3.4 pex-1.1.10 psutil-4.3.0 py-1.4.31 pystache-0.5.3 pytest-2.6.4 pytest-cov-1.8.1 pywatchman-1.3.0 requests-2.5.3 scandir-1.2 setproctitle-1.1.10 setuptools-5.4.1 six-1.10.0 twitter.common.collections-0.3.7 twitter.common.confluence-0.3.7 twitter.common.dirutil-0.3.7 twitter.common.lang-0.3.7 twitter.common.log-0.3.7 twitter.common.options-0.3.7
1.1.0

ビルドの設定はBUILDという名前のファイルに記述します。以下のような簡単な構成で試してみます。

reporoot/
 |
 +- java-sample/
     |
     +-HelloWorld.java
     |
     +-BUILD

BUILDファイルの内容は以下のような感じです。ソースディレクトリやパッケージがネストしている場合はglobsのパターンの書き方や、パターンを複数列挙することで対応できます。

java_library(
  name = 'java-sample',
  dependencies = [
  ],
  sources = globs('*.java'),
)

ビルドしてみます。

$ ./pants compile java-sample
...
11:08:37 00:02         [zinc]
                       [info] Compiling 1 Java source to /Users/naoki.takezoe/pants/.pants.d/compile/zinc/252d64521cf9/java-sample.java-sample/current/classes...
                       [info] Compile success at 2016/09/01 11:08:38 [0.072s]
...
11:08:38 00:03   [complete]
               SUCCESS

外部のライブラリを使う場合、別途ライブラリを定義する必要があります。

reporoot/
 |
 +- java-sample/
 |   |
 |   +-HelloWorld.java
 |   |
 |   +-BUILD
 |
 +- 3rdparty/
     |
     +-BUILD

3rdparty/BUILDはこんな感じです。java_libraryを列挙することで複数のライブラリを1つのBUILDファイルで定義することができます。

java_library(
  name = 'junit',
  jars = [
    jar('junit', 'junit', '4.12')
  ],
  scope = 'forced',
)

java-sample/BUILDでは以下のようにdependeniesを記述することでライブラリを使用することができます。

java_library(
  name = 'java-sample',
  dependencies = [
    '3rdparty:junit',
  ],
  sources = globs('*.java'),
)

他のプロジェクトを参照する場合も基本的には同じような感じです。以下のような構造のリポジトリで、java-sampleプロジェクトがjava-commonプロジェクトに依存しているとします。

reporoot/
 |
 +-java-sample/
 |  |
 |  +-BUILD
 |  |
 |  +-HelloWorld.java
 |
 +-java-common/
    |
    +-BUILD
    |
    +-StringUtils.java

java-sample/BUILDでは以下のようにBUILDファイルが存在するディレクトリを指定することで対象のプロジェクトを依存関係に追加することができます。

java_library(
  name = 'java-sample',
  dependencies = [
    'java-common',
  ],
  sources = globs('*.java'),
)

BUILDファイルはプロジェクト毎だけでなくディレクトリ毎に作ることができるのでパッケージ単位でBUILDファイルを作成することでビルド単位を細かく分割することができます。また、1つのBUILDファイルで複数のjava_libraryを定義することもできるので同一のディレクトリでも条件によってビルド単位を分けることが可能です。

まとめ

既存のビルドツールとPantsを比較した場合の大きな違いはプロジェクト毎ではなく、ディレクトリ毎など自由な単位でビルドファイルを配置できるということです。「ビルド単位を細分化することでビルドの並列可能性や、キャッシュの有効性を高める」というのが基本的なコンセプトのようです。

他のビルドツールと併用することもできるので*3、デプロイ用のビルドには既存のビルドツールを使用し、ローカルでの開発時や実行頻度の高いプルリクエストのビルドのみPantsで高速化する、というような使い方もありかもしれません。

*1:Buckは元Google社員が作ったそうで、ビルドファイルの記法もBazelそっくりらしいです。

*2:PantsはMono Repository用のビルドツールということもあり、リポジトリのルートディレクトリにインストールして使用することが想定されているようです。

*3:既存のビルドが特定のビルドツール向けのプラグインなどに依存してると厳しいですが…。

GitBucket 4.4をリリースしました

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

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

SQLダンプファイルのインポート

これまでデータベースのダンプはXMLSQLで行うことができましたが、インポートはXMLのみのサポートでした。4.4ではSQLでのインポートがサポートされ、代わりにXMLでのインポート・エクスポートが廃止されています。

f:id:takezoe:20160828120344p:plain

プライベートリポジトリでもgo getをサポート

4.3まではpublicリポジトリに対してのみgo getのサポートが有効でしたが、4.4からはprivateリポジトリに対しても有効になりました。GitBucketはprivateリポジトリに対して認証のないHTTPアクセスが送信された場合、以下のようなHTMLを返します。

<!DOCTYPE html>
<html>
  <head>
    <meta name="go-import" content="localhost:8080/root/hogege git http://localhost:8080/git/root/hogege.git" />
  </head>
</html>

マイルストーンを期限でソートして表示

マイルストーンを期限の昇順にソートして表示するようになりました。

f:id:takezoe:20160828120613p:plain

f:id:takezoe:20160828120621p:plain

apache-sshdを1.2.0にバージョンアップ

apache-sshdを1.2.0にバージョンアップしました。

DSA(ssh-dss)は脆弱性により推奨されておらずOpen SSHでも現在はデフォルトで無効になっています。これまでGitBucketはDSAしかサポートしていませんですたがapache-sshdのバージョンアップによってRSAも利用できるようになりました。

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