tachikoma (5.2.0)のコードリーディングをしました。
tachikomaはrakeタスクを起動して使います。bundle updateをしたい場合はbundle exec rake tachikoma:run_bundler
を実行します。今回はrun_bundlerのパターンを追っていきます。
Rakeタスクは以下のように定義されています。いずれもTachikoma::Application.runを呼び出します。
require 'tachikoma/application'
namespace :tachikoma do
desc 'run tachikoma with bundler'
task :run_bundler do
Tachikoma::Application.run 'bundler'
end
desc 'run tachikoma with carton'
task :run_carton do
Tachikoma::Application.run 'carton'
end
# ...
Tachikoma::Application.runは#runを呼び出します。strategyのメソッド以外は共通で#load, #fetch #pull_requestを呼び出します。
module Tachikoma
# Main logic of Tachikoma
class Application
include FileUtils
def self.run(strategy)
new.run(strategy)
end
def run(strategy)
load
fetch
send(strategy) if respond_to?(strategy)
pull_request
end
#loadでは設定ファイルのロードとインスタンス変数への設定を行います。base_config_pathはgem自体に存在するデフォルト設定ファイルのパス、user_config_pathはプロジェクト全体の設定ファイルのパス、each_config_pathはプロジェクトごとに固有の設定ファイルのパスです。@build_forがプロジェクト名を表しています。
def load
@build_for = ENV['BUILD_FOR']
@github_token = ENV[github_token_key(@build_for)]
base_config_path = File.join(Tachikoma.original_data_path, 'default.yaml')
base_config = YAML.safe_load_file(base_config_path) || {}
user_config_path = File.join(Tachikoma.data_path, '__user_config__.yaml')
user_config = YAML.safe_load_file(user_config_path) if File.exist?(user_config_path)
user_config ||= {}
each_config_path = File.join(Tachikoma.data_path, "#{@build_for}.yaml")
each_config = YAML.safe_load_file(each_config_path) if File.exist?(each_config_path)
unless each_config
fail %(Something wrong, BUILD_FOR: #{@build_for}, your config_path: #{each_config_path})
end
@configure = base_config.merge(user_config).merge(each_config)
@commiter_name = @configure['commiter_name']
@commiter_email = @configure['commiter_email']
# ...
#fetchはgit cloneを使ってリモートリポジトリからソースコードを取得します。
def fetch
clean
sh(*([
'git', 'clone',
*@depth_option,
@authorized_base_url,
"#{Tachikoma.repos_path}/#{@build_for}"
].compact))
end
#bundlerでは以下の処理を行います。
- git configでユーザ名・メールアドレスを設定
- bundle update用のブランチを生成
- bundle update実行
- Gemfile.lockファイルをgit add
- git commit => git push
def bundler
Dir.chdir("#{Tachikoma.repos_path}/#{@build_for}") do
Bundler.with_clean_env do
sh(*['ruby', '-i', '-pe', '$_.gsub! /^ruby/, "#ruby"', 'Gemfile'])
sh(*['git', 'config', 'user.name', @commiter_name])
sh(*['git', 'config', 'user.email', @commiter_email])
sh(*['git', 'checkout', '-b', "tachikoma/update-#{@readable_time}", @base_remote_branch])
if File.exist?('Gemfile')
@bundler_key_file = 'Gemfile'
@bundler_lock_file = 'Gemfile.lock'
elsif File.exist?('gems.rb')
@bundler_key_file = 'gems.rb'
@bundler_lock_file = 'gems.locked'
else
@bundler_key_file = 'Gemfile'
@bundler_lock_file = 'Gemfile.lock'
end
sh(*([
'bundle',
'--gemfile', @bundler_key_file,
'--no-deployment',
'--without', 'nothing',
'--path', 'vendor/bundle',
@parallel_option
].compact))
sh(*%w(bundle update))
if @bundler_restore_bundled_with
# restore_bundled_with
lock_file_contents = File.read(@bundler_lock_file)
lock_file = RestoreBundledWith::Lock.restore(
lock_file_contents, @bundler_lock_file)
File.write(@bundler_lock_file, lock_file.body)
end
sh(*['git', 'add', @bundler_lock_file])
sh(*['git', 'commit', '-m', "Bundle update #{@readable_time}"]) do
# ignore exitstatus
end
sh(*['git', 'push', @authorized_compare_url, "tachikoma/update-#{@readable_time}"])
end
end
end
最後に#pull_requestでOctokit経由でプルリクエストを作成します。
def pull_request
@client = Octokit::Client.new access_token: @github_token
@client.create_pull_request(
@pull_request_url,
@pull_request_base,
@pull_request_head,
@pull_request_title,
@pull_request_body
)
rescue Octokit::UnprocessableEntity
end