くりにっき

フルスタックキュアエンジニアです

ChatWork API v2への追従について

下記でChatWork APIのバージョンアップがアナウンスされています

help.chatwork.com

既にv2はリリースされていてv1は5月上旬に停止予定とのことなので早めにアップデートしておくのがいいでしょう

とのことで、自分の視界に入っている限りでのv2対応状況について紹介します

既存のプログラムをv2対応する

既存プログラムの改修レベルでいえば

  • エンドポイントをv1からv2にする
  • 静的型付け言語であれば message_id の型をintegerからstringにする

だけで動きます。完全なv2 API対応にはコンタクト承認依頼APIにも対応する必要がありますが、「v1 API廃止後も既存のプログラムが問題なく動く」ということだけにターゲットを絞ればこの2つだけで十分かと。(以降「v2対応」はこの最低限の対応を指します)

他の人が作ったライブラリのv2対応状況

chatwork

github.com

Rubyで一番使われてそうなChatWorkクライアントのgem

https://github.com/asonas/chatwork-ruby/pull/14 でv2対応されているので、v0.4.0以降を使えばv2対応完了です

capistrano-chatwork, cha

github.com

github.com

capistrano-chatworkの中で使ってるクライアントgemがchaです。

chaとcapistrano-chatworkの両方でPR投げて取り込まれています。

cha v1.2.0以降とcapistrano-chatwork v1.4.0以降を使えばv2対応完了です。*1

go-chatwork

github.com

golang用のクライアント。本家 ではなくfork版の方です

https://github.com/griffin-stewie/go-chatwork/pull/8 でPRを投げて取り込まれているので go get -u github.com/griffin-stewie/go-chatwork すればv2対応完了です。

拙作のChatWork系ツールのv2対応状況

Jenkins ChatWork Plugin

github.com

v1.0.8 でv2対応完了しています。余談ですがこの時送られてきたPRで初めてChatWork API v2のことを知りましたw

https://github.com/jenkinsci/chatwork-plugin/pull/34

zatsu_monitor

github.com

v0.2.2でv2対応しています

capistrano-around_chatwork

github.com

v0.1.1以降にアップデートすれば自動的にchatwork gemのv0.4.0以降(v2対応版)が入るようになってるのでそれでv2対応完了です

ただし capistrano-around_chatwork v0.1.1 と capistrano-chatwork を同時にインストールしようとするとバージョンがコンフリクトして bundle install できないので capistrano-around_chatwork v0.2.0推奨

詳細: https://github.com/sue445/capistrano-around_chatwork/pull/2

fluent-plugin-chatwork

github.com

v1.0.1にアップデートすれば自動的にchatwork gemのv0.4.0以降(v2対応版)が入るようになってるのでそれでv2対応完了です

*1:capistrano-chatworkのPRを見てもらえれば分かりますが、chaだけだと依存の関係でbundle installできないのです

Travis CIでdepを使う

zatsu_monitordep対応をしたのでその時のメモ

depについて

depについては下記を参照

mattn.kaoriya.net

.travis.yml

必要最低限だとこんな感じ

language: go
go:
  - 1.7
  - tip
before_install:
  - go get github.com/golang/dep/...
install:
  - $GOPATH/bin/dep ensure
script:
  - go test

depのREADMEに書いてるのを.travis.ymlに書いただけです。

.travis.ymlの完全版はこちら https://github.com/sue445/zatsu_monitor/blob/c265f1b9301c056cbac5006952270812c9155266/.travis.yml

注意点としては、Travis CIだと go get しても $GOPATH/bin にPATHが通らない *1 こと。

goveralls でもフルパスで書いてるのでそんなもんかなぁ。。。

あと、golang 1.6系だとdepがビルドできずにエラーになったので1.7以降必須。

https://travis-ci.org/sue445/zatsu_monitor/jobs/196138494

depをいれたメリット

依存するライブラリのバージョンを上げた時にそれがgitの履歴として残るのはいいですね。 https://github.com/sue445/zatsu_monitor/commit/cb1d7ab446e1a511844740f63d366d8a6a901c62

*1:明示的にPATHを通してもダメだった

