GitBucket 4.23.0をリリースしました

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

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

URL末尾のスラッシュを許容するように

URLの末尾にスラッシュがついていてもスラッシュがついていない場合と同じように振る舞うようになりました。これによってJenkinsなどの外部システムとの連携が改善されます。

GitBucketは内部的には末尾がスラッシュで終わるリクエストをスラッシュを取り除いたURLとして扱うようになっています。そのためプラグインでパスの末尾がスラッシュで終わるアクションを定義している場合、そのアクションが呼び出されることはなくなります。プラグイン開発者の方はご注意ください。

リリース一覧にタグのコミットメッセージを表示

リリース一覧に、各リリースに対応するタグのコミットメッセージが表示されるようになりました。このコミットメッセージは新規リリースを作成する際に説明文の初期値としても使用されます。

f:id:takezoe:20180330162754p:plain

イシューとプルリクエスAPIのレスポンスにlabelsプロパティを追加

イシューやプルリクエストの情報を取得するAPIのレスポンスにlabelsプロパティが追加されました。

 [
  {
    "number": 1,
    "state": "open",
    ...
    "labels": [
      {
        "name": "enhancement",
        "color": "84b6eb",
        "url": "http://localhost:8080/api/v3/repos/root/gitbucket/labels/enhancement"
      }
    ],
    ...
  }
]

プラグイン一覧API

インストール済みのプラグインの一覧情報を取得するためのAPI/api/v3/gitbucket/pluginsとして追加されました。このAPIは将来的にプラグインビルドファームで利用されることを想定したものです。

[
  {
    "id": "maven-repository",
    "name": "Maven Repository Plugin",
    "version": "1.3.0",
    "description": "Host Maven repository on GitBucket.",
    "jarFileName": "gitbucket-maven-repository-plugin-assembly-1.3.0.jar"
  },
  {
    "id": "ci",
    "name": "CI Plugin",
    "version": "1.4.0",
    "description": "This plugin adds simple CI functionality to GitBucket.",
    "jarFileName": "gitbucket-ci-plugin-assembly-1.4.0.jar"
  }
]

プライベートアクセストークンによるGit認証

Gitリポジトリの認証時にアカウントのパスワードの代わりにプライベートアクセストークンが使用できるようになりました。特に自動化ツールなどからGitリポジトリにアクセスする場合、セキュリティ上の観点からユーザのパスワードの代わりにアクセストークンを使用するとよいでしょう。

CIプラグインの設定

CIプラグインで並列実行可能なビルド数および保存する履歴の最大数を設定できるようになりました。

f:id:takezoe:20180330162811p:plain

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

なお、今回のバージョンではRELEASEテーブルがRELEASE_TAGテーブルにリネームされ、これに伴ってDBアクセス用のモデルクラスも変更されているため、DBアクセスを行う多くのプラグインでバイナリ互換性が失われています。プラグイン作者の方は必要に応じてGitBucket 4.23.0でビルドし直したバージョンをリリースしていただければと思います。また、プラグイン利用者の方はGitBucket 4.23.0で動作しないプラグインを発見したら各プラグインにフィードバックをお願いできればと思います。

Apache PredictionIOとApache Zeppelinを組み合わせてみる

Apache PredictionIOとApache ZeppelinはどちらもApache Software Foundationのプロジェクトです。PredictionIOはSparkML / MLlibベースの機械学習アプリケーションを開発・運用するためのプラットフォームを提供するもので、ZeppelinはSpark / SparkSQLを使用可能なノートブックを提供するものです。

PredictionIOは学習に使用するイベントデータをRDBMSやHBase、Elasticsearchに保存するのですが、これをZeppelin上でSQLを使って分析することができたら便利ではないかと思いやってみました。本当はPredictionIOが提供しているAPIをそのままライブラリとして使えたらよかったのですが、初期化処理などがPredictionIOのライフサイクルにべったり依存していて厳しそうだったので無理やりラップして簡単に使えるライブラリを作ってみました。

github.com

このライブラリはMavenセントラルにpublishしてあるのでZeppelinのノートブック上で以下のようにロードするだけで使用することができます。

%dep
z.load("com.github.takezoe:predictionio-toolbox_2.11:0.0.1")

以下のようにPIOToolboxクラスを使用してイベントデータをRDDとして取得し、SparkSQLで利用するためにテーブルとして登録します。

import com.github.takezoe.predictionio.toolbox._

// Create toolbox with PIO_HOME
val toolbox = PIOToolbox("/Users/naoki.takezoe/PredictionIO-0.12.1")
val eventsRDD = toolbox.find("MyApp1")(sc)

