LoginSignup
3
3

More than 5 years have passed since last update.

プルリクエストごとに、変更されたファイルの一覧から情報を得る

Last updated at Posted at 2015-04-21

git diff --stat のフォーマットからファイル一覧を得る

$ git diff --stat=256 origin/master..HEAD
 test/lib/populus/test_configuration.rb | 36 ++++++++++++++++++++++++++++++++++++
 test/lib/test_populus.rb               |  7 +++++++
 2 files changed, 43 insertions(+)

git diff --stat のフィールドは、空白で分割すると1番目がファイルパス、4番目が追加削除の様子、になるのでそれを利用できる。

長いファイル名だとtruncateされるので --stat=256 みたいに横幅を明示すると良さそう。

# origin/master..${current SHA1} みたいなのを渡す
@range = ARGV[0]
data = `git diff --stat=256 --no-color #{@range}`
rolenames = data.lines
  .map {|l| l.strip.split(/\s+/) }
  .select {|r| r[3].include? '+' }
  .map {|r| detect_foobar_from_filename(r[0]) }
  .compact
  .flatten

detect_foobar_from_filename で、例えばファイル名からPuppetやChefのロールを検出することができそう。手元では、ロールを導出して最後に計算してgit diffから一番影響を与えるロールを検出し、そのロールだけテストするみたいなことをやった。

パッペット事例のご紹介

以下、思いっきりPuppetの事例だが、みんな Puppet 使ってると思うし、あとディレクトリ構成は 名著 の構成を前提にしたものとしているのでそのまま使えると思う。え、あのディレクトリ構成を守ってないんですか......。

@range = ARGV[0]
data = `git diff --stat=256 --no-color #{@range}`

detect_role_from_filename = lambda {|filename|
  case filename
  when %r<^roles/([_a-z]+)/.*$>
    # ロールのマニフェスト直接変更の場合は10ポイント
    [$1] * 10
  when %r<^spec/([_a-z]+)/.*\.rb$>
    # 関連するspecの変更の場合は5ポイント
    # この辺の傾斜は徐々に調整するといいのでは
    if $1 != 'support' # serverspecサポート用のファイルを除外する
      [$1] * 5
    else
      nil
    end
  when %r<^modules/([_a-z]+)/.*>
    # そのほか、普通のモジュールは
    # 影響度を雑に計算している
    `git grep 'include ::#{$1}' roles/`
      .lines
      .map{|l| l.split('/')[1] }
      .uniq
  else
    nil # detection failed
  end
}

rolenames = data.lines
  .map {|l| l.strip.split(/\s+/) }
  # ファイル削除の場合は無視
  .select {|r| r[3].include? '+' }
  .map {|r| detect_role_from_filename.(r[0]) }
  .compact
  .flatten

# group_by みたいなんを自分でやってる...
stats = rolenames.inject({}) { |dst, record|
  dst[record] ||= 0
  dst[record] += 1
  dst
}

$stderr.puts "Summary: #{stats.inspect}"

# TODO: 同率の場合辞書順...
detected = stats.max_by{|(k, v)| v }

if detected
  $stderr.puts "Detected: role #{detected.inspect}"
  puts detected[0]
else
  $stderr.puts "No role detected. Fall back to testing against <base>"
  puts 'base'
end

# おしまい
exit

以下のように、stdoutに最終的に www みたいなロールを吐き出すようになっていて、シェルスクリプト内部で変数にアサインできる。

$ role=$( script/get_role_by_change.rb origin/master..a123bc45 )
=> stderr: Summary: {"www"=>5, "log"=>3}
=> stderr: Detected: role ["www", 5]
=> `www' is assigned to $role

関係ないとこをテストして厭っぽいとか時間かかりすぎるとか、よくあると思うので、こういうハックをして乗り切るのも一興だと思う。

3
3
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3