Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

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

More than 3 years have passed since last update.

自動実行の流れ

a0c0b578-7cf8-2a98-0bf5-ccd21005aa8e.png

1. CircleCIを起動する

Nightly builds というAPIを使います。本来は重たいテストを夜中に1回だけ実行する、みたいな用途で使うことを意図しているのでNightly buildsという名前になっていますが、簡単な話が「外から環境変数を追加した上で、特定のブランチに対するテストを実行させる」機能です。

BUNDLE_UPDATE という環境変数を設定した上でmasterブランチをビルドするとbundle updateが実行されるようにすることにしました。以下のようなスクリプトを用意し、cronで平日の朝に自動実行するようにしています。

_project=user-name/repo-name  # 適当に変える
_branch=master
_circle_token=$1

trigger_build_url=https://circleci.com/api/v1/project/${_project}/tree/${_branch}?circle-token=${_circle_token}

# BUNDLE_UPDATE環境変数をつけてCIを走らせる
post_data='{ "build_parameters": { "BUNDLE_UPDATE": "true" } }'

# Nightly buildsを実行
curl \
  --header "Accept: application/json" \
  --header "Content-Type: application/json" \
  --data "${post_data}" \
  --request POST ${trigger_build_url}

2. CIサーバ上で bundle update を実行する

BUNDLE_UPDATE かつmasterブランチの場合は bundle update だけを実行するようにします。1

circle.yml
test:
  post:
    - >
      if [ -n "${BUNDLE_UPDATE}" -a "${CIRCLE_BRANCH}" = 'master' ] ; then
        bundle update
      fi

3. Gemfile.lockが変更されている場合はプルリクエストを作る

circle.yml
deployment:
  auto-bundle-update:
    branch: master
    commands:
      - >
        if [ -n "${BUNDLE_UPDATE}" ] ; then
          bash script/circleci/create_pull_request_if_needed.sh
        fi
script/circleci/create_pull_request_if_needed.sh
export BRANCH=bundle-update-`date -u "+%Y%m%d"`
if [ -n `git status -sb 2> /dev/null | grep Gemfile.lock` ] ; then
  git config --global user.email user@eample.com
  git config --global user.name 'your name'
  git add Gemfile.lock
  git commit -m 'Bundle update'
  git branch -M $BRANCH
  git push origin $BRANCH
  bundle exec ruby script/circleci/create_pull_request.rb
fi

プルリクエストを作る部分はrubyで作りました。

script/circleci/create_pull_request.rb
require 'octokit'

client = Octokit::Client.new(access_token: ENV['GITHUB_ACCESS_TOKEN'])
client.create_pull_request(
  'user-name/repo-name',  # 適当に変える
  'master',
  ENV['BRANCH'],
  'Bundle update',  # Title
  ''                # Body
)

4. githubのhookでCircleCIが起動され新しく作られたブランチのテストが走る

ここからは通常のCIと同じです。テスト結果に応じて3で作られたプルリクエストを煮るなり焼くなりします。

その他の話題

背景

bundle updateとは

bundle update は依存しているgemに新しいバージョンが公開されている場合、その新しいものを使うように更新する作業のこと。bundlerは明示的に bundle update を実行しなければ、同じgemの同じバージョンをずっと使い続ける。

なぜupdateする必要があるのか

bundle update せずに放置すると、古いバージョンに含まれるバグ :bug: の修正や、パフォーマンスの改善、機能追加などの恩恵を享受することができない。逆に言えば、その果実を回収するために bundle update を実行する。

なぜ定期実行する必要があるのか

bundle update を実行すると、場合によってはアプリが意図した通りに動かなくなることがある。これは bundle update によって変更されるバージョンの差分が大きければ大きいほど発生しやすくなり、また確認すべきドキュメント、コミットの数が増えるため対応するコストが大きくなる。大量のgemが一度にアップデートされると、仮に問題が発生した時に、それがどのgemを更新したことによって発生するようになったのかを切り分けることが難しくなるという問題も発生する。

定期実行すれば、大きなジャンプを経験する可能性が減るため、仮に問題が起きても対応するのが容易になる。

なぜ自動化する必要があるのか

理由は単純に「忘れてしまう」ため。忘れてしまうと大きなバージョンチェンジにいっきに対応する必要が発生し、前述の通り追従にかかるコストが増加するのでますます bundle update する気がなくなってしまう悪循環が発生する。

関連実装

bundlerに限らず色々な言語のライブラリ管理ツールをアップデートするためのgem。抽象化しなくても十分に理解できるレベルだと思ったので使わなかった。

上のgemをSaaSとして提供しているもの。リポジトリへのアクセス権限を渡す必要があるので :no_good: publicリポジトリの自動更新をしたい場合は、これを使った方がいいと思う。


  1. テストの実行もスキップするようにしています。 

yuku_t
FLYWHEEL社でソフトウェアエンジニアをしています。昔はIncrements社最初の従業員としてQiitaを開発したりCTOやったりしていました。
http://yuku.takahashi.coffee
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away