このページを正しく表示するにはJavascriptを有効にしてください。
SBTで複数の設定を切り替えてビルドする
Scala+SBTの環境で開発環境用と本番用の設定を切り替えて実行したり、jar(war)を作ったりしたいと思ったのですが、デファクトみたいなのが見当たらなかったので、自分なりに調べて実現方法を検討してみました。
## ゴール
やりたいこととしては、sbt上で
```sbt
run
```
とすれば開発環境用の設定(データベースなど)でソースがコンパイルされ、ローカルで実行される。
```sbt
package
```
とすれば本番用の設定でコンパイルされ、そのままサーバーに置くためのjarが生成される。
(状況に応じてコマンドが異なったり、別途引数をとってもよい)
みたいな感じです。
## 実現方法
### 概要
SBTのマルチプロジェクトという仕組みと、「Typesafe Config」というライブラリを組み合わせる。
SBTのマルチプロジェクトを利用すると、プロジェクトを選択してビルドするという事が可能になるので、これを使うことで開発環境用のビルドと本番用のビルドを切り分けることが出来る。ただしSBTのマルチプロジェクトから操作できるのは、ビルド時のプロジェクト設定のみのため、Scala(Java)側のコードを直接変更することは難しい。そこでTypesafe Configというライブラリを利用することで、プロジェクトのリソース経由でプロジェクトごとの設定を与える事が出来る様になる。
### 実装手順
#### プロジェクトをマルチプロジェクト化する
まず今あるプロジェクトをマルチプロジェクト化します。
```
#build.scala(Before)
object AppBuild extends Build {
lazy val project = Project (
"app",
file("."),
settings = Seq(
organization := Organization,
name := Name,
version := Version,
scalaVersion := ScalaVersion,
resolvers += Classpaths.typesafeReleases,
libraryDependencies ++= Seq(
//..........
)
)
)
}
```
今あるプロジェクトのSBTがこのような感じだとすると、共通のSettingを抜き出して定義した後、別のプロジェクトを作成します。
```
# build.scala(After)
object AppBuild extends Build {
//共通部分を抜き出す
//別のプロジェクト側で上書きも可能なのでごっそり抜き出してよい
lazy val commonSettings = Seq(
organization := Organization,
name := Name,
version := Version,
scalaVersion := ScalaVersion,
resolvers += Classpaths.typesafeReleases,
libraryDependencies ++= Seq(
//..........
)
)
)
// デフォルトのプロジェクトをcommonSettingsで置き換え
lazy val project = Project (
"app",
file("."),
settings = commonSettings
)
// 本番用のプロジェクトを作成
lazy val prod = Project (
"app_prod", //プロジェクトID
file("prod"), //プロジェクトの成果物をどこに作成するか
settings = commonSettings ++ Seq(
// ここでcommonSettingsを独自に上書きしたり追加したりできる
name:= "prod" //プロジェクト名を上書き
)
).dependsOn(project)
}
```
こう書き換える事によって複数のプロジェクトを簡単に使い分けることが出来るようになります。
使い方としては
```sbt
run
#=> デフォルトプロジェクト(= appプロジェクト)がrunする
app_prod/run
#=> app_prodプロジェクトがrunする
```
みたいにプロジェクト名 + スラッシュを実行したいSBTコマンドの前に入れてあげれば切り替えることが出来ます。
#### プロジェクトごとに設定を切り替える
プロジェクトの切り分けが出来たので今度はソースに対するの設定の切り替えを行います。
```
# build.scala
"com.typesafe" % "config" % "1.2.1"
```
Typesafe Configを利用するため```commonSettings```の```libraryDependencies```に上記を追加します。
次に ```src/main/resources/``` の中に
```
#application.conf
sample_config {
environment = "dev"
}
```
```
# application_prod.conf
sample_config {
environment = "prod"
}
```
のように2つファイルを作成します。
__※ ```application.conf``` の方はこのファイル名を決め打ちする必要があります。__
次に、Scalaのコードからここで定義したsample_config.environmentを取り出してみます。
```
val sampleConfig = ConfigFactory.load().getConfig("sample_config")
val environment = sampleCOnfig.getString("environment")
println(environment)
// => "dev"
```
これでapplication.confの値を取り出すことが可能になります。このconfファイルの構造は結構自由に定義できるみたいです。
ただし、このままではプロジェクトに関係なくデフォルトの```application.conf```が読まれてしまうので、プロジェクトに応じて異なる設定を読みに行くよう修正を加えます。
```
# scala:build.scala
javaOptions += "-Dconfig.resource=application_prod.conf",
```
```prod```プロジェクトのsettingsに上記を追記します。この指定により、```prod```プロジェクトをビルドした時には```application_prod.conf```の中身を参照するようになります。
```sbt
app_prod/run
```
```
val sampleConfig = ConfigFactory.load().getConfig("sample_config")
val environment = sampleCOnfig.getString("environment")
println(environment)
// => "prod"
```
#### 開発環境で実行、本番用のパッケージ作成を使い分ける
当初の目的だった使い分けは
```sbt
run
#=> 開発環境用設定で実行
app_prod/package
#=> 本番用の設定でパッケージを作成
```
こんな感じで実現することが出来るようになりました。