case class Rating(
  user: String,
  item: String,
  rating: Double
)

val ratingRDD = eventsRDD.map { event => 
  val ratingValue: Double = event.event match {
    case "rate" => event.properties.get[Double]("rating")
    case "buy" => 4.0 // map buy event to rating value of 4
    case _ => throw new Exception(s"Unexpected event ${event} is read.")
  }
  // entityId and targetEntityId is String
  Rating(event.entityId,
    event.targetEntityId.get,
    ratingValue)
}

val df = spark.createDataFrame(ratingRDD)
df.registerTempTable("rating")

するとSQLで検索することが可能になります。

%sql
select rating, count(*) from rating group by rating

f:id:takezoe:20180330140251p:plain

PIOToolboxクラスにはこの他にもRDDをイベントデータとして登録するメソッドなどがあります。利用可能なメソッドについてはソースコードを参照していただければと思います。

ノートブックというとJupyterの利用者が多いのではないかと思いますが、ZeppelinはデフォルトでScala / Spark / SparkSQLを利用することができるので、同じくScala / Sparkベースの機械学習プラットフォームであるPredictionIOとは相性が良さそうです。PredictionIOで機械学習アプリケーションを開発する場合、学習データのインポートや前処理などにZeppelinを使うのはありかもしれないと感じました。

Twilio社がOSS化したScala用SwaggerコードジェネレータGuardrailを試してみる

先日のScalaMatsuri 2018でTwilioの方が発表されていた、SwaggerからAkka HTTP用(http4sにも対応している)のコードを生成するコードジェネレータがGitHubで公開されていました。

github.com

発表を聞いていて良さげな感じだったのでちょっと試してみました。

まずはGitHubからリポジトリをクローンしてきます。

$ git clone https://github.com/twilio/guardrail.git
$ cd guardrail

コード生成にはルートディレクトリのcli.shというシェルスクリプトを使います。ソースツリーにサンプルのSwaggerファイルが含まれているのでこれを指定してAkka HTTP用のコードを生成してみます。クライアント側とサーバ側のコードをそれぞれ生成できるようです。

$ ./cli.sh --client --specPath modules/codegen/src/main/resources/petstore.json --packageName petstore --outputPath /tmp/petstore-client/src/main/scala
$ ./cli.sh --server --specPath modules/codegen/src/main/resources/petstore.json --packageName petstore --outputPath /tmp/petstore-server/src/main/scala

このとき--framework http4sというオプションを付けるとhttp4s用のコードを生成することもできるようです。

生成されるのはコードのみなので実行するにはbuild.sbtなどを別途作成する必要があります。それぞれのプロジェクトに以下のような感じのbuild.sbtを作成しました。

name := "petstore-server"
version := "1.0"
scalaVersion := "2.12.5"
scalacOptions := Seq("-unchecked", "-deprecation", "-encoding", "utf8")
libraryDependencies ++= {
  val akkaHttpV   = "10.0.10"
  val catsVersion = "0.9.0"
  val circeVersion = "0.8.0"
  Seq(
    "com.typesafe.akka" %% "akka-http" % akkaHttpV,
    "io.circe" %% "circe-core" % circeVersion,
    "io.circe" %% "circe-generic" % circeVersion,
    "io.circe" %% "circe-java8" % circeVersion,
    "io.circe" %% "circe-parser" % circeVersion,
    "org.typelevel" %% "cats" % catsVersion
  )
}

ではサーバから見ていきましょう。実際に処理を行うハンドラ部分がトレイトとして生成されているのでこれを実装する必要があります。

object Main extends App {
  implicit val system: ActorSystem = ActorSystem("helloAkkaHttpServer")
  implicit val materializer: ActorMaterializer = ActorMaterializer()
  implicit val executionContext: ExecutionContext = system.dispatcher
  
  val userHandler = new UserHandler {
    override def getUserByName(respond: UserResource.getUserByNameResponse.type)(username: String): Future[UserResource.getUserByNameResponse] = {
      Future(respond.OK(User(username = Some("takezoe"))))
    }
    override def createUser(respond: UserResource.createUserResponse.type)(body: User): Future[UserResource.createUserResponse] = {
      Future(respond.OK)
    }
    // TODO: Implement other methods
    ...
  }

  val routes = UserResource.routes(userHandler)
  val serverBindingFuture: Future[ServerBinding] = Http().bindAndHandle(routes, "0.0.0.0", 8081)
  ...
}

ハンドラの各メソッドの最初の引数リストにはレスポンスのためのオブジェクトの型、2つめの引数リストにはSwaggerで定義したパラメータ群が渡されてきます。レスポンスのステータスもSwaggerで定義したものしか返せないようタイプセーフになっているのが嬉しいところです。

続いてクライアントサイドです。こちらはHttpClientを定義する必要があります。これはAkka HTTPのHttpRequestを受け取りFuture[HttpResponse]を返す関数のエイリアスなのですが、トレーシングなどのために処理を挟めるよう関数で抽象化されているようです。

object Main extends App {
  implicit val system: ActorSystem = ActorSystem("helloAkkaHttpClient")
  implicit val materializer: ActorMaterializer = ActorMaterializer()
  implicit val executionContext: ExecutionContext = system.dispatcher
  implicit val httpClient: HttpClient = (request: HttpRequest) => Http().singleRequest(request)

  val client = UserClient("http://localhost:8081")

  val result = for {
    _    <- client.createUser(User(username = Some("takezoe")))
    user <- client.getUserByName("takezoe")
  } yield user

  val user = Await.result(result.value, Duration.Inf)
  println(user)
}

クライアントの各メソッドの戻り値はCatsのEitherTになっているのでfor式で簡単に合成することができます。また、クライアント、サーバ共に生成されたコードを直接編集せずに処理を実装することができるようになっているので、Swaggerファイルを変更した場合は単純にもう一度コードを生成しなおせばよさそうです。

ScalaのWebフレームワークにおけるSwaggerとの連携機能は、コードからSwaggerのAPI定義を生成することを目的としたものが多く、API定義をDSLソースコードに埋め込むものが多いのですが、これだと二度手間ですし、実装と定義の整合性をプログラマが担保しなくてはなりません。GuardrailはSwaggerを正としてコード生成を行うので二度手間になりませんし、スペックに従った実装であることをツールで担保できる有用なアプローチなのではないかと思います。

ちなみに試しに生成してみたAkka HTTPサーバとクライアントのソースコードは以下のリポジトリに置いてあります。

github.com

自動テストでElasticsearchを使うためのJavaライブラリ

その昔、ElasticsearchではEmbeddedモードという、アプリケーションに組み込んで使用したり、自動テストで使用するのに便利な機能がサポートされていました。しかし、この機能はElasticsearch 5.0でサポートされなくなってしまいました。詳しくはElastic社の以下のブログ記事に書かれていますが、Embeddedモードはセキュリティマネージャーや起動時のチェックなどをバイパスしてしまうなど様々な問題があったようです。

www.elastic.co

確かに理由はわからないでもないですが、とはいえ自動テストのためにはやはりEmbeddedモードのようなものがあると便利です。そこで利用できるのがelasticsearch-cluster-runnerというJavaライブラリです。

github.com

このライブラリを使うとElasticsearchクラスタをコードから制御することが可能になります。実際にelastic-scala-httpclientやAlpakka(のElasticsearchコネクタ)のテストでこのライブラリを使用してみましたが、非常に便利なものでした。

以下はJavaでのelasticsearch-cluster-runnerを使ったテストケースの例です。

public class ElasticsearchTest {
  private static ElasticsearchClusterRunner runner;
  @BeforeClass
  public static void setup() throws IOException {
    runner = new ElasticsearchClusterRunner();
    runner.build(ElasticsearchClusterRunner.newConfigs()
        .baseHttpPort(9200)
        .numOfNode(1)
        .disableESLogger());
    runner.ensureYellow();
  }
  @AfterClass
  public static void teardown() throws Exception {
    runner.close();
    runner.clean();
  }
  ...
}

この場合、ElasticsearchのRESTインターフェースはlocalhost:9201で起動します。見ての通り、ノード数を指定することもできるのでクラスタ環境でのテストも簡単に行うことができます。なお、elasticsearch-cluster-runnerは並列実行には対応していないので、テストで使用する場合は各テストをシリアルに実行する必要があるという点だけ注意が必要です。

JUnitなどでElasticsearchを使ったテストを行うにはうってつけのライブラリだと思いますので是非試してみていただければと思います。

ScalaDays 2018 NewYorkの気になるセッション

ScalaDays 2018 NewYorkのタイムテーブルが公開されていましたので、完全に個人的な趣味で気になるセッションを列挙してみたいと思います

na.scaladays.org

  • Type-driven Development (Workshop) カンファレンスの前日と前々日行われるワークショップでは「Type-driven development」というワークショップがあります。これは…と思ったのですが、どうやらIdrisのワークショップというわけではないようですw 残念。
  • Networks and Types — the Future of Akka 今楽努さんことKonradさんは先日西新宿で開催されたReactive System Meetupでも発表されていたAkka Typedの話をされるようです。日本で先行して聞けたのはラッキーでした。
  • Slinky: a modern toolkit for modern apps ScalaDays常連スピーカーのShadajさんによるSlinkyというScala.js用のReactインターフェースの紹介。これまでもScala.js用のReact用ライブラリはいくつかありましたが、これは結構本気度を感じますがどうでしょうか。JSX相当の部分は独自のタグAPIを使うみたいなのでデザイナとの共同作業は難しそうですが…。
  • How we built tools that scale to millions of lines of code マクロでお馴染みEugene Burmakoさんですが、今回はTwitter社内でコードレビュー、コードブラウジングなどを行うツールを開発する際にScalaのセマンティックツールを大規模なコードベースに対応させるためにどのようにスケールさせたかという話。ちょっと想像がつかないのですが興味深いです。
  • Serverless Scala.js iOS/Android/Web apps from one code base Scala.jsとScala-Nativeを使って1つのScalaコードベースでWeb、iOSAndroid用のUIアプリを開発した経験を話すらしい。仕事でこんなことしたら確実に怒られが発生しそうですが、人の話を聞く分には楽しい気がします。
  • Meet bloop and get more productive with Scala Scala Centerの方々によるBloopのセッションもあります。以前このブログでも紹介したのですが、非常に微妙な立ち位置のツールのような気がするのですが、本気でやるつもりがあるのかどうか気になるところです。
  • Compiling Scala Faster with GraalVM Oracle社のChristianさんのセッションで、Graalを使ってScalaコンパイルを速くするという話らしい。
  • sbt 1 Yokotaさんとdwijnandさんによるsbt1のセッション。自分は参加できなかったのですが、YokotaさんはScalaMatsuriでもsbt1について話されていましたね。KonradさんのAkka Typedの話もそうですが、日本でも同じ話を聞ける機会があるというのは有難いことです。
  • ScalaQuest: the Scala adventure クラウドファンディングもしていたScala教育用ゲーム「ScalaQuest」の進捗報告とのこと。Scala Warriorと似たコンセプトのアプリケーションなので技術的なところに興味があります。
  • sttp: the Scala HTTP client that you always wanted! SoftwareMillのAdamさんによるセッションで、Scala業界にまた新たに爆誕してしまったHTTPクライアントsttpを使ったライブコーディングだそうです。いろいろ爆誕しがちなのはScala業界らしくていいと思います。ScalaSphereでビルドツール対決とかやって欲しい。
  • Twitter's quest for a wholly Graal runtime Twitter社におけるGraalの話。Graalで良いコードを生成することでコストを削減するという、Scalaで大規模な分散システムを運用しているTwitter社ならではの発表ですね。

全体的な傾向として、ことScalaDaysに関してはFreeモナドブームは完全に去った感じがあります。Graalのセッションが2つあったり、ここでは取り上げませんでしたがSparkのセッションが2つあったりと、Scalaエンタープライズ寄りな雰囲気が出てきたかなという気がしますが、一方でScala.jsやScala-Nativeも依然として注目度が高いようです。

最終兵器トラックポイント付きメカニカルキーボード「TEX Yoda II」レビュー

f:id:takezoe:20180312122502j:plain
最終兵器「TEX Yoda II」

初代を買い逃してから早数年、2代目が発表され入手できる日を心待ちにしていたのですが、先日ようやくゲットすることができました。数日使ってみてのレビューです。

Massdropで組み立てキットも販売されていましたが、面倒なので以下のサイトから赤軸、バックライトなしの組み立て済みモデルをオーダーしました。到着まで3週間ほどかかりました。また、関税がかかったため受け取り時に支払いが必要でした(送付先を会社にしていたので社で怒られが発生した)。

mechanicalkeyboards.com

触ってみてまず感じたのはトラックポイントの感度が良すぎるということです。スイッチの高さの分、本家ThinkPadキーボードより支柱が長いせいでしょうか。Mac側の設定でマウスカーソルの移動速度を最低にして使っています。ボタンが通常のキーと同じスイッチなのも最初はかなり違和感がありましたが、現行モデルのThinkPad USBキーボードと異なり、Macでも横スクロールが可能でした。また、センターボタンは実はFnキーなので、センターボタンでFnキーのコンビネーションを利用可能です。これは慣れると結構便利そうです。

