dackdive's blog

新米webエンジニアによる技術ブログ。JavaScript(React), Salesforce, Python など

HubotをES2015で書いてHerokuにデプロイする

今さらながら Slack bot を作りたくて、フレームワークHubot を選んだ。
Hubot はそのままでは CoffeeScript で書く必要があるんだけど、ES2015 もやっと覚えたばかりなのに CoffeeScript の勉強はしたくない。
ということで、ES2015 で書くための手順をメモ。

デプロイ先は Heroku です。

Hubot のインストール

まずは 公式ドキュメント の通りに Hubot スクリプトのひな形を作成する。

# Node.js はインストール済みとする
$ npm install -g yo generator-hubot
$ mkdir hubot-es2015
$ cd hubot-es2015
$ yo hubot
                     _____________________________
                    /                             \
   //\              |      Extracting input for    |
  ////\    _____    |   self-replication process   |
 //////\  /_____\   \                             /
 ======= |[^_/\_]|   /----------------------------
  |   | _|___@@__|__
  +===+/  ///     \_\
   | |_\ /// HUBOT/\\
   |___/\//      /  \\
         \      /   +---+
          \____/    |   |
           | //|    +===+
            \//      |xx|

? Owner zaki-yama <shingoyamazaki00@gmail.com>
? Bot name hubot-es2015
? Description A simple helpful robot for your Company
? Bot adapter slack
   create bin/hubot
   create bin/hubot.cmd
   create Procfile
   create README.md
   create external-scripts.json
   create hubot-scripts.json
   create .gitignore
   create package.json
   create scripts/example.coffee
   create .editorconfig
                     _____________________________
 _____              /                             \
 \    \             |   Self-replication process   |
 |    |    _____    |          complete...         |
 |__\\|   /_____\   \     Good luck with that.    /
   |//+  |[^_/\_]|   /----------------------------
  |   | _|___@@__|__
  +===+/  ///     \_\
   | |_\ /// HUBOT/\\
   |___/\//      /  \\
         \      /   +---+
          \____/    |   |
           | //|    +===+
            \//      |xx|

なんか色々ファイルが作成されました。

$ ls -al
total 64
drwxr-xr-x   13 yamazaki  staff   442  7 12 18:23 ./
drwxr-xr-x    5 yamazaki  staff   170  7 12 18:22 ../
-rw-r--r--    1 yamazaki  staff   197  5 20 06:45 .editorconfig
drwxr-xr-x   12 yamazaki  staff   408  7 12 18:37 .git/
-rw-r--r--    1 yamazaki  staff    39  5 20 06:45 .gitignore
-rw-r--r--    1 yamazaki  staff    24  7 12 18:23 Procfile
-rw-r--r--    1 yamazaki  staff  7904  7 12 18:23 README.md
drwxr-xr-x    4 yamazaki  staff   136  7 12 18:23 bin/
-rw-r--r--    1 yamazaki  staff   213  7 12 18:23 external-scripts.json
-rw-r--r--    1 yamazaki  staff     2  7 12 18:23 hubot-scripts.json
drwxr-xr-x  113 yamazaki  staff  3842  7 12 18:23 node_modules/
-rw-r--r--    1 yamazaki  staff   662  7 12 18:23 package.json
drwxr-xr-x    3 yamazaki  staff   102  7 12 18:23 scripts/


Babel のインストールと設定

ES2015 で書き、ES5 にトランスパイルするため Babel をインストールする。

$ npm install --save babel-cli babel-preset-es2015

通常、Babel などのアプリケーション本体で使われないパッケージは --save-dev を使ってインストールするが、
Heroku でデプロイしたときに devDependencies は自動的にインストールされない ため、dependencies に保存されるようにしてます。

つづいて、.babelrc を作り、以下を記述。

{
  presets: ["es2015"]
}

さらに package.json に npm script を定義する。

// 抜粋
"scripts": {
  "build": "babel src --out-dir build",
}

src, build のフォルダ名は自由です。


スクリプトの作成とビルド

準備ができたので、スクリプトを書いていく。
src/index.js (ファイル名は任意)に以下のように記述する。

module.exports = (robot) => {
  robot.hear(/すし/, (res) => {
    res.send(':sushi:');
  });
}

「すし」と入力したら Slack 上で寿司アイコンを返すだけの単純なスクリプト

保存したら、先ほどの npm script でビルドする。

$ npm run build
> hubot-es2015@0.0.0 build /Users/yamazaki/workspace/hubot/hubot-es2015
> babel src --out-dir build

src/index.js -> build/index.js

build ディレクトリ以下にファイルが生成されました。


Bot 起動用のコマンドを編集する

デフォルトでは、bot の起動は

$ ./bin/hubot

だけで済むが、今回は --require オプションでスクリプトが格納されているディレクトリを指定する。

$ ./bin/hubot --require build

デプロイ先に Heroku を使う場合は Procfile も編集しておく。

web: bin/hubot -a slack --require build

-a slack は Slack と連携するために必要)

bot の動きを Slack から試してみる場合は、事前に Slack の Hubot 設定ページから HUBOT_SLACK_TOKEN をコピーし

f:id:dackdive:20160713144815p:plain

.env に記述した後、

$ heroku local web

コマンドを実行する。

f:id:dackdive:20160713145028p:plain

ちゃんと動いてるようです。


Heroku にデプロイする

最終的に Heroku にデプロイするときは、以下の作業が必要です。

postinstall という npm script を追加する

参考:https://devcenter.heroku.com/articles/nodejs-support#customizing-the-build-process

postinstall というスクリプトは、Heroku にデプロイ後に自動的に走るスクリプト
デプロイ後に Babel によるビルドが実行されるようにする。

"scripts": {
  "build": "babel src --out-dir build",
  "postinstall": "babel src --out-dir build"
}


本番環境の環境変数にも HUBOT_SLACK_TOKEN を設定する

f:id:dackdive:20160713150224p:plain

画面を開くのが面倒な人は

$ heroku config:set HUBOT_SLACK_TOKEN=...

というコマンドを実行するか、.env ファイルから設定する以下の方法が便利です。
Herokuで本番環境の環境変数(config vars)を.envファイルで設定する - dackdive's blog


環境変数 HUBOT_HEROKU_KEEPALIVE_URL (とタイムゾーン)を設定する

external-scripts.json を見ると、hubot-heroku-keepalive が指定されている。
これは、Free dyno で bot がスリープしないようにするための Hubot スクリプトらしい。

そして、このスクリプトが正しく動作するためには HUBOT_HEROKU_KEEPALIVE_URL という環境変数が設定されている必要があるそうなので、設定する。

# Heroku アプリケーションの URL を指定すれば良い
$ heroku config:set HUBOT_HEROKU_KEEPALIVE_URL=https://[your-heroku-app].herokuapp.com/

# 以下のように、ワンライナーで指定することもできる
$ heroku config:set HUBOT_HEROKU_KEEPALIVE_URL=$(heroku apps:info -s  | grep web-url | cut -d= -f2)

その他の変数については以下の通り。詳しくは README の Configuration 参照。

  • HUBOT_HEROKU_WAKEUP_TIME : Bot を起動する時間。デフォルト 6:00
  • HUBOT_HEROKU_SLEEP_TIME : Bot をスリープする時間。デフォルト 22:00
  • HUBOT_HEROKU_KEEPALIVE_INTERVAL : Bot を定期的に起こす間隔。デフォルト 5 分間隔

おおむねデフォルト通りで良さそうに見えるが、WAKEUP_TIME および SLEEP_TIME は Heroku アプリのタイムゾーン(デフォルト UTC)に従うので、以下のようにタイムゾーンJST に設定しておいた方がよさそう。

$ heroku config:set TZ=Asia/Tokyo


(2016/08/23 追記) Heroku Scheduler というアドオンを追加する

上述した hubot-heroku-keepalive というスクリプトだけでは不十分だった。
スクリプト自体は dyno が スリープしないように 定期的にアクセスするためのものなので、
HUBOT_HEROKU_SLEEP_TIME から HUBOT_HEROKU_WAKEUP_TIME の間で dyno がスリープしてしまうとそれ以降スリープしっぱなしになってしまう。

そのため、HUBOT_HEROKU_WAKEUP_TIME に合わせて dyno をスリープから復旧させる必要がある。
これには hubot-heroku-keepalive の README に書いてあるとおり、Heroku Scheduler というアドオンを使う。

アドオンは管理画面からインストールするか、ローカルで以下のコマンドを実行する。

$ heroku addons:create scheduler:standard

インストール後に heroku addons:open scheduler を開くと設定画面が開くので、新規でジョブを追加して以下のように設定する。

f:id:dackdive:20160823084253p:plain

中央のスクリプトには

$ curl ${HUBOT_HEROKU_KEEPALIVE_URL}heroku/keepalive

を入力する。

また、NEXT DUE は UTC で入れる必要があるので要注意。(キャプチャだと日本時間の 9:00 に起こす)


その他

1) 起動時のログに

INFO /Users/yamazaki/workspace/hubot-es2015/build/index.js is using deprecated documentation syntax

と出力されている。
調べてみると、Description: などの冒頭のコメントがないとこのログが出力されるそう。
参考:Hubotでusing deprecated documentation syntax - Qiita

というわけで、試しにビルド前の JS にコメントを追記してみたけど
ビルドすると冒頭に 'use strict' が追加されてしまい、1 行目からコメントがないとこのログは消せないよう。

ビルド前
// Description:
//   My first slack bot
//
// Commands:
//   hubot すし - return sushi icon
module.exports = (robot) => {
  ...
ビルド後
'use strict';

// Description:
//   My first slack bot
//
// Commands:
//   hubot すし - return sushi icon
module.exports = function (robot) {
  ...

今のところ特に困らないので、とりあえず諦める。


2) (解決) 同じく起動時のログに

ERROR hubot-heroku-alive included, but missing HUBOT_HEROKU_KEEPALIVE_URL. `heroku config:set HUBOT_HEROKU_KEEPALIVE_URL=$(heroku apps:info -s  | grep web-url | cut -d= -f2)`

と出ている
確認したところ external-scripts.jsonhubot-heroku-keepalive というライブラリが指定されていて、README を読む限り HUBOT_HEROKU_KEEPALIVE_URL という環境変数を指定する必要があるみたい。


3) デフォルトで作成される scripts ディレクトリは不要になったので削除した。一緒に、hubot-scripts.json も deprecated のようなので削除。
(起動時に以下のログが出ていた)

WARNING Loading scripts from hubot-scripts.json is deprecated and will be removed in 3.0 (https://github.com/github/hubot-scripts/issues/1113) in favor of packages for each script.


GitHub


リファレンス