the industrial

都内で働くITエンジニアの日記

Playframework2→Bitbucket→HerokuのデプロイをCodeshipを利用して実現

「読まれるにたるものを書くか、書かれるにたることをしろ」 – ベンジャミン・フランクリン
いやー、無理っす。でも、書く事で自分の中に根付くという感じがするので、今日も覚えた事を書く笑

Play使ってちまちまWebアプリを作っていて、Heroku上で動かそうと思ってる。
たまにHeroku上で動作確認したくなる。でもソース管理はBitbucketを使いたいと思ってる。
だから、BitbucketとHerokuの両方にPushしないと行けないのがめんどくさくなって、なんか良い方法無いのかと思ってちょっと調べてみた。
そしたら、Codeshipというのが面白そうだったのでやってみた。

タイトルはカッコ良く「〜実現」とか書いてあるんだけど、Codeshipの設定をして上げるだけで良いのでめっちゃ簡単でした。
今回myappというPlayアプリを使って、Bitbucketにpush→Codeship経由でHerokuにデプロイとうのをやってみまーす。



Play App作成

$ play new myapp
$ play run


はい、オッケーでーす。



Bitbucketにリポジトリを作成


Bitbucketを利用する上で、SSH設定やなんかはやっておきましょう。
どうでもいいけど、言語を選択するときに「Scala」が出てこないサービスはどうかと思うんだ。ナントカナビとかw


はい、オッケーでーす。



Bitbucketにプッシュ


$ git init
$ git add .
$ git commit -m "MyApp初回コミット"
$ git remote add origin git@bitbucket.org:omiend/myapp.git
$ git push -u origin --all


はい、オッケーでーす。



Herokuのアプリケーション作成

とりあえずこんな感じで。いつもはHeroku Toolbeltから作成していたんだけど、まあ今回は画面からアプリを作成。


はい、オッケーでーす。



Codeshipの設定

いよいよ今回のメイン、Codeshipの設定。Sign upやらなんやらはてきとうに済ませておく。
Create a new Projectする。


Bitbucketを選択。Githubもあるので、同じ手順でGithubからも出来ると思う。
僕はシャイな人間なので、プライベートリポジトリーがただで使えるBitbucketをいつもつかってマンモス。



CodeshipからBitbucketにログインしておけば、このように先ほど作成したBitbucketのリポジトリーが選択出来る。これを選択。


次が結構キモかな?Testの設定。

Select your technology to prepopulate basic commands:PlayってかScalaJVM上で動くので、「Java」を選択。
Modify your Setup Commands:「bundle install」とか書いてあるので、Railsで使うんでしょう。この辺りの細かい設定は後で試してみる。(まあ書いてある通りなんだけど笑)
Modify your Test Commands:ここでコメントアウトされている「sbt test」を有効化しておく。
「play test」コマンドが出来ないからどうしようと思ってたけど、良く見たらsbt testで良かった。そりゃそうだ。sbt最高。


そしたらこんな画面が出るので、「Follow this link and add the url below as 'POST' hook to your repository:」をコピーしておく。


続いてHerokuの設定。
Project SettingsのDeploymentから、Herokuを選択し、Application Name(Heroku)とAPI Key(Heroku)を設定して保存。
ここら辺でDBのmigrate設定とか出来ると思うけど、取りあえず保留。







Bitbucketのフック設定


Bitbucketのリポジトリー設定からフックの設定を行う。
ドロップダウンから「POST」を選択し、URLに、Codeshipで取得したBitbucket push hookのURLを設定して保存。


BitbucketにPush


ここまでCodeshipの設定が完了したら、Bitbucketに改めてpush。
ソースは適当に更新したものとしておきマンモス。

Pushが成功すると、CodeshipのDashbordでインテグレーションの様子が確認出来る。
sbt test も回ってるのが分かる。このテストが成功すれば、自動的にHerokuにもPushされるハズ。