rubocopでreversibleなmigrationかどうかチェックしたかったので作った

僕がrubocopに送ったPRが v0.47.0 に取り込まれました。

f:id:sue445:20170116111818p:plain

個人的に便利機能だと思うのでこの場を借りて軽く紹介したいと思います。

Rails/ReversibleMigrationについて

Railsのmigrationファイルで change メソッドの中に書いたmigrationコマンドがreversible *1かどうかをチェックするためのcopです

github.com

具体例

https://github.com/bbatsov/rails-style-guide#reversible-migration より抜粋

これだと drop_table :users だけだと逆方向のmigration(create_table)でどのようなカラムでテーブルを作ればいいか分からないのでreversibleではありません。rake db:rollbackrake db:migrate:down した時にエラーになります。

# bad
class DropUsers < ActiveRecord::Migration
  def change
    drop_table :users
  end
end

こういう場合、upメソッドとdownメソッドでそれぞれmigrationを書いてあげないと rake db:rollbackrake db:migrate:down が正しく実行されません

# good
class DropUsers < ActiveRecord::Migration
  def up
    drop_table :users
  end

  def down
    create_table :users do |t|
      t.string :name
    end
  end
end

http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters.html#method-i-drop_table にも書いていますが、 drop_table にブロックを渡すとchangeで逆方向のmigrationを補完する時に create_table の引数扱いになりカラムが作られます。

# good
# In this case, block will be used by create_table in rollback
# http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters.html#method-i-drop_table
class DropUsers < ActiveRecord::Migration
  def change
    drop_table :users do |t|
      t.string :name
    end
  end
end

どうして作ったか?

慣れてくればreversibleなコマンドかどうかは Railsの気持ちになって考えれば だいたい推測はつきます。

例えば

add_column :users, :name, :string

の逆は

remove_column :users, :name

ですが、

remove_column :users, :name

の逆方向を考えた時にどんな型でカラムを作ればいいかの情報がこれだけでは推測できないのでreversibleじゃないといった感じです。

微妙に迷った時も全部 http://api.rubyonrails.org/classes/ActiveRecord/Migration/CommandRecorder.html に書いてあるのでこれを読めば分かります。

しかしコードレビューの度にそれをいちいち指摘するのは面倒なので自動化するために Rails/ReversibleMigration を作成しました。

検出精度について

さっきのリファレンスを全部読んだ上でchangeメソッド内の下記を検出するようにしています

  • 絶対にreversibleじゃないmigrationコマンド
    • change_table
    • change_table_comment
    • execute
    • remove_belongs_to
  • 特定の条件を満たさないとreversibleじゃないmigrationコマンド
    • drop_table
    • change_column_default
    • remove_column
    • remove_foreign_key

ただし特例として reversibleブロックがある場合は reversibleであるとみなして検出対象外にしています。

詳細は下記参照

railsguides.jp

auto correctについて

rubocopのcopでは autocorrectメソッドを定義すればauto correct時の実装を定義することができますが、down(逆方向)のmigrationは過去のmigrationファイルを探して人間が書く必要がありそうな気がしたのであえて実装していません。

Cop開発Tips

次回以降自分がrubocop触りやすくするために開発手順とか参照すべきソースをメモっときます。

前提知識

日本語だとここが参考になりました。

koic.hatenablog.com

tech.sideci.com

rubocop静的解析したいソースをruby-parseでS式を出す

ruby_parser というRubyソースコードを解析するためのgemを使います。rubocopのソースをcloneしていればbundle installで入るので別途gem installする必要はありません。

例えばこんなファイルを作っといて

class ExampleMigration < ActiveRecord::Migration
  def change
    # ブロックがあるのでreversible
    drop_table :users do |t|
      t.string :name
    end

    # ブロックがないのでrubocopで警告を出したい
    drop_table :users
  end
end

ruby-parseに食わせてS式を出します。

bundle exec ruby-parse /tmp/migration.rb
(class
  (const nil :ExampleMigration)
  (const
    (const nil :ActiveRecord) :Migration)
  (def :change
    (args)
    (begin
      (block
        (send nil :drop_table
          (sym :users))
        (args
          (procarg0 :t))
        (send
          (lvar :t) :string
          (sym :name)))
      (send nil :drop_table
        (sym :users)))))

rubocopのcopに on_defon_send などのメソッドを定義することによって、rubocopで解析中にS式のその要素と対応するメソッドが実行されます。

今回の場合はメソッド呼び出し drop_tablechange_table などのメソッド呼び出しをフックしたかったので on_sendメソッドを作りました。

binding.pryなどでとめてnodeの中身を確認

on系のメソッドに binding.prybinding.irb を書いて

def on_send(node)
  binding.pry

任意のspecを実行

$ bundle exec rspec -- spec/rubocop/cop/rails/reversible_migration_spec.rb:85
Run options:
  include {:focus=>true, :locations=>{"./spec/rubocop/cop/rails/reversible_migration_spec.rb"=>[85]}}
  exclude {:broken=>#<Proc:./spec/spec_helper.rb:32>}

Randomized with seed 63344

From: /Users/sue445/workspace/github.com/bbatsov/rubocop/lib/rubocop/cop/rails/reversible_migration.rb @ line 120 RuboCop::Cop::Rails::ReversibleMigration#on_send:

    119: def on_send(node)
 => 120:   binding.pry
    121:   return unless within_change_method?(node)
    122:   return if within_reversible_block?(node)
    123:
    124:   check_irreversible_schema_statement_node(node)
    125:   check_drop_table_node(node)
    126:   check_change_column_default_node(node)
    127:   check_remove_column_node(node)
    128:   check_remove_foreign_key_node(node)
    129: end

[1] pry(#<RuboCop::Cop::Rails::ReversibleMigration>)> node
=> s(:send, nil, :drop_table,
  s(:sym, :users))
[2] pry(#<RuboCop::Cop::Rails::ReversibleMigration>)>

慣れていないうちは1回1回 binding.pry で止めてnodeの中身を確認しつつデバッグしていくのがいいと思います。余談ですがデバッグに慣れていなさすぎて1日 1migrationコマンド対応が限界でした('A`)

S式にマッチさせるmatcherを書く

cop内に

def_node_matcher :drop_table_call, <<-END
  (send nil :drop_table ...)
END

のようなmatcherを書くことによって、

drop_table_call(node) do
  # drop_table メソッドの呼び出しがあればこの中が評価される
end

のように処理を挟むことができます。

matcherの書き方は https://github.com/bbatsov/rubocop/blob/master/lib/rubocop/node_pattern.rb のコメントを要熟読

matcherに $_$... を書くことにより、マッチした文字列を変数として取得することもできます。

$_ は単一nodeを取得

# remove_foreign_keyに渡された引数の2つ目のみを取得するmatcher
def_node_matcher :remove_foreign_key_call, <<-END
  (send nil :remove_foreign_key _ $_)
END
remove_foreign_key_call(node) do |arg|
  # $_ の内容がブロック引数のargに入ってくる
  if arg.hash_type?
    # remove_foreign_keyの第2引数がHash(テーブル名以外)の時に警告を出す
    add_offense(
      node, :expression,
      format(MSG, 'remove_foreign_key(without table)')
    )
  end
end

$... は以降のnodeを全部取得

# remove_columnに渡された引数を全部取得するmatcher
def_node_matcher :remove_column_call, <<-END
  (send nil :remove_column $...)
END
remove_column_call(node) do |args|
  # argsに引数全部入ってくる
  if args.to_a.size < 3
    # 引数が3個未満の時に警告を出す
    add_offense(
      node, :expression,
      format(MSG, 'remove_column(without type)')
    )
  end
end

*1:rake db:rollback や rake db:migrate:down した時に逆方向のmigrationを自動補完する

僕に「サザエさん」の話させたら長くなりますよ?

これは 僕に「○○」の話させたら長くなりますよ Advent Calendar 2016 - Adventar の25日目です。

www.adventar.org

僕に「サザエさん」の話させたら長くなりますよ?

サザエさんとは?

テレビ放送45年以上で老若男女誰でも知ってる圧倒的国民的アニメです。

www.fujitv.co.jp

サザエ実況採点ポイント

採点ポイントを細かく解説します

ちなみに毎週この採点表で採点して、ツイートしています。

採点表 | SuperSazaeTime History #sst_history

エイケン

サザエさんの制作会社です。エイケンが製作すると◯がつきます。

www.eiken-anime.jp

開幕雪室

サザエさんは30分間で3本の話で構成されていますが、「開幕雪室」はその1番目が脚本家の雪室俊一先生が担当する回だと◯がつきます。

テクマクマヤコン―ぼくのアニメ青春録

テクマクマヤコン―ぼくのアニメ青春録

城山先生生存確認

雪室先生と並んでサザエさん二大脚本家の1人、城山昇先生の脚本が登場するA〜Cパートのどこかで出ると◯がつきます。

レア脚本家

雪室先生と城山先生以外の脚本は出ると◯がつきます。

ハイエナ行為

「ハイエナ」ことノリスケがハイエナ行為(磯野家などからたかる)を行うと◯がつきます

shirts-custom.com

BACK COME ON

波平が「バッカモーン!(BACK COME ON)」と怒鳴ると◯がつきます

SAYOU

波平が「左様(SAYOU)」と言うと◯がつきます。

創価

波平が「そうか(創価)」と言うと◯がつきます。

ちなみに「BACK COME ON」「SAYOU」「創価」は波平の三大名言です。

花沢異常性欲

「カツオの嫁」こと花沢さんがカツオに対して異常な性欲を発揮すると◯がつきます。無限性欲とも呼ばれます

タラヲ氏ね

文字通り

サイコパス堀川

タラヲがウザいのは元からですが、近年頭角を現してきた堀川くん。そのサイコパスっぷりが発揮されると◯がつきます

news.merumo.ne.jp

otapol.jp

レアBGM

普段聴かないBGMが流れると◯がつきますが、よく聴くレアBGMもあります。

熱いカツオDIS

いたずら小僧として世間で認知されているカツオも最近は割とそんなこともなく良識派だと思います。

そんな中偏見だけででカツオがぬれぎぬを着せられたりDISられたりすると◯がつきます。

タラヲメイン

タラヲメインの話が1本でもあると◯がつきます。

じゃんけん

番組最後の予告でサザエさんが出した手です。

脚本家で振り返る2016年サザエさん

今年はサザエさんを実況しながらパートごとの脚本家の名前を全部スプレッドシートにメモしていました。

2016年版サザエ脚本家リスト - Google スプレッドシート

ここからいろいろ検証してみたいと思います。

開幕雪室率

開幕雪室率は98%。

今年全51週あったうち、3/27の1時間スペシャルを除いて全て雪室先生が先発脚本でした!

城山先生生存率

城山先生が全く出てこなかったのは計11回なので、今年の城山先生生存率は80%でした

レア脚本家は本当にレアなのか?

今年の各脚本家の回数を数えたら下記のようになりました

f:id:sue445:20161225190922p:plain

雪室先生と城山先生が圧倒的すぎて、他の脚本家が出るのはレアだということが証明されましたと思います

ネタ提供で雑に振り返る2016年

その週に個人的に話題になってたものを提供にしています

1月

2月

3月

4月

5月

5/29は別件で実況していなかったのでツイートは無し

6月

7月

8月

9月

10月

10/23は日本シリーズ第2戦でサザエさんの放送はありませんでした

11月

12月

予告

来年のサザエさんは2017/1/8(日)からです

rubicureのこれまでとこれから

これは プリキュア Advent Calendar 2016 - Adventar の16日目です。

www.adventar.org

12/16はキュアピースの中の人の金元寿子さんのお誕生日です。おめでとうございます!!!!

今回はこれまでのrubicureとこれからのrubicureについて書くポエムです

rubicureとは

プリキュアRuby実装です

github.com

最新情報は先日Qiitaに書いたのでこちらを御覧ください。

qiita.com

これまでのrubicure

リリースノート

この辺

https://github.com/sue445/rubicure/blob/master/CHANGELOG.md

rubicureのバージョニングポリシー

僕のgemはだいたいセマンティックバージョニングに則っています

セマンティック バージョニング 2.0.0 - Semantic Versioning

ただ、rubicureの場合若干変えていて、同一シリーズ内の場合は必ず +0.0.1し、シリーズが変わる時に +0.1するようにしています。

https://github.com/sue445/rubicure/blob/master/CHANGELOG.md

今まで割と曖昧なバージョニングをしていましたが、魔法つかいプリキュアで上記のバージョニングを思いついたので魔法つかいプリキュアは 0.4.x 系を保ち続けています

moongiftにとりあげられた

www.moongift.jp

正月に帰省先でファッ!?となった記憶があります。

周辺ツールとかができた

Rubyをインストールして gem install すればすぐに使えるというお手軽さから(推測)、周辺ツールがいろいろできています

github.com

igreque : Info -> ユナイトプリキュア!このあとVim! #cure_advent

これからのrubicure

rubicure v1.0.0

映画プリキュアがオールスターズからドリームスターズになるということは記憶に新しいと思います

www.precure-dreamstars.com

nlab.itmedia.co.jp

これを機にrubicureも1.0.0を考えています

今考えているのは主に

  • ドリームスターズ対応
  • 古いRuby(2.0系, 2.1系)の非サポート化
    • Travis CIでビルドしない(古いRubyでもインストールはできるけど動作は保証しない)だけにするか、required_ruby_versionを変更して古いRubyではインストールすらできなくするかまでは考えてない
    • 雑にやるなら前者でもいいと思うけど、2.1系でうまく動かないってってissueがきて対応するのも面倒なので個人的には後者でいい気もしている。(もうすぐ出るRuby 2.4から数えたら3世代以上前なので切り捨ててもバチは当たらないだろう)
  • deprecatedメソッドの削除

辺りです。

個人的にはactuvesupportも外したいところなのですが厳しそうな予感はしてる。。。

区切り的に次回作の「キラキラ☆プリキュアアラモード」でv1.0.0にしようと考えています

www.toei-anim.co.jp

最後に

これからもrubicureをよろしくお願いします m( )m

久しぶりにJenkinsプラグインをリリースしようとしたら謎のエラーで失敗した

Qiitaや会社ブログにはエントリ書いてたけどこっちのブログは1ヶ月間更新無しとかマジか。(挨拶)

前置き

先日自分がメンテしてるJenkinsプラグインにPRがきて「よっしゃ!マージしてリリースするぜ!」と思っていつものリリースコマンド *1 をたたいたらリリース時に謎のエラーがおきて途方に暮れていた時の出来事です。

日本語だと今回の事象が見つからなかったので日本語でメモ残しておきます

tl;dr

org.jenkins-ci.plugins.plugin(全てのJenkinsプラグインの親プラグイン)を2.5以降にしよう

エラー内容

[INFO] [INFO] --- maven-install-plugin:2.3.1:install (default-install) @ gitlab-logo ---
[INFO] [INFO] Installing /Users/sue445/dev/workspace/github.com/jenkinsci/gitlab-logo/target/checkout/target/gitlab-logo.hpi to /Users/sue445/.m2/repository/org/jenkins-ci/plugins/gitlab-logo/1.0.2/gitlab-logo-1.0.2.hpi
[INFO] [INFO] Installing /Users/sue445/dev/workspace/github.com/jenkinsci/gitlab-logo/target/checkout/pom.xml to /Users/sue445/.m2/repository/org/jenkins-ci/plugins/gitlab-logo/1.0.2/gitlab-logo-1.0.2.pom
[INFO] [INFO] Installing /Users/sue445/dev/workspace/github.com/jenkinsci/gitlab-logo/target/checkout/target/gitlab-logo.jar to /Users/sue445/.m2/repository/org/jenkins-ci/plugins/gitlab-logo/1.0.2/gitlab-logo-1.0.2.jar
[INFO] [INFO] Installing /Users/sue445/dev/workspace/github.com/jenkinsci/gitlab-logo/target/checkout/target/gitlab-logo-sources.jar to /Users/sue445/.m2/repository/org/jenkins-ci/plugins/gitlab-logo/1.0.2/gitlab-logo-1.0.2-sources.jar
[INFO] [INFO] Installing /Users/sue445/dev/workspace/github.com/jenkinsci/gitlab-logo/target/checkout/target/gitlab-logo-javadoc.jar to /Users/sue445/.m2/repository/org/jenkins-ci/plugins/gitlab-logo/1.0.2/gitlab-logo-1.0.2-javadoc.jar
[INFO] [INFO]
[INFO] [INFO] --- maven-deploy-plugin:2.6:deploy (default-deploy) @ gitlab-logo ---
[INFO] Uploading: http://maven.jenkins-ci.org:8081/content/repositories/releases/org/jenkins-ci/plugins/gitlab-logo/1.0.2/gitlab-logo-1.0.2.hpi
[INFO]
[INFO] Uploading: http://maven.jenkins-ci.org:8081/content/repositories/releases/org/jenkins-ci/plugins/gitlab-logo/1.0.2/gitlab-logo-1.0.2.pom
[INFO]
[INFO] [INFO] ------------------------------------------------------------------------
[INFO] [INFO] BUILD FAILURE
[INFO] [INFO] ------------------------------------------------------------------------
[INFO] [INFO] Total time: 3:57.988s
[INFO] [INFO] Finished at: Thu Oct 27 08:50:12 JST 2016
[INFO] [INFO] Final Memory: 71M/499M
[INFO] [INFO] ------------------------------------------------------------------------
[INFO] [ERROR] Failed to execute goal org.apache.maven.plugins:maven-deploy-plugin:2.6:deploy (default-deploy) on project gitlab-logo: Failed to deploy artifacts: Could not transfer artifact org.jenkins-ci.plugins:gitlab-logo:hpi:1.0.2 from/to maven.jenkins-ci.org (http://maven.jenkins-ci.org:8081/content/repositories/releases): Connection to http://maven.jenkins-ci.org:8081 refused: Operation timed out -> [Help 1]

うーん、http://maven.jenkins-ci.org:8081プラグインアップロードする時にタイムアウト????

エラー全文 https://gist.github.com/sue445/259d4c71d2e4d34b33948cb61c66a900

原因

プラグインのアップロード先が http://maven.jenkins-ci.org:8081 から https://repo.jenkins-ci.org に変わったため

ソース

原因が見つかったのは https://issues.jenkins-ci.org/browse/JENKINS-37423 の下記のコメントを見つけたのがきっかけでした。(このきっかけを見つけるまで約1週間かかった)

CrossBrowserTesting.com LLC added a comment - 2016/Aug/22 11:23 PM fixed it. had to update the parent to version of at least 2.5 in pom.xml https://wiki.jenkins-ci.org/display/JENKINS/Hosting+Plugins#HostingPlugins-Workingaroundcommonissues

で、リンク先には

Either overwrite the repository location in your POM to point to https://repo.jenkins-ci.org, or update to the parent plugins POM 2.5 or newer

って書いてた。親プラグインのバージョン上げずにPOMのアップロード先だけ変えるってのが分からなかったので正攻法に親プラグインのバージョンを上げることに。

実際に対応したのがこのPR

github.com

この修正でプラグインをリリースすることができました

org.jenkins-ci.plugins.pluginのバージョンを上げるとBetamaxがうまく動かない問題

Betamaxというのはhttp通信をモック化するGroovy製のライブラリです。自分のプラグインだとGitLabの通信部分をスタブにするために使っていました

betamax.software

上記PRでbetamaxを消した理由をメモ

  1. org.jenkins-ci.plugins.pluginのバージョンを上げるとつられてJettyのバージョンも上がって、betamaxの中で使ってるJettyのバージョンが古すぎてビルドでエラーになる
  2. betamaxのバージョンを1系から2系に上げると今度はSSLのモックがうまく効かない
  3. 公式のドキュメント 読むと「 keytool -importcert 〜 でbetamaxの鍵を追加すると動くようになるよ」って書いてある
  4. しかし自分で管理してるJenkinsならまだしもcloudbeesのJenkinsでそれは無理ぽ

ってことでbetamaxを捨てて昔ながらにStubクラスからテスト対象のクラスを継承してテストするようにしました

github.com

*1:mvn release:prepare release:perform