githubやesaのmentionをslackでmentionするよ

f:id:naoto5959:20160319010013p:plain

githubやesaでmentionがあったら、slackbotにこんな感じのmentionを飛ばさせたいんですよ。

f:id:naoto5959:20160319004631p:plain

やむを得ない理由だったり、ナンカついウッカリだったりで

  • githubとslackのidが違う
  • esaとslackのidが違う

なんてことが、あったりなかったりするわけですが、そんな時でも

-
  github: ppworks
  esa: koshikawa_naoto
  slack: koshikawa.naoto

みたいなidのmappingがあれば

  • githubのmentionをslackで通知
  • esaのmentionをslackで通知

することが出来るんじゃないかなーと思って作ってみました。heroku buttonでササッと構築したかったのでなるべくDBを使わずに設定ができるようにしています。

準備

github.com

へアクセスして、heroku buttonをクリックします。herokuではないどこかにホスティングしたい場合は、怪しげな英語のナニカで書かれたREADMEにそれっぽいことが書いてあります。

heroku buttonで入力を促される以下の環境変数にいろいろ設定していきます。

  • MENTIONS_MAPPINGS_FIlE_PATH
  • SLACK_WEBHOOK_URL
  • GITHUB_TO_SLACK_TOKEN
  • ESA_TO_SLACK_TOKEN

MENTIONS_MAPPINGS_FIlE_PATH

以下の様な感じの、githubとesaとslackのmappingをしたyamlファイルを置いたgist(ちゃんとrawにしてね)などのURLを記載します。

-
  github: ppworks
  esa: koshikawa_naoto
  slack: koshikawa.naoto

たとえば、こんな。

https://gist.githubusercontent.com/ppworks/49f6ce44efb09d5fc8e9/raw

SLACK_WEBHOOK_URL

通知したいslackのIncoming WebHooksを作って、埋めます。

たとえば、こんな。

https://hooks.slack.com/services/xxxxxx/yyyyyy/zzzzzz

GITHUB_TO_SLACK_TOKEN

githubのmentionをslackに通知したい場合埋めて下さい。

ruby -r 'securerandom' -e 'puts SecureRandom.hex'

の結果などを入れるとよいです。

githubに設定すべきwebhookは以下のようになります。

https://your-heroku-application-name.herokuapp.com/webhooks/**ここが今作ったtoken**

githubでは以下の様なEventにhookするようにwebhookを設定して下さい。

githubのwebhook設定

  • githubのissue, pull requestでmentionされたとき
  • githubのissue, pull requestでassignされたとき

mappingが存在すれば、slackにslackbotの個別チャットでmentionが飛びます。

f:id:naoto5959:20160319004539p:plain

ESA_TO_SLACK_TOKEN

esaのmentionをslackに通知したい場合埋めて下さい。

ruby -r 'securerandom' -e 'puts SecureRandom.hex'

の結果などを入れるとよいです。

githubに設定すべきwebhookは以下のようになります。

https://your-heroku-application-name.herokuapp.com/webhooks/**ここが今作ったtoken**

esaでは以下の様なEventにhookするようにwebhookを設定して下さい。

esaのwebhook設定

  • esaのpost, commentでmentionされたとき

mappingが存在すれば、slackにslackbotの個別チャットでmentionが飛びます。

f:id:naoto5959:20160319004631p:plain

@allのときは、#general@everyone宛のmentionが飛びます。

感想

ウッカリ勢いで意味もなくRails5で作ってしまったけども、今のところそれなりに便利に運用出来ているのでよしとする。

割と便利です。

要望とかはPR頂ければ喜びますので、何卒(\( ⁰⊖⁰)/)

Circle CIでbundle updateのPull Request作成を自動化する手順

日々、bundle updateしてますか!

circleci-bundle-updatecircleci-bundle-update-prを使って、bundle updateの Pull Requestを自動化する手順をまとめてみます。作者の記事を読めばいいっちゃいいんですが、地味に抜けていた手順と各ステップは何のために必要かを整理してみました。

このgemは、Pull Requestに各gemの差分リンクを貼ってくれるのが便利だな!って思って採用してます。素敵なgemをありがとうございます。

github.com github.com

ステップは

  1. CircleCI経由でbotからgithubにpush出来るようにしておく
  2. CircleCI経由でbotからgithubにPR作れるようにしておく
  3. 外部からCircleCIへbuild指示できるようにする
  4. 外部からcronでCircleCIへbuild実行する
  5. CicleCIのbuild時にPRを送るようにする

といった感じです。

CircleCI経由でbotからgithubにpush出来るようにしておく

CircleCIからgithubgit pushするのが目的です。

  1. githubでPRを送りたいユーザーでログインする
  2. CircleCIにてPRを送りたいprojectを登録する or Followする
    f:id:naoto5959:20151223211412p:plain
  3. CircleCI経由でbotからPR出来るように鍵を登録する。Project SettingsからCheckout SSH keysからAdd user keyする
    f:id:naoto5959:20151223211426p:plain
  4. もし、まだAuthorizeしていなかった場合は、次に「Create and add nekodayo user key」をクリックして鍵を追加
    f:id:naoto5959:20151223211439p:plain
  5. こんな感じでkeyが追加されていればok
    f:id:naoto5959:20151223211447p:plain

CircleCI経由でbotからgithubにPR作れるようにしておく

CircleCIからgithubのAPIを叩けるようにするのが目的です。

  1. githubでPRを送りたいユーザーでログインする
  2. access_tokenをrepo grantで作る
    f:id:naoto5959:20151223211458p:plain
  3. 生成されたAccess Tokenをコピーしておく
    f:id:naoto5959:20151223211452p:plain
  4. CircleCIの該当Project SettingsからEnvironment variablesにてGITHUB_ACCESS_TOKENという名前で環境変数を設定する
    f:id:naoto5959:20151223211506p:plain

外部からCircleCIへbuild指示できるようにする

つまり、CircleCIのAPIを叩けるようにするのが目的です。

  1. CircleCIの該当Project SettingsからAPI Permissionsで、Allを選択し、bundle update cron用とか適当にラベルを付ける
    f:id:naoto5959:20151223211513p:plain
  2. 出来上がったTOKENをcron botから使う

外部からcronでCircleCIへbuild実行する

  1. ci-bundle-updateのheroku buttonでデプロイ
  2. 指示通りに環境変数を設定
    • GITHUB_USERNAME=botのユーザー名
    • GITHUB_REPONAME=projectの名前
    • CIRCLECI_TOKEN=先ほどのTOKEN
  3. heroku schedulerにbundle exec ruby ci-bundle-update.rbを設定

CicleCIのbuild時にPRを送るようにする

github.com

を参考に、pplogではこんな感じに設定しています。

deployment:
  production:
    branch: master
    commands:
      - |
        if [ "${BUNDLE_UPDATE}" ] ; then
          gem update bundler --no-document
          gem install circleci-bundle-update-pr
          circleci-bundle-update-pr
        fi
test:
  override:
    - |
      if [ -z "${BUNDLE_UPDATE}" ] ; then
        bundle exec rspec --fail-fast
      fi

こんな感じのPull Requestが届くようになります。

f:id:naoto5959:20151223220030p:plain

余談

設定している際に、botのACCESS_TOKENを持っている状態でワザワザgithu user_nameとemailを指定している箇所が無駄だなと思ったので、Pull Request送ったりしてました。

github.com

まとめ

Railsを選択するには、「それでもRailsを選択する3つの理由 - pblog」といった理由があるはずで、そのためには

  • テストを書く
  • 周辺gemの更新に追随し続ける

必要があります。そのためにも自動でbundle updateをして日常的に最新のgemを使うようにしたいですね。

pplogの「ちょっと足跡が見えちゃう」機能を偲んで( ˘ω˘)

f:id:naoto5959:20151216001632p:plain

そこには足跡があった。

そう、名前の下に「最後に読んだよしたあしあと」が出ていたのだ。正確には、「最後のあしあとの最後の20文字」が出ていた。ウッカリおしりが見えちゃっている感じだ。

何故作ったか

ツイッターの名前欄にはロマンがあるんですよ。

名前欄とは、Koshikawa Naoto とか書いちゃうところである。しかし、なんでそれ名前欄に書いたのって言う文字列を名前欄に入れている人々がいるのだ。なんていうかMNS MessengerとかSkypeにあったアレ、ムードみたいなアレ。間違ってアレ目的で名前欄を書き換えている。それも頻繁に書き換えている。そんなNAMAE-RAN FREAKSがいるんですよ。

正直ツイッターにおける一番いい機能である名前欄で遊ぶという機能を、我々はそんな機能を、pplogに搭載したくなったのですよ。

(当時の会話です)

f:id:naoto5959:20151216002142p:plain

そうですね、一時期「コントレックス」という文字列を入力していたことがありますね。今はもう飲んでいないコントレックスよ。

どう作るか

名前欄を変更できる機能を作ろう。

違う、そんなんじゃない。pplogに欲しい機能はそんなんじゃない!設定なんてさせるなよ!何が名前の設定だ!アホか!ポエム書きにきてんだよ、ポエム読みに来てんだよ!ふざけんなよ!

そんな気持ちから産み出した機能。

「最後に読んだよしたあしあと」を表示する機能。

気づかない人には、うっかりおしりが見えてしまっているような。しかし気づいた人はふざけて遊べる、そんな機能。くすっと笑えるお遊び機能。コレだ!って思ったんですよ(当時)

どやPull Requestの様子です

どうですか。私のツイッターIDの下には「ほしぶどう」という称号のような何かが。何なんですか、これは!バカバカしい!

f:id:naoto5959:20151216001820p:plain 

成功かと思った

無駄に回遊率が上がった(感覚値です)表示したい文字列を探し集めるためにポエムをあさった。ホネを回し続けた。ホネをフォワードし続けたのだ。そして、皆の名前欄の下を見たくてたまらなくなった。無駄に人のページヘ行った。

f:id:naoto5959:20151216003023p:plain

ヨサソウ、ッテ思った。

いやまて

足跡を必要以上に選び始める自分が居た。今までと違う気持で「読んだよ」していた。違った。それは違った。なんなら最高の「読んだよしたあしあと」のあとは、「読んだよ」したくなくなったりしてた。

思ったのと違う行動をし始めていた。ポエムを読めよ。おもいっきり、心置きなく「読んだよ」しろよ。なにやってんだよ!!!

おれの作りたい世界はコレじゃない。

やめよう

f:id:naoto5959:20151216001632p:plain

ひっそりと始まった機能はひっそりと消えた。それは儚い。ポエムのように、その機能は消えた。pplogの機能はそのコンセプトのように、儚く去っていった。

記憶にだけ残る君よ。

「ちょっと足跡が見えちゃう」機能よ。安らかに眠れ。

nyauthという認証gemを作ってた

MoneyForward Advent Calendar 2015 - Qiitaの14日目」の記事です。

今年、一体何やっていたかなと振り返ってみたところ、nyauthというgemを作っていたことに気づいたので、雑に紹介します。

「お前エモい記事しか書かなくなったな」みたいな声もチラホラ聞くのでたまにはプログラミングの記事を。

f:id:naoto5959:20151213124149p:plain

こうして振り返ってみると、何故かコントリビューターも現れ始めて嬉しかったです。

何なの

一般ユーザー向けと管理者用向けといった複数のコンテクストに対応した認証gemです。

猫の「にゃん」(って何)と「Authentication」を合わせて「にゃおーす」つまりnyauthです。

なんで作ったの

個人プロジェクトでとあるWebアプリケーションを作る際に、せっかくだし deviseから卒業しようってのと、単純になるべくメタプロし過ぎないシンプルなものを作りたかったのです。

ただそれだけです。学びが多かった。それが収穫です。

どんなもの

ざっと

  • 登録
  • 認証
  • パスワード変更
  • 本人確認(メールが届くかどうか)
  • パスワード変更

といった機能があります。一般ユーザーにはこれら全てを提供して、管理者ユーザーには、「認証だけ」みたいなユースケースに対応してます。

一般ユーザーの認証URLは

/session/new

管理者ユーザーの認証URLは

/admin/session/new

と言った感じで、現在のコンテクストをURLのパスで判断してます。

ルーティング

config/routes.rb

Rails.application.routes.draw do
  mount Nyauth::Engine => "/"
end

/にマウントすると以下の様なroutesが定義されます。/にマウントした時にはデフォルトでUserモデルが認証の対象となります。

Prefix Verb URI Pattern Controller#Action
nyauth      /nyauth     Nyauth::Engine

Routes for Nyauth::Engine:
              registration POST   /registration(.:format)                             nyauth/registrations#create
          new_registration GET    /registration/new(.:format)                         nyauth/registrations#new
                   session POST   /session(.:format)                                  nyauth/sessions#create
               new_session GET    /session/new(.:format)                              nyauth/sessions#new
                           DELETE /session(.:format)                                  nyauth/sessions#destroy
             edit_password GET    /password/edit(.:format)                            nyauth/passwords#edit
                  password PATCH  /password(.:format)                                 nyauth/passwords#update
                           PUT    /password(.:format)                                 nyauth/passwords#update
     confirmation_requests POST   /confirmation_requests(.:format)                    nyauth/confirmation_requests#create
  new_confirmation_request GET    /confirmation_requests/new(.:format)                nyauth/confirmation_requests#new
              confirmation GET    /confirmations/:confirmation_key(.:format)          nyauth/confirmations#update
   reset_password_requests POST   /reset_password_requests(.:format)                  nyauth/reset_password_requests#create
new_reset_password_request GET    /reset_password_requests/new(.:format)              nyauth/reset_password_requests#new
       edit_reset_password GET    /reset_passwords/:reset_password_key/edit(.:format) nyauth/reset_passwords#edit
            reset_password PATCH  /reset_passwords/:reset_password_key(.:format)      nyauth/reset_passwords#update
                           PUT    /reset_passwords/:reset_password_key(.:format)      nyauth/reset_passwords#update

config/routes.rb

Rails.application.routes.draw do
  mount Nyauth::Engine => "/hoge"
end

/hogeにマウントすれば、Hogeモデルが認証の対象となります。

複数のモデルを認証したいときのルーティング

config/routes.rb

Rails.application.routes.draw do
  # for admin
  namespace :nyauth, path: :admin, as: :admin do
    # concerns :nyauth_registrable
    concerns :nyauth_authenticatable
    # concerns :nyauth_confirmable
  end

  # for user
  mount Nyauth::Engine => "/"
end

本当は、2箇所に違う名前でマウントしたいのだけども

Rails.application.routes.draw do
  # for admin
  mount Nyauth::Engine => "/admin", as: 'admin'

  # for user
  mount Nyauth::Engine => "/", as: 'user'
end

とは出来ません。Railsが2箇所にEngineをマウントはできてもURLヘルパーを上手く扱えず後で定義した方のasで上書いてしまうので、今回はmountは1つ、2つ目以降は

namespace :nyauth, path: :admin, as: :admin do
    # concerns :nyauth_registrable
    concerns :nyauth_authenticatable
    # concerns :nyauth_confirmable
end

と言った感じで定義します。もちろん、1つ目からこの形式でも大丈夫です。

ここで、nyauthでは以下のRouting Conrcenを使えます。

concern :nyauth_registrable do
  resource :registration, only: %i(new create)
end

concern :nyauth_authenticatable do
  resource :session, only: %i(new create destroy)
  resource :password, only: %i(edit update)
  resources :reset_password_requests, only: %i(new create)
  resources :reset_passwords, param: :reset_password_key, only: %i(edit update)
end

concern :nyauth_confirmable do
  resources :confirmation_requests, only: %i(new create)
  get '/confirmations/:confirmation_key' => 'confirmations#update', as: :confirmation
end

コントローラー

application_controller.rb

class ApplicationController < ActionController::Base
  include Nyauth::ControllerConcern
  before_action -> { require_authentication! as: :user }
  helper_method :current_user

  private

  def current_user
    current_authenticated(as: :user)
  end
end

使いたいコントローラーに、include Nyauth::ControllerConcernと書いておくと、require_authentication!というメソッドが生えるので、

before_action -> { require_authentication! as: :user }

てな感じで、フィルターをかけるイメージです。

管理者向けのコントローラーにも同じように、

admin/base_controller.rb

class Admin::BaseController < ActionController::Base
  include Nyauth::ControllerConcern
  before_action -> { require_authentication! as: :admin }
  helper_method :current_admin

  private

  def current_admin
    current_authenticated(as: :admin)
  end
end

テーブル定義

class CreateUsers < ActiveRecord::Migration
  def change
    create_table :users do |t|
      t.string :nickname
      # Authenticatable
      t.string :email, null: false
      t.string :password_digest, null: false
      t.string :password_salt, null: false
      t.string :reset_password_key
      t.datetime :reset_password_key_expired_at
      # Confirmable
      t.datetime :confirmed_at
      t.string :confirmation_key
      t.datetime :confirmation_key_expired_at

      t.timestamps null: false
    end
    add_index :users, :email, unique: true
  end
end

モデル

適切なカラムを定義した上で、モデルにmoduleincludeしておきます。

app/models/user.rb

class User < ActiveRecord::Base
  include Nyauth::Authenticatable
  include Nyauth::Confirmable
end

app/models/admin.rb

class Admin < ActiveRecord::Base
  include Nyauth::Authenticatable
end

なんとなく見れば分かるかな。更に詳しくは、READMEやソースコードを読んで頂けると良いかと嬉しいです。

学び

  • 同じEngineを複数のパスにマウントする事を想定していないようでつらかった。もうちょいソースを読み込んでコントリビュートしたい気持ちはある。
  • そのため、helperの生成がつらかった。実装的にもシンプルじゃないのでいけてない。
  • テスト時に、sign_in(user)みたいなヘルパーを使うために、wardenを大いに参考にさせていただいた。Rack層の段階で次のrequestにhookするという発想がとても勉強になった。
  • generator生成のノウハウを得た。
  • respondersの拡張ノウハウを得た。
  • gemのconfigを用意してゴニョゴニョカスタマイズするノウハウを得た。

個人プロジェクトの進捗どうですか

なんと肝心の個人プロジェクトのWebアプリは、このgemを作ることに寄り道したり、React.jsをこねくり回したり、RailsとJavaScriptのモダンな共存などを模索していたせいで公開がまで来てません。個人プロジェクトは、yak shavingばかりしていた1年でしたね。

github.com

今回の記事でnyauthにご興味持たれた方は是非Pull Requestでフィードバック頂けると嬉しいです。

iCloudをサインアウトする前にApple IDを変えてしまうとつらい

家族の携帯キャリアをIIJmioに統一し、iPhone4からiPhone6sへ機種変した。

LINEの過去トークを含めてすべてのデータをとっておきたいとのことで、

  • iPhone4をiTunes経由でMacにバックアップ(LINEのトーク移行には暗号化をONにするのがポイントらしい)
  • 買ってきたばかりのまったく新しいiPhone6sをiPhone4のバックアップから復元

という手順で移行する計画で進めた。

このとき、移行後のiPhone6sでiCloudがサインアウト出来ずにつらかった話。

Apple IDを変えた

今回、Apple IDがキャリアメールアドレスだったのでApple ID自体をgmailのアドレスに変える必要があった(hoge@carrier.example.netから、hoge@examle.comへ変更するものとするイメージ)。

Apple IDを変更したのでiPhone側のiTunesの設定とiCloudをサインアウトし、サインインしなおすことになる。このときiCloudで「iPhoneを探す」設定をしていると面倒くさい。

Apple ID変更前にiCloudをサインアウトしていれば、何の問題もないが、先にiCloudのサインアウトするのを忘れているとつらい。

iCloudからサインアウト出来ない

Apple IDを変えた後でも、iTunesは難なくサインアウト出来る。んが、iCloudをサインアウトしようとすると

「iPhoneを探す」をオフにするには、現在設定しているhoge@carrier.example.netのパスワードを入力してね

的なことを言われる。しかし、すでにAppleIDをhoge@carrier.example.netからhoge@example.comへ変更しているので自分のAppleIDを入力しても「そのIDはロックされています」と言われてどうにもならない。初期化しようとしても「iPhoneを探す」をオフにする過程で同じ状況。セキュリティ意識が高い!

解決策

(2015年11月29日現在はこの手順が有効だった)

解決策は、AppleのウェブサイトからAppleIDを一時的に前のアドレスに戻す こと。戻した際に確認のメールアドレスを送信云々表示されるが無視する。ていうか、無視したくなくてももうそのキャリアアドレスは受け取れないので無視せざるをえないので若干不安な気持ちになるが強い心で先へ進む。

この状態で、iCloudをサインアウトしようとすると先ほどと同じように

「iPhoneを探す」をオフにするには、現在設定しているhoge@carrier.example.netのパスワードを入力してね

と言われるので、パスワードを入力すると「iPhoneを探す」をオフに出来て、iCloudからもサインアウト出来る。

そして、ウェブサイトからAppleIDをまたhoge@example.comへ変更する。今度はこのメール宛の確認メールは踏んでおく。

こうして、ようやくiTunesとiCloudからサインアウト出来たので、一旦iPhone6sを初期化する。

サインアウトさえ出来れば計画通りの作業が可能となるので、iPhone4でも同じ手順で両方サインアウトして、バックアップを再取得し、iPhone6sを初期化して初めからやりなおして、うまくいった。

途中で諦めそうになった

これはApple IDのバグ?いや、仕様だと思い、もうiPhoneを交換するしか無いかとまで思ったが、ココで諦めてはいかん!という謎のモチベーションにより問題を解決できたの良かった。

なんせ、

SIMフリー版iPhoneをAppleで交換したらSIMロックが掛かっちゃって…もう大変さ。 | Tools 4 Hack

こういう話を知っていたので、SIMフリーiPhoneの交換はコミュニケーションミスが怖くてやりたくなかったのだ。とにかく解決してよかった。

Apple IDを変える際には事前に全デバイスのiCloudをサインアウトするんだ!