bundle update のプルリクエストを毎日自動で作成する

Gemfile.lock を最新に保つため、bundle update を毎日自動でできるようにしたい。
Tachikoma.io というサービスもあるみたいだけど、 private repo は有料っぽいので自作した。

勝手に update されてアプリケーションがバグると困るので、

  1. Gemfile.lock を更新したプルリクエストを作る
  2. プルリクエストを人間が確認してマージ

という手順で行う。

先人の知恵

ここら辺を参考にした。

環境

  • CircleCI 1.X
    • 1年前に構築した環境なので 2.0 ではない...
  • GitHub Private Repository
  • Heroku
  • Rails 5.X

流れ

  1. Heroku Scheduler が CircleCI を叩く
  2. CircleCI で bundle update を実施
  3. Gemfile.lock 更新があれば CircleCI が GitHub に PR を作成
  4. PR を人間が確認してマージボタンをポチ

1. 前準備

2. rake task 作成

lib/tasks/bundle_update_pull_request.rake を作成する。

# frozen_string_literal: true
namespace :bundle_update_pull_request do
  desc 'pull request if bundle updated'

  task check: :environment do
    project = 'repo-team/repo-rails'
    branch = 'master'
    token = 'CircleCI Token'
    params = '{"build_parameters": {"BUNDLE_UPDATE": true}}'

    header = { 'Content-Type' => 'application/json' }
    client = Net::HTTP.new('circleci.com', 443)
    client.use_ssl = true
    response = client.post("/api/v1/project/#{project}/tree/#{branch}?circle-token=#{token}", params, header)
  end
end

CircleCI の Nightly Builds を叩くだけの task。
params に '{"build_parameters": {"BUNDLE_UPDATE": true}}' を渡していて、 CircleCI ではこのパラメータをみて bundle update するのか判断する。

この task は以下のコマンドで実行できる。

$ bundle exec rake bundle_update_pull_request:check

3. circle.yml

circle.yml をこんな感じで書く。

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

deployment:
  master:
    branch: master
    commands:
      - >
        if [ -n "${BUNDLE_UPDATE}" ] ; then
          bash script/circleci/create_pull_request_if_needed.sh
        fi

2 で params に渡した BUNDLE_UPDATE が true だったら bundle update 実行 + PR 作成するようになっている。

4. create_pull_request_if_needed.sh

script/circleci/create_pull_request_if_needed.sh を作成する。

#!/bin/bash

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 git@git.com
  git config --global user.name 'git'
  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

3 で実行された bundle update で、 Gemfile.lock に差分があれば PR を作成するだけ。

5. create_pull_request.rb

script/circleci/create_pull_request.rb を作成。

# frozen_string_literal: true
require 'octokit'

client = Octokit::Client.new(access_token: 'GitHub Personal access tokens')
client.create_pull_request(
  'repo-team/repo-rails',
  'master',
  ENV['BRANCH'],
  'Bundle update',
  ''
)

これで PR が作成される。

ここまでで bundle exec rake bundle_update_pull_request:check で Bundle update の PR が自動で作成されるようになった。

6. Heroku Scheduler で定期実行

ここでは Heroku を使っているけど、定期実行できればなんでも良い。

f:id:star__hoshi:20171126030819p:plain

動かしてみる

こんな感じで GitHub に毎日 PR がくるようになります。

f:id:star__hoshi:20171126031005p:plain

これを人間が目視で確認して問題なければマージしましょう。

しかし、目視といっても version が変わったことしかわからないので、これを実際に運用するにはテストがしっかりと書かれていて、そのテストが壊れてないことを確認したらマージするようにしないとダメですね。