Skip to content

Instantly share code, notes, and snippets.

@ykpythemind
Created May 20, 2021 14:22
Show Gist options
  • Save ykpythemind/f51f8a84e96ce6c9bd42d30621071480 to your computer and use it in GitHub Desktop.
Save ykpythemind/f51f8a84e96ce6c9bd42d30621071480 to your computer and use it in GitHub Desktop.
GitHub Action + Rails test example
# テストの並列実行用にspecファイルを分割する
require 'optparse'
options = {}
OptionParser.new do |o|
o.on('--glob=OPT', 'glob') { |v| options[:glob] = v }
o.on('--node-index=OPT', 'node-index') { |v| options[:node_index] = v.to_i }
o.on('--node-count=OPT', 'node-count') { |v| options[:node_count] = v.to_i }
end.parse!(ARGV.dup)
SpecFile = Struct.new(:filepath, :line_count)
files =
Dir
.glob(options[:glob])
.map do |f|
# テストの実行時間の指標として、ファイルの行数を重み付けに使用する
line_count = File.open(f, 'r').each_line.count
# 特別遅いspecファイル. 偏るので雑に重み付け
line_count = 30_000 if f.include?('api/v2/reservations_spec.rb')
SpecFile.new(f, line_count)
end
result = {}
options[:node_count].times { |i| result[i] = [] }
# 行数が多い順に並び替える ( sort_byが不安定なソートなので indexをもちいて安定なソートにする必要がある )
sorted_files = files.sort_by.with_index { |f, i| [f.line_count, i] }.reverse
sorted_files.each do |file|
# 行数が多いファイルから順番に, 現時点で一番軽い箱 (result[min_node_index]) に詰めていく (貪欲法)
min_node = result.values.sort_by.with_index { |f, i| [f.sum(&:line_count), i] }[0]
min_node_index = result.find { |_k, v| v == min_node }[0]
result[min_node_index] << file
end
# result objectはコードの同一ならば常に同じ結果になる (matrix buildされるので同じ結果を返さないと壊れるので注意)
target_files = result[options[:node_index]].map(&:filepath)
# 実行ごとにファイルの順序がランダムになるようにする. https://shime.sh/til/running-parallel-rails-tests-on-github-actions
puts target_files.shuffle(random: Random.new(ENV['GITHUB_RUN_ID']&.to_i || 1)).join(' ')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment