Cron for GitHub作った

Cron for GitHub gem作った。

TL;DR

場所はどこでもいいけど、たとえばHerokuから、定期的にブランチを切ることで、webhookを起動させて、GitHubエコシステムを強制起動するコマンド及びgem。

使い方

毎日とか毎週とかHeroku Schedulerから定期実行出来る。Heroku Schedulerは10min/1hour/1day しか選べないが、例えばスクリプト側で、起動されたうちの月曜日の場合は、とか書けばいい。Githubが割とローレベルのapiを提供してくれているので、branch作ったりbranch消したりしてるのにgit clone不要。さくさく快適すぎる。

gemの機能は正確にはcreate branch for github, clear branches for github で、cronにするかどうかは、このgemの外側でどうつかうかなんだけど、名前のインパクト重視で、ひとまず cron for github。

appの使いかた面はこっちに書いた。
GitHubとHerokuの組み合わせでCron for GitHub – Saddler – checkstyle to anywhere

背景

ビルドを定期的にできればいいのに、という要望はtravis-ciに昔からあって、実現していない。

Nightly Builds – CircleCIがそれかと思ったら、これは

簡単な話が「外から環境変数を追加した上で、特定のブランチに対するテストを実行させる」機能

CircleCIを使ってbundle updateを定期実行する – Qiita

で、起動時にはcurlで起こしてやる必要があった。curlはどこから叩くんだっていう。

ciを定期的に叩きたい構想はしばらく前から持っていて、その一つの実現がTachikoma.io。でもTachikoma.ioが提供するのは Dependency Update as a Service 一つだけだった。これgithubのwebhook起点にすればいいのでは?という発想に気づいてからさくさくっと出来た。

自画自賛で神gem。更にCron for GitHubにHerokuデプロイボタンを付けたリポジトリも用意。tagsとか消したら死ぬ系のrefは消せないようにしてるけど、考慮漏れてたら追加は必要。

いやーpacksaddleプロダクト便利だなあ。

Tachikoma.iostraregy: none相当のことが完全にできる上、スケジューラーで実行間隔指定、プルリクエストを送らないなどTachikoma.ioやtachikoma gemで指定しづらかったところが解消できる、完全上位互換ですね。

参照

heroku logs, 特にhubotのlogをslackに流すと捗る

heroku logs, 特にhubotのlogをslackに流すと捗る。

heroku-logs-slack2

TL;DR

heroku logs -> logentries.com -> hubot -> slack の経路で転送する。slackのインテグレーションにpapertrailapp.com あるので、ちゃんとお金を払うならそっちのほうが良さそう(未確認)。

仕組み

heroku logsを流しこむのに定番ぽいのはpapertrail。freeプランの制限がpapertrailキビシイので、無料じゃないと死ぬ人はlogentries。
papertrail plans
logentries pricing

logentriesはapi経由でlogがダウンロードできる。logをhubotで1分おきに取りに行って、roomにpostしてる。

コード

cron = require('cron').CronJob
LogentriesRetrievingLog = require('logentries-retrieving-log')
accountKey = process.env.HUBOT_LOGENTRIES_STOLLEN_KEY
logAddr = process.env.HUBOT_LOGENTRIES_STOLLEN_ADDR

retriever = new LogentriesRetrievingLog({
  accountKey: accountKey,
  logAddr: logAddr
})
room = process.env.HUBOT_LOGENTRIES_STOLLEN_ROOM
# logentries delays 10-30sec, so get from 2minutes ago to 1minute ago
params = {start: - 2 * 60 * 1000, end: - 1 * 60 * 1000}
noLogs = '...No logs.'

