CircleCI Advent Calendar 2018の9日目です🎄
8日目の記事は @f-naoto832 さんのCircleCIで試行錯誤した話でした。
はじめに
この記事は
CircleCI2.0とdanger(ruby)専用のdocker imageを使うことで
どんなプロジェクトでもdangerを利用できるようにした例を紹介します。
主な対象読者
- 最終的にPRで、approveする人(Techリードとか?)
- ruby, js, swift以外のどんなプロジェクトでもdangerを実行したい人
- CIの整備をしている人
きっかけ
- ソースコードレビュー時、「〇〇、まだ終わってないんだけど」って毎回指摘するのがめんどくさい。そして、される側も嫌だろう。
- ruby on railsのプロジェクトにはdangerをGemfileに追加しているが、Scala / Java / Golangとかのプロジェクトでもやりたい。
- モノリシックだったが、マイクロサービスにして、リポジトリが増えて、メンテしなければならないDangerfileが増えた。
- dangerのバージョンを変更する時や、danger pluginを追加する時に、毎回それぞれのリポジトリのGemfileを変更しなければならない。
- そもそもサービスに関係ないdangerのgemをGemfileに追加するのはいかがなものだろうか?
前提
- danger自体の説明は他にもわかりやすい資料があるので、そちらを参考。
ex. Dangerでpull requestレビューの指摘事項を減らす
https://www.slideshare.net/ShunsukeMaeda/dangerpull-request -
Dangerfile
の詳細な書き方に関しては、触れません。 - 「Only Build pull requests」関連の話は、別の記事を参考。
ex. CircleCIでDangerを動かすためにOnly Build Pull Requestsの設定を調べてみた
https://blog.sshn.me/posts/circleci-only-build-pull-requests/ - docker image作成時のcircleciの設定に関しても触れません。
作ったもの
対象 | URL |
---|---|
circleci-danger | https://github.com/jkkitakita/circleci-danger |
dangerfile | https://github.com/jkkitakita/dangerfile |
dockerhub | https://hub.docker.com/r/jkkitakita/circleci-danger/ |
circleci-danger-sample | https://github.com/jkkitakita/circleci-danger-sample |
各種設定
Dockerfile
まずは、danger専用のdocker imageのDockerfile.
https://github.com/jkkitakita/circleci-danger/blob/master/Dockerfile
FROM ruby:2.3.1-alpine
ARG VCS_REF
ARG BUILD_DATE
LABEL maintainer="jkkitakita (Jun Kitamura)" \
org.label-schema.url="http://jkkitakita.com" \
org.label-schema.build-date=$BUILD_DATE \
org.label-schema.vcs-url="https://github.com/jkkitakita/circleci-danger"\
org.label-schema.vcs-ref=$VCS_REF
ENV HOME=/home/circleci
# CircleCIのuserを作成
RUN apk update && apk upgrade && \
apk add --no-cache bash git openssh && \
addgroup -g 3434 circleci && \
adduser -D -u 3434 -G circleci -s /bin/bash circleci
# CircleCIのuserへ切り替え
USER circleci
# Gemfile, Gemfile.lock, Dangerfileをimage内にcopy
COPY --chown=circleci:circleci Gemfile* $HOME/danger/
COPY --chown=circleci:circleci Dangerfile $HOME/danger/
# $HOME/dangerへ移動
WORKDIR $HOME/danger
# bundlerをinstallして、globalにdangerをinstall
RUN gem install bundler --no-document && \
bundle install
CMD ["/bin/sh"]
ポイント
-
circleci
userを作成しました。(rootじゃなくても、dangerだけ実行できればいいので。) - ciでの実行頻度が高い可能性があるので、なるべく軽くするために
alpine
を利用した。(2.4/2.5も試したのだが、うまくいかなかった。。なぜ。。) -
Gemfile
,Gemfile.lock
,Dangerfile
にcircleci user権限を付与しながらcopyしないと、権限がない。 - 事前にしておけば、あとでbundle installする必要ない。
Gemfile
bundle installする時のGemfile.
https://github.com/jkkitakita/circleci-danger/blob/master/Gemfile
source 'https://rubygems.org'
gem 'danger'
# Add a danger plugin if necessary.
## gem 'danger-plugin_name'
ポイント
-
gem 'danger'
を追加 - 必要に応じて、
gem 'danger-plugin_name'
を追加 (個人的には、LGTMのplugin好きです。笑)
gem 'danger-todoist'
gem 'danger-lgtm'
https://github.com/hanneskaeufler/danger-todoist
https://github.com/leonhartX/danger-lgtm
Dangerfile(共通)
docker imageに含める共通Dangerfile.
https://github.com/jkkitakita/circleci-danger/blob/master/Dangerfile
# Change to the path of the dangerfile you want to import.
danger.import_dangerfile(github: "jkkitakita/dangerfile", branch: "master")
ポイント
-
danger.import_dangerfile
を利用する
https://danger.systems/reference.html -
Dangerfile
自体を、別リポジトリで管理するようにしておけば、毎回docker imageを作り直す必要がない - また、CircleCI実行時、このDangerfileを修正してから、dangerを実行するようにすれば、プロジェクト毎に違うルールで、CIを実行できる。
dangerfile/Dangerfile(ex. ruby on rails)
danger.import_dangerfile
するDangerfileの例。
https://github.com/jkkitakita/dangerfile
# Alias
BIG_PULL_REQUEST_LINES = 1000
APP_FILES = /^(app|lib)/
TEST_FILES = /^(features|spec|test)/
has_app_changes = !git.modified_files.grep(APP_FILES).empty?
has_test_changes = !git.modified_files.grep(TEST_FILES).empty?
## Check label and body of PR.
has_label_wip = github.pr_title.match(/wip/i) || github.pr_labels.include?('wip') || github.pr_labels.include?('Wip') || github.pr_labels.include?('WIP')
has_pr_body_no_check = github.pr_body.match(/- \[ \]/)
# Request changes and Attention
## TODO in code.
## ref. https://github.com/hanneskaeufler/danger-todoist
todoist.message = "TODO対応してくれないの?:cry:"
todoist.warn_for_todos
todoist.print_todos_table
## WIP
fail('まだWIPだからmergeできないの!') if has_label_wip
## TODO in PR body.
fail('「[ ] チェックリスト」全部終わらせるなの!') if has_pr_body_no_check
## Attention to code
warn('1000行以上の修正は、危ないかもだけどー?') if git.lines_of_code > BIG_PULL_REQUEST_LINES
warn('appのコードを修正してるけど、testは書かないのー?') if has_app_changes && !has_test_changes
## Attention to a PR without any changes
if git.modified_files.empty? && git.added_files.empty? && git.deleted_files.empty?
message('何も修正してないかもだけどー?')
end
# Procedure for re-executing CircleCI
message(
"@#{github.pr_author}\n\n"\
"失敗した時は、指摘内容を修正して\n"\
"<a href='https://circleci.com/workflow-run/" + ENV['CIRCLE_WORKFLOW_ID'] + "'>https://circleci.com/workflow-run/" + ENV['CIRCLE_WORKFLOW_ID'] + "</a>\n"\
"から「Rerun from failed」を実行してなの!"
) if !violation_report[:errors].empty?
# LGTM pic
# ref. https://github.com/leonhartX/danger-lgtm
lgtm.check_lgtm
ポイント
-
APP_FILES
、TEST_FILES
あたりはプロジェクト毎に追加・修正 - GitHubのPRの画面上に、CircleCIの「Trigger Jobs」を叩く「ボタン」をmessageに表示させたかったが、できなかったので、workflowのリンクを表示させた。
.circle/config.yml(各リポジトリ側)
version: 2
jobs:
danger:
docker:
- image: jkkitakita/circleci-danger
steps:
# ~/projects(defaults)へ、対象リポジトリのsourceをcloseする
- checkout
# 共通Dangerfileをコピー
# 対象リポジトリにすでにあるDangerfileを利用する場合、実行不要。
- run: cp -f ~/danger/Dangerfile .
# danger実行
# --fail-on-errors=trueをつけると、PRでfailになります。
- run: bundle exec danger --fail-on-errors=true
workflows:
version: 2
danger:
jobs:
- danger
# contextを使う場合
# - danger:
# context: danger
ポイント
- 共通Dangerfileを利用する場合、
~/danger/Dangerfile
をカレントへコピーすればok。 -
--fail-on-errors=true
をつけて実行しないと、jobは成功しているけど、dangerだけ失敗してしまう(rerunできない) - 複数プロジェクトで利用する場合は、
context
にした方が、プロジェクト毎にEnvitonmentsを毎回設定しなくて良い。
実際にやってみると...
- GitHub - PR
https://github.com/jkkitakita/circleci-danger-sample/pull/1 - CircleCI - Job
https://circleci.com/gh/jkkitakita/circleci-danger-sample/8
結論
- 業務では、
Scala / Java / Golang / js etc...
なプロジェクトにこのdanger jobを導入していますが、今の所、問題なく動作しています。 -
「ci通したら、レビュー依頼出してもらえますか?」
って言うコミュニケーションをしておくだけで、スムーズになった。 -
Dangerfileの管理
が格段に楽になった!!
今後の課題
- 業務では、mergeしたら、buildを実行する運用をしているので、「Only Build pull requests」はoffにしているので、Pull Requestをcreateしたら、buildを実行されるようにしたい。今まで↓みたいに、1日1回深夜に実行したりしていたのですが、無駄にbuildが実行されてしまうので、どうにかしたい。
scheduled-workflow:
triggers:
- schedule:
cron: "0 18 * * *"
filters:
branches:
ignore:
- master
jobs:
- danger:
context: danger
まさに、2日目の、@yasuhiroki さんの
CircleCIのOnly build pull requestsをoffにしてもプルリクエストを作ったらJobを実行したい
が参考にやってみようかなと思う!
2. dangerのdocker imagesのrubyのバージョンをあげたい。(ruby2.3系は2018年12月にサポートが切れるので。。)
3. CircleCIのAdventcalendarで言う事かわかりませんが(笑)、TravisCIでも実行できるようにしたい。
さいごに
「ここは違うだろ!」「ここはこうした方がいいよ!」
とかあれば、いつでも、コメントください。非常に喜びます。