HerokuでScalaやろうとしたら、すごくはまったけど、とりあえずHello worldできたっていう話

10日くらい前にHerokuでのScalaのサポート発表されましたよね

http://blog.heroku.com/archives/2011/10/3/scala/
http://devcenter.heroku.com/articles/scala

herokuも無料枠あるわけだし、とにかくやってみよう(・ω・´)というわけで、やろうとした・・・・がしかし!

そしたらナカーマが

この時点で数時間格闘していたわけですが、悔しくて諦めがつかずその後も頑張る・・・(´・ω・`)

なんか原因わかったっぽい?

結論から言うと、(推測があっていれば) sample にある、typesafe の xsbt-start-script-plugin が原因(あとで書くけど、こいつが100%悪いというわけでもなく、heroku側も悪い・・・? )

サンプルには、 xsbt-start-script-plugin という、以下のような*1起動スクリプトを生成*2するプラグインを使えって書いてあります

#!/bin/bash

function die() {
    echo "$*" 1>&2
    exit 1
}
test -x './target/start' || die "'./target/start' not found, this script must be run from the project base directory"


MAINCLASS=Web


java $JAVA_OPTS -cp "./target/scala-2.8.1/classes:/home/kenji/.sbt/boot/scala-2.8.1/lib/scala-library.jar:/home/kenji/.ivy2/cache/com.twitter/finagle-core/jars/finagle-core-1.9.0.jar:/home/kenji/.ivy2/cache/com.twitter/util-hashing/jars/util-hashing-1.11.4.jar:/home/kenji/.ivy2/cache/com.twitter/util-core/jars/util-core-1.11.4.jar:/home/kenji/.ivy2/cache/com.twitter/util-collection/jars/util-collection-1.11.4.jar:/home/kenji/.ivy2/cache/com.google.guava/guava/jars/guava-r09.jar:/home/kenji/.ivy2/cache/commons-collections/commons-collections/jars/commons-collections-3.2.1.jar:/home/kenji/.ivy2/cache/org.jboss.netty/netty/bundles/netty-3.2.5.Final.jar:/home/kenji/.ivy2/cache/com.twitter/finagle-http/jars/finagle-http-1.9.0.jar:/home/kenji/.ivy2/cache/com.twitter/util-logging/jars/util-logging-1.11.4.jar:/home/kenji/.ivy2/cache/commons-lang/commons-lang/jars/commons-lang-2.6.jar" "$MAINCLASS" "$@"

exit 0

で、twitterでつぶやいたとおり、これの3行目で、シンタックスエラーになってたみたいです(´・ω・`)ェ・・・

(ちゃんと調べてないから推測ですが)、heroku で scala をやる場合は、 ソースコードとsbtのプロジェクトの設定のファイルをgit push するだけで、jarなどを直でデプロイするわけではないようです。おそらく、heroku側のサーバーで、コンパイルして、そちらで生成されたclassファイルや依存ライブラリにパスを通して実行する仕組みになってるようです。*3で、依存ライブラリのパスを通して起動するためのファイルを自動生成するために xsbt-start-script-plugin を使うことを推奨してるってことなのでしょう。
で、こいつが、"#!/bin/bash" となっていたり、 "function die() " と書いてあったりするように bashでないと動かない! みたいですが、どうやらheroku側のサーバーで何らかの原因で bash として実行できなかったようで、そもそも上記の起動スクリプトが動いてなかったようです!

というわけで、 xsbt-start-script-plugin を fork して、とりあえず雑にいらない部分を消して、

https://github.com/xuwei-k/xsbt-start-script-plugin/commit/b1089a09a61ee27a92dc3aa8149f0588c0dfbaac

project/build.sbtは削除し、以下のようなファイルを"project/project/Build.scala" として保存して、自分が変更したversionのxsbt-start-script-pluginを直接参照*4

import sbt._

object PluginDef extends Build{

  lazy val root = Project("plugins", file(".")) dependsOn( scriptPlugin )
  lazy val scriptPlugin = uri("git://github.com/xuwei-k/xsbt-start-script-plugin")

}

すると動きました・・・(´・ω・`)疲れた・・・

typesafeがサンプル作ったときは、heroku上でbashが実行できてたんですかね・・・?xsbt-start-script-plugin自体が100%悪いわけでもないし*5・・・これは、pull request送るべきなのか、送るとしても、今のままじゃ雑に直したというか、無理やり一部消しただけなので、どうしようか・・・

そもそも、最初の方の行で、"test -x './target/start'"とやっていて、自分自身の実行権限調べてる(?)けどなんかおかしくないか・・・?shとbashの違いとか、その他UNIX系のシェルの知識あまりないから、どうすればbestなのかよくわからん(・ω・`)あとxsbt-start-script-plugin自体はwarに対応していたり、ある程度汎用的なものだから、色々理由があるのかもしれないけれど・・・

もうちょっと詳しいこととがわかったり、なにか進展あれば、追記するか、新しくエントリを書きます。


追記:

heroku scale web=1

というように、接続数の設定(?)をやらないと、サンプルの手順の通りやっただけでは動かない気が・・・?その辺の設定の概念とか(無料枠の場合の)上限とか全然把握してないので勉強しないと・・・

*1:これ実際にローカルに吐かれていたファイル

*2:targetの下にstartというファイル名で生成される

*3:もしくは、git pushのときの、gitのhookでなにかやってる?

*4:sbtには、以下のように、uriというメソッドによって、github上のプロジェクトを直接参照できる機能がある

*5:いやしかしこれ色々作りが雑なきがしなくもないがががが