module CodeOwners

Constants

NO_OWNER
VERSION

Public Class Methods

git_owner_info(patterns) click to toggle source
# File lib/code_owners.rb, line 67
def git_owner_info(patterns)
  make_utf8(raw_git_owner_info(patterns)).lines.map do |info|
    _, _exfile, line, pattern, file = info.strip.match(/^(.*):(\d*):(.*)\t(.*)$/).to_a
    [line, pattern, file]
  end
end
log(message) click to toggle source

github's CODEOWNERS rules (help.github.com/articles/about-codeowners/) are allegedly based on the gitignore format. but you can't tell ls-files to ignore tracked files via an arbitrary pattern file so we need to jump through some hacky git-fu hoops

-c “core.excludesfiles=somefile” -> tells git to use this as our gitignore pattern source check-ignore -> debug gitignore / exclude files –no-index -> don't look in the index when checking, can be used to debug why a path became tracked -v -> verbose, outputs details about the matching pattern (if any) for each given pathname -n -> non-matching, shows given paths which don't match any pattern

# File lib/code_owners.rb, line 18
def log(message)
  puts message
end
ownerships() click to toggle source
# File lib/code_owners.rb, line 22
def ownerships
  patterns = pattern_owners
  git_owner_info(patterns.map { |p| p[0] }).map do |line, pattern, file|
    if line.empty?
      { file: file, owner: NO_OWNER, line: nil, pattern: nil }
    else
      {
        file: file,
        owner: patterns.fetch(line.to_i-1)[1],
        line: line,
        pattern: pattern
      }
    end
  end
end
pattern_owners() click to toggle source

read the github file and spit out a slightly formatted list of patterns and their owners Empty/invalid/commented lines are still included in order to preserve line numbering

# File lib/code_owners.rb, line 49
def pattern_owners
  codeowner_path = search_codeowners_file
  patterns = []
  File.read(codeowner_path).split("\n").each_with_index { |line, i|
    path_owner = line.split(/\s+@/, 2)
    if line.match(/^\s*(?:#.*)?$/)
      patterns.push ['', ''] # Comment/empty line
    elsif path_owner.length != 2 || (path_owner[0].empty? && !path_owner[1].empty?)
      log "Parse error line #{(i+1).to_s}: \"#{line}\""
      patterns.push ['', ''] # Invalid line
    else
      path_owner[1] = '@'+path_owner[1]
      patterns.push path_owner
    end
  }
  return patterns
end
raw_git_owner_info(patterns) click to toggle source

expects an array of gitignore compliant patterns generates a check-ignore formatted string for each file in the repo

# File lib/code_owners.rb, line 76
def raw_git_owner_info(patterns)
  Tempfile.open('codeowner_patterns') do |file|
    file.write(patterns.join("\n"))
    file.rewind
    `cd #{current_repo_path} && git ls-files | xargs -- git -c \"core.excludesfile=#{file.path}\" check-ignore --no-index -v -n`
  end
end
search_codeowners_file() click to toggle source
# File lib/code_owners.rb, line 38
def search_codeowners_file
  paths = ["CODEOWNERS", "docs/CODEOWNERS", ".github/CODEOWNERS"]
  for path in paths
    current_file_path = File.join(current_repo_path, path)
    return current_file_path if File.exist?(current_file_path)
  end
  abort("[ERROR] CODEOWNERS file does not exist.")
end

Private Class Methods

current_repo_path() click to toggle source
# File lib/code_owners.rb, line 94
def current_repo_path
  `git rev-parse --show-toplevel`.strip
end
make_utf8(input) click to toggle source
# File lib/code_owners.rb, line 86
def make_utf8(input)
  input.force_encoding(Encoding::UTF_8)
  return input if input.valid_encoding?
  input.encode!(Encoding::UTF_16, invalid: :replace, replace: '�')
  input.encode!(Encoding::UTF_8, Encoding::UTF_16)
  input
end