キーボード部分については、筐体のサイズはHHKBと同じくらいなのですが、キー配列がHHKBと結構違うので最初はかなり戸惑いました(まあこれはHHKBがおかしいという説もあります)が、TEX Yoda IIはキー配列のカスタマイズの自由度が非常に高いのでHHKB風の配列に変更したところだいぶ快適になりました。キー配列の設定は以下のWebサイトで設定ファイルを作成し、それをTEX Yoda IIに書き込むという方法で行います。

yoda2.tex-design.com.tw

Fnキーとのコンビネーション含めてあらゆるキーを変更できるので自分の好みの配列にすることができます。ただ、HHKBとは物理的にキー配置が違うので完全に同じ配列にすることができなかったり、Fnキーとのコンビネーションを変更するとキートップの表記と紛らわしかったりするなどの問題があります。特にESCキーとbackquote、tildeが被っているのが不便なのでなんとかしたいのですが、キーの数が足りないのでどうするか迷い中です。

なお、一度生成したファイルを読み込んで編集することはできないのですが、サインインすると設定を保存しておくことができるので、キー配列を試行錯誤するのであればサインインしておくと便利です。

f:id:takezoe:20180312123206j:plain
HHKBとの比較 その1

f:id:takezoe:20180312123224j:plain
HHKBとの比較 その2

スイッチは赤軸と茶軸で迷って結局赤軸にしたのですが、軽いタッチで入力できていい感じです。ちょっとタッチが安っぽい感じもしますが、これはキーキャップの差によるところもあるかもしれません(バックライト付きのモデルはキーキャップが違うはずなので微妙にタッチが異なる可能性もあります)。ケーブル接続はUSB-Cで、付属のケーブルはL字型なので奥行きのない場所にも設置できます。ただ、筐体の重量がかなりあるので尊師スタイルで使うにはやや不安もあります。チルトスタンドはネジ式の足を取り付けるという漢らしい仕様。ゴム足もついていますし前述の通り重量もあるので安定感は抜群です。

f:id:takezoe:20180312123321j:plain
チルトスタンドはネジで取り付ける方式

f:id:takezoe:20180312123338j:plain
L字型のUSBケーブルが付属

トラックポイントについて少し補足ですが、キャップはクラシックドーム、ソフトリム、ソフトドームの三種類が付属していました。ThinkPad純正のものと同じ形状のようなので交換も容易そうです。最初からついているものだとちょっと高さがあってキー入力時に指に引っかかってしまうことがあるのでロープロファイルのキャップに交換してもいいかもしれません。

Lenovo ThinkPlus トラックポイント・キャップ・コレクション [73P2698]

Lenovo ThinkPlus トラックポイント・キャップ・コレクション [73P2698]

欠点を挙げるとすれば、コンパクトキーボードではあるものの、持ち運びにはまったく適していないという点でしょうか。重量もありますし、トラックポイントもキャップをつけたままカバンの中に入れたりすると引っかかって支柱が折れてしまうのではないかと不安になります。持ち運び時は注意した方がよさそうです。

あれこれ書きましたが、トラックポイント付きメカニカルキーボードという点では唯一無二の存在なのでみんな買うといいと思います。

GitBucket 4.22.0をリリースしました

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

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

プルリクエストのマージ方法の設定

リポジトリ毎にデフォルトのマージ方法と、選択可能なマージ方法を設定できるようになりました。

f:id:takezoe:20180303100516p:plain

イシューでのアクションの記録を強化

イシュー、プルリクエストで以下の操作も履歴として記録されるようになりました。

f:id:takezoe:20180303100702p:plain

空コミットでリポジトリを作成可能に

リポジトリ作成時に空コミットで初期化されたリポジトリを作成できるようになりました。

f:id:takezoe:20180303100757p:plain

データベースビューアの改善

データベースビューアにいくつかの機能が追加されました。

  • 左側のツリーでカラムを選択した場合にそのカラムを検索するSQLが生成されるようになりました。
  • "Auto query"チェックボックスが追加されました。チェックしておくと左側のツリー項目を選択した際に自動的にSQLが実行されます。

f:id:takezoe:20180303101136p:plain

maven-repository-pluginのアップデート

非公式プラグインですが、maven-repository-pluginがアップデートされました。

管理画面で任意のリポジトリを追加できるようになり、リポジトリ毎にアーティファクトの上書きが可能かどうか、全員に後悔するかどうかを設定できます。非公開リポジトリはアクセスするためのGitBucketアカウントでのBASIC認証が必要です。

f:id:takezoe:20180303101336p:plain

また、管理者限定でブラウザからリポジトリ内のファイル、ディレクトリを選択して削除できるようになりました。これは上書き不可能なリポジトリでも利用可能です。

f:id:takezoe:20180303101434p:plain

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