Kesinの知見置き場

知見を共有していきたいじゃないですか

herokuにデプロイするときでもgruntしたい!(+rubyサーバー)

JSの勉強のためにタイトルのような構成のwebアプリをherokuにデプロイしようとしたところ、主にjsのライブラリ管理やcoffeeのコンパイルなどで苦労したのでメモ。

rubyだとjs周りも面倒見てくれるRails関係のgemが色々あるみたいですが、jsは独立して管理した方がいいという風潮を最近感じているのでgemには一切依存しない形にしてみました。
(試していませんがサーバー側をPythonPerlに置き換えても問題ないはず)

構成

サーバー: rubysinatra
クライアント: 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

参考