module.exports = (robot) ->
  retrieveLogs = ->
    if !accountKey || !logAddr || !room
      robot.logger.error "env missing " + 
        "HUBOT_LOGENTRIES_STOLLEN_KEY: #{process.env.HUBOT_LOGENTRIES_STOLLEN_KEY} " + 
        "HUBOT_LOGENTRIES_STOLLEN_ADDR: #{process.env.HUBOT_LOGENTRIES_STOLLEN_ADDR} " + 
        "HUBOT_LOGENTRIES_STOLLEN_ROOM: #{process.env.HUBOT_LOGENTRIES_STOLLEN_ROOM}"
      return
    retriever.getLogs params, (err, _, body) ->
      if (err)
        robot.logger.error "logentries error: #{err}, body: #{body}"
        return
      if (!body.trim())
        robot.send {room: room}, noLogs
        return
      robot.send {room: room}, body

  new cron '10 * * * * *', () ->
    retrieveLogs()
  , null, true, "Asia/Tokyo"

実際のhubot script

工夫

brain使って、前回取得以降の〜ってカッチリやるならそうするんだろうけど、適当でよかった。それから、heroku logsがlogentries側に反映されるまで10秒~30秒ぐらいタイムラグあるのでどうしようか考えた。
logentriesのパラメーター指定で、負のミリ秒を設定すると、nミリ秒前からのデータが取得できる、とドキュメントにあった。なので、毎分 2分前~1分前のログを取得することにして、解決!

試行錯誤1

logentriesにwebhookあるので、条件を /.*/ にして、都度ログが来るようにした。ログ1行に1リクエスト来る上に、各リクエストに対してcontextとして直前10行が送られてくる。全行送ってるので、context無駄… あと、1条件ではmax 100req/hourなので、足りなかった。

試行錯誤2

webhookを受けてリクエストを処理、するlogが出る。ので自分自身を検知して無限ループした。うける。

まとめ

ライフチェンジング。

yo angular-fullstackのアプリをherokuにdeployしようとして色々引っかかる

yo angular-fullstack:heroku

するとdistにいい感じにbuildして, dist以下が別git repositoryとなり、heroku にアプリ作って、pushしてくれる
おお

おお、と思ったんだけどやっぱイマイチなのでイマイチなところを書く
そのうちもうちょっと理解したらどうにかしよう とりあえずはいいとして

自分の環境以外でこれどうやってdeployするんだろう? とひとまずdist消してみた。
で、yo angular-fullstack:herokuすると、名前がかぶって作れませんと。
うーむ。

なんかProcfileとか独自に作ってたのでどうするかな、と思って
herokuの画面開いてgitリポジトリコピーしてherokuから復旧した。

$ mkdir -p dist
$ cd dist
$ git clone foo/bar.git .

別リポジトリになるイマイチさは、結局productionに出たコードが何なのかよくわから
なくなる。distもgithubに作ってpushするんだろうなあ。二度手間。

github pagesならそういうgrunt/gulpタスクが合ったような気がした、けどnpm search herokuしてもあんまり良さそうなの無いな

これ結局deployこんなんになるのかだいぶツライな

$ grunt build && cd dist/ && git add -A && git commit -m 'Build' && git push heroku master

github pages使ってた時に良さそうだったのは、結局deploy用のshellで、これ。
https://github.com/X1011/git-directory-deploy
subtreeとかよくわからないので。

generator-herokuherokudirectory作ってgrunt のcopyタスクに追加するのがよ
さげ? grunt の書き方コピペして編集以外あんまりわかってないのでまだ出来ない。

figaro/dotenv相当のものってなんだろ figaroインスパイアのfigaroあるけどそんなに
メジャーじゃないっぽい? そのままconfigってmodule使うのが良さそうか
な? とかそのへんが全然わかんなくてツライ

herokuでrails4のassets precompileがこけるwork around

herokuのrails4でasset precompileでabortしたら enable user-env-compile

herokuのrails3.xでassets precompileがこけるのは設定して回避するのが(自前でprecompileしてからpushするよりは)定番だった。

config.assets.initialize_on_precompile = false
https://devcenter.heroku.com/articles/rails-asset-pipeline

rails4.1でも同じくassets precompileで落ちるんだけど、別の原因らしい?
—–> Preparing app for Rails asset pipeline
Running: rake assets:precompile
WARNING: Nokogiri was built against LibXML version 2.8.0, but has dynamically loaded 2.7.6
rake aborted!
could not connect to server: Connection refused
Is the server running on host “127.0.0.1” and accepting
TCP/IP connections on port 5432?
以下バックトレース略

stackoverflowに書いてあるとおりuser-env-compileをenableにするとdeploy出来るようになった。なんで出来るようになったかよくわかってない。説明読んでもよくわからない。precompileせずにdevelopmentモードとおなじになってます! だといやだなー。

$ heroku labs:enable user-env-compile -a <<my app>>

http://stackoverflow.com/questions/16124490/heroku-rails-4-could-not-connect-to-server-connection-refused
http://stackoverflow.com/questions/16007718/heroku-heroku-labsenable-user-env-compile
https://devcenter.heroku.com/articles/labs-user-env-compile

あと、config.assets.initialize_on_precompile = false はもう要らないぽい。

余談だけどnokogiriのwarningなんだろ bundle時とruby compile時の見てるlibxml2が違うんか

whenever勘違いしてたからherokuで動くわけなかった

whenever勘違いしてた。dslで書けるからなにかcron的な行動勝手にやってくれるのかと思ってたらそんな訳はなくて、crontabに追記/書き換えしてくれてるgemだった。そりゃherokuで動くわけねーわーとなって、heroku schedulerにブラウザからポチポチbundle exec rake …とコピペした。俺はリポジトリに入れたいんだよ

heroku-configとheroku-dev-env使ったが手作業でつらい

heroku-configとheroku-dev-envでherokuのENV扱うのつらい。

結論

.envローカルに持ってくる必要ない。
mailはletter_opener使えば
gem ‘letter_opener’, github: ‘ryanb/letter_opener’
MANDRILL_APIKEYなどローカルに要らないので。NewRelicも要らないし。ほかは使わなかったのでどうでもいい。

おわり。

経緯

herokuのENV, heroku-configとheroku-dev-envの組み合わせで行けるジャーンとおもったら、GEM_PATH=vendor/bundle/ruby/1.9.1とかRAILS_ENV=productionとかPATH=bin:vendor/bundle/ruby/1.9.1/bin:/usr/local/bin:/usr/bin:/binとかうっかりしてうっかりしそうになった。

手作業

heroku-config入れる [finished #39943567] by sanemat · Pull Request #17 · sanemat/gist-mail
“config:pull した.envは下記を残して手で消してください!”

こんな手作業やりたくないしうっかりconfig:pushした日にはデータベースにもつながらないしrubygemsも動かないしどうしようもなくなってしまう。

option –filename

プルリク 見てたら https://github.com/joelvh/heroku-config こっち使えばなんかそれっぽいこと出来るような気がした。試してない。

Release “I have been pocketing his money.”

お金を借りたのに返すのを忘れたとき”着服してすいません”とtweetする”I have been pocketing his money.”を作りました。

UseCase
手持ちに小銭がなくてお金を借りたとき、あとで一万円札コンビニで崩して返そうと思ってそのまま忘れることがよくあります。そんなとき”木曜日ばけおに借りた弁当将軍代500円を着服してすいません @sanemat” と自分のアカウントでtweetしてくれるアプリです。”I have been pocketing his money.“。事前に設定した時間内にお金を返したら、コマンドを入力する $ bin/bakeo-snooze ことでtweetを止めることができます。

ありがとうございます!

Create App with MiddleMan, provided from heroku

I can make html website with MiddleMan easily. MiddleMan is rack application, so application can be provided from heroku.

But this is development way. Raw MiddleMan rack application does not fit with production environment. That is not MiddleMan’s purpose.

Once I make a web site, I want to put builded web site to heroku.

Blogging with jekyll, rack and heroku for free! | gmarik.info

I copy above one!

https://github.com/sanemat/web-soundnano/blob/master/web.ru

I compare Rack::TryStatic with middleman. On my MacbookAir. Run with chrome, firefox, eclipse, android simulator, skype, etc.

summary:

MiddleMan : Requests per second: 52.87 [#/sec] (mean)

Rack::TryStatic : Requests per second: 929.27 [#/sec] (mean)

https://gist.github.com/1074416

I say agein, middleman’s rack adapter is development way. It’s not middleman’s purpose.

For deploying heroku only static files, this article is worth.