完了したのがこちら。
やや時間かかってるけど、まあこんな物か。


Herokuの確認

ok。(Procfile作るの忘れたのでこんな画面)



テストが失敗するケース


BitbucketにPushして、Codeshipでテスト実行し、エラーだったらHerokuへのデプロイが行われない事を確認してみる。
なので、わざとテストが失敗するケースを追加。

myapp/test/ApplicationSpec.scala
import org.specs2.mutable._
import org.specs2.runner._
import org.junit.runner._

import play.api.test._
import play.api.test.Helpers._

/**
* Add your spec here.
* You can mock out a whole application including requests, plugins etc.
* For more information, consult the wiki.
*/
@RunWith(classOf[JUnitRunner])
class ApplicationSpec extends Specification {
"Application" should {
"send 404 on a bad request" in new WithApplication{
route(FakeRequest(GET, "/boum")) must beNone
}

"render the index page" in new WithApplication{
val home = route(FakeRequest(GET, "/")).get
status(home) must equalTo(OK)
contentType(home) must beSome.which(_ == "text/html")
contentAsString(home) must contain ("Your new application is ready.")
}

"わざとテストNGになるケース" in new WithApplication{
val home = route(FakeRequest(GET, "/hoge")).get
status(home) must equalTo(OK)
contentType(home) must beSome.which(_ == "text/html")
contentAsString(home) must contain ("Your new application is ready.")
}
}
}


試しにsbt testして、エラーになる事を確認。

$ sbt test
[info] Loading project definition from /Users/myapp/project
[info] Set current project to myapp (in build file:/Users/myapp/)
[info] Compiling 1 Scala source to /Users/myapp/target/scala-2.10/classes...
[info] Compiling 1 Scala source to /Users/myapp/target/scala-2.10/test-classes...
[info] IntegrationSpec
[info] Application should
[info] + work from within a browser
[info] Total for specification IntegrationSpec
[info] Finished in 8 seconds, 325 ms
[info] 1 example, 0 failure, 0 error
[info] ApplicationSpec
[info] Application should
[info] + send 404 on a bad request
[info] + render the index page
[info] ! わざとテストNGになるケース
[error] NoSuchElementException: None.get (ApplicationSpec.scala:31)
[error] ApplicationSpec$$anonfun$3$$anonfun$apply$6$$anon$3$delayedInit$body.apply(ApplicationSpec.scala:31)
[error] play.api.test.WithApplication$$anonfun$around$1.apply(Specs.scala:20)
[error] play.api.test.WithApplication$$anonfun$around$1.apply(Specs.scala:20)
[error] play.api.test.PlayRunners$class.running(Helpers.scala:45)
[error] play.api.test.Helpers$.running(Helpers.scala:364)
[error] play.api.test.WithApplication.around(Specs.scala:20)
[error] play.api.test.WithApplication.delayedInit(Specs.scala:17)
[error] ApplicationSpec$$anonfun$3$$anonfun$apply$6$$anon$3.(ApplicationSpec.scala:30)
[error] ApplicationSpec$$anonfun$3$$anonfun$apply$6.apply(ApplicationSpec.scala:30)
[error] ApplicationSpec$$anonfun$3$$anonfun$apply$6.apply(ApplicationSpec.scala:30)
[info] Total for specification ApplicationSpec
[info] Finished in 805 ms
[info] 3 examples, 0 failure, 1 error
[error] Error: Total 4, Failed 0, Errors 1, Passed 3
[error] Error during tests:
[error] ApplicationSpec
[error] (test:test) sbt.TestsFailedException: Tests unsuccessful
[error] Total time: 34 s, completed 2014/04/06 14:39:36

BitbucketにPushする。


Codeshipで監視。


無事、エラーになってストップしました。いい感じ。


エラーでデプロイがこけた場合、こんな感じでメールくれます。
素晴らしい。実用出来るね。