herokuにデプロイするときでもgruntしたい!(+rubyサーバー)
JSの勉強のためにタイトルのような構成のwebアプリをherokuにデプロイしようとしたところ、主にjsのライブラリ管理やcoffeeのコンパイルなどで苦労したのでメモ。
rubyだとjs周りも面倒見てくれるRails関係のgemが色々あるみたいですが、jsは独立して管理した方がいいという風潮を最近感じているのでgemには一切依存しない形にしてみました。
(試していませんがサーバー側をPythonやPerlに置き換えても問題ないはず)
構成
サーバー: ruby(sinatra)
クライアント: coffee-script(gruntでjsにコンパイル)
jsライブラリ: BoostrapとかjQueryなど(bowerでインストール)
サーバー側
サーバー側でやることは何もありません。
js周りの面倒を見てくれるgemを一切入れないので、sinatraで簡単なrubyサーバーをherokuにプッシュするために最低限必要なGemfileとProcfileだけあればよいです。
Gemfile
ruby "2.1.2" gem "sinatra" gem 'haml' group :development do gem 'foreman' gem 'heroku' end
Procfile
本当はunicornとか使うべきなのだろうけど、sinatraのデフォルトでもとりあえず動く。
web: bundle exec ruby app.rb -p $PORT
クライアント側
今どきのjsはbowerでライブラリを管理するらしいので、BootstrapやjQueryといったライブラリをbower install --save
でインストールします。
--saveを付けて必要なライブラリをbower.jsonで管理することで、外部ライブラリをgitで管理せずともherokuにデプロイしたときに向こう側でインストールできるようにしておきます。
coffeeもコンパイルするたびにgitにコミットするのは大変なので、コンパイルされたjsはgitに追加せず元のcoffeeだけgitで管理します。
問題点
という方針で開発を進めていたのですが、いざherokuへのデプロイという段階で壁にぶつかってしまいました。
herokuのbuildpackは単一言語しか動かない
herokuのbuildpackは基本的に1つのプログラミング言語しか動かないので、この構成ではrubyは動くがnodeが動かずbowerでjsライブラリのインストールができませんでした・・・
この問題を解決するheroku-buildpack-ruby-bowerというサードパーティのbuildpackもあるようですが、今後もメンテされるかが不安なので公式のbuildpackで解決する方法を探してみました。
解決策
mulit_buildpack
で、見つけたのがこのmulti_buildpack。
これを使うと.buildpackに定義した複数のbuildpackを1つのインスタンスに入れられるので、rubyとnode.jsのbuildpackを両方とも入れてしまうことでbowerも使えるようになります。
multi_buildpackを使う場合には、herokuアプリを最初に作成するheroku createで使用するbuildpackの指定をmulti_buildpackにする必要があります。
heroku create -b https://github.com/heroku/heroku-buildpack-multi.git your-app
次に、以下のように公式のrubyとnodejsのbuildpackを定義した.buildpacksというファイルを作ってからherokuにデプロイをすることで、めでたくrubyとnodejsの両方が動くインスタンスになります。
.buildpacks
https://github.com/heroku/heroku-buildpack-nodejs https://github.com/heroku/heroku-buildpack-ruby
ちなみにこの記事を書く前にはこの方法を各所で見みかけたのですが、何気にheroku公式ドキュメントを見たらheroku buildpacks:setとheroku buildpacks:addというコマンドを使うことでも同じことができるみたいです。
お好きな方法でどうぞ。
https://devcenter.heroku.com/articles/using-multiple-buildpacks-for-an-app
JSライブラリのインストールとcoffeeからのコンパイル
gruntやbowerの使い方をググるとだいたいnpm install -g
でインストールしましょうと書かれていることが多い気がしますが、 この方法では手元のPCでgrunt, bowerが使えるようになるのですがheroku側にはインストールされないため、このままherokuにデプロイしても上手くいきません。
解決するにはpackage.jsonをこんな感じに書きます。
{ "name": "MarkdownServer", "version": "0.1.0", "dependencies": { "bower": "^1.4.1", "grunt": "^0.4.5", "grunt-cli": "^0.1.13", "grunt-contrib-coffee": "^0.13.0" }, "devDependencies": { "grunt-contrib-connect": "^0.10.1", "grunt-contrib-watch": "^0.6.1" }, "scripts": { "postinstall": "bower install && grunt build" } }
まずはdependenciesにbowerとgruntを追加してbowerとgruntを使えるようにして、postinstallにbower install && grunt build
を書いておくことでbowerとgruntのインストール後にbowerとgruntを起動させます。
herokuにpushすると自動的にnpm install
を実行してくれるので、
npm install bower install grunt build (Gruntfile.jsにbuildでcoffeeのコンパイルなどが行われるように自分で定義しておく)
の順番にコマンドが実行され、無事に依存jsライブラリののインストールとcoffeeのコンパイルが行われるようになります。
結論
- multibuildbackを使う
- package.jsonにbower, gruntのインストールとpostinstallでの実行を記述しておく。
ローカルのPCで動いている状態からherokuデプロイ用の設定を書くのはなかなか面倒でしたが、herokuへのデプロイは特別なコマンドを使っていないので、
herokuにデプロイできるようにする = どの環境でもコマンド一発叩けばインストールできる
ということになり結果的に非常にポータブルになりました。
いざとなればVPSなどへのお引越しも簡単にできるようになるので、git clone後にコマンド一発でインストールできるように設定しておくことは大事。
ちなみに執筆当時に書いてたWebアプリでここまでの設定を書き終えたのがこちらです。 設定のサンプルにどうぞ https://github.com/Kesin11/MarkdownServer/tree/a389c477f95cabe0624e77d193b56b0237603a1f