supermanner log

supermannerです

CircleCIからGitHub Actionsに移行する際に迷ったポイント・ハマったポイント

f:id:supermanner:20191215131630j:plain
最近illustratorを使えるようになりたくて、自分でOGPを書いてみました

こんにちは。最近cosme kitchenで新しいコスメを試すことにハマっている @super_mannerです。 今年大人買いした漫画がBEASTARS鬼滅の刃進撃の巨人で、ちょっと買いすぎなのでは?と思っています...。 普段はRailsやVue.jsを書いています。PHPer歴が長いので、色々と気づきがあって面白いです😊

さて、今回は業務で取り扱った 「CircleCI => GitHub Actionsへの移行」 でいくつか無駄にハマったポイントについて書こうと思います。 超初歩的なポカから、なるほどなーというところまで色々とあったので今後導入される方の助けになれば幸いです🍎

この記事はTECHPLAY女子部 Advent Calendar 2019の12日目の記事です。

目次

導入したjob

今回移行したものは大きく分けると3種類です。
まだすべてを移行しきれていないので移行できたものだけを列挙します。

  • lint系のjob
    • rubocop
    • slim-lint
    • stylelint
  • test系のjob
  • 定期実行系のjob
    • bundle update

これらを一つずつ移行していき、問題がなければCircleCIから外すというオーソドックスなやり方で実施しました。 詳しいworkflowの書き方は、公式ドキュメントがかなり丁寧なことと、昨今流行っているのか(?)ググるといい記事がたくさん出てくるので本エントリでは割愛させていただきます。

f:id:supermanner:20191215131717p:plain
何かがコケるとコケ太郎がSlack通知してくれます

迷ったポイント

workflowの分け方

さて、早速洗い出しが終わった段階で一つ迷った点がありました。
それは「workflowにどのような切り口で仕事をもたせるか」です。
GitHub Actionsのベストプラクティスがまだよくわかっていないのが大きいのですが、

  • 使っているライブラリ群でworkflowを分けるのがよいのか(gemが必要なjobとnode_modulesが必要なjobでworkflowをわける)
  • 担っている責務ごとにworkflowを分けるのが良いのか(lint/test/cronでworkflowをわける)

の2パターンを思いつき、どちらにするべきなのかの判断がが非常に悩ましかったです。
私が悩んだ観点はそれぞれこんな感じでした。

ライブラリで分ける 責務で分ける
処理時間が早そう。workflowが分散されていない分セットアップの時間が短縮できそうだ 利用者がわかりやすそう。それぞれのジョブが失敗したときに何が要因なのかが一発でわかりそうだ

並列で実行されるかつ、cacheがうまく機能していそうだったことから(有効範囲はこのあたりを読むとわかりやすいです) 今回はメンテナンス性や利用者がわかりやすいように後者の「責務で分ける」でいくことにしました。

ハマったポイント

さて、ここが本日の本丸です。 やることは決まっているのでサクサク行くだろうなと思ってやり始めたのですが、いくつか無駄に時間を使ったポイントが出てしまったので 一挙大放出します。

cache keyを間違えていた

11月頃に、GitHub Actionsにも待望のcache機能が登場しました。
意気揚々と使っていたのですが、掲題のとおりです。。。はい。 でもそこそこあるケースだと思うのでしっかり確認しましょう...。

bundle installタスクがうまく動かない

これはものすごいポカミスでした!
当初このように, bundle installしたものをキャッシュしておき、変更があればcontainer内部で再installするという至ってシンプルなworkflowを書いていました。

- name: Run Bundle install
  if: steps.bundle_install.outputs.cache-hit != 'true'
  run: |
    bundle config --local build.mysql2 "--with-ldflags=-L/usr/local/opt/openssl/lib"
    bundle install --jobs=4 --retry=3 --path vendor/bundle --clean
- name: Run rubocop
# 以下略

しかし、想定したとおりにrubocopが動きません。
それもそのはず、 .bundle/config はcacheしておらず引き継がれない(もちろんgit管理もしてない) のでせっかく入れていた設定が全て吹っ飛んでしまったのです。 なので、初回とキャッシュヒット時で処理を分岐させて回避しました。

- name: touch bundle config
  if: steps.bundle_install.outputs.cache-hit == 'true'
  run: |
    mkdir .bundle
    touch .bundle/config
    echo BUNDLE_PATH: "vendor/bundle" >> .bundle/config

今回は、rubocop実行時にpathがほしかっただけなので最低限の設定を注入して終了です。

ということで、結構力技ではありますがなんとななりました。設定をworkflow内部で注入している場合は気をつけたほうが良さそうです。

git diffする対象を間違えていた

すべてのファイルをlint対象にしてもいいんだけどちょっと実行時間が気になるよね...と思った私は

「よし、git diffして差分が合ったファイルだけlint対象にしよう」

と思い下記の様に書いていました。

- name: Run rubocop
  run: git diff --name-only | egrep '.+rb$' | xargs bundle exec rubocop -D

パット見でお気づきの方もいるかもしれないですが、私は何も変更対象にならないことに気づかないまま数分が闇に葬られていきました👼

色々気づいて最終的に修正した結果こうなりました。

- name: Run rubocop
  run: git diff --name-only origin/master --diff-filter=d | egrep '.+rb$' | xargs bundle exec rubocop -D --force-exclusion

修正したポイントは

  • 自分自身とdiffをとっていたので origin/masterとのdiffを取るようにした
    • 本当はPRが向いているbranchとのdiff取りたかったのですが調べきれずに今の所origin/master差分で見ています(releaseにこまめに差分を取り込んでれば問題ないと判断しました)
  • --diff-filter=d をつかって削除ファイルはlint対象から外した
    • review時にこれ意味ないことしてるような...という指摘をもらって直しました
  • --force-exclusion をつけて .rubocop.yml の設定を読み込めるようにした
    • パイプで明示的にファイル名を渡しているのでこれをつけていないと除外ファイルの設定が反映されなかったです

という点です。

GitHub Actionをつくった

定期実行タスクの一つにbundle updateで更新があれば自動的にPRを飛ばすといったものがあったので
いい感じのactionがないか探していたのですがなかったので...作ってみました!

github.com

機能としては至極単純で

「お好みのトリガでbundle updateを行い、差分があれば指定したユーザーをreviewerにしてPRを飛ばす」

というものです。PRのdescription表示にはbundler-diffを使わせていただきました。ありがとうございます!!

f:id:supermanner:20191215131834p:plain
こんな感じでPRが飛んできます

事前に見ていただいた方からもうすでに提案を頂いているくらいに完全に網羅したわけじゃないのですがw、世の中に出してみようと思い切ってreleaseしてみました〜🎉
PRやIssueをいただけると勉強になりますし、今後のメンテの励みになるので、もしよかったら使ってみてください😊そしてPRください😏

最後に

というわけで、GitHub Actionsなかなかたのしいぞい!ということがちょっとでも伝われば嬉しいです!! 読んでくださりありがとうございました🙆