Module: CodeOwners

Defined in:
lib/code_owners.rb,
lib/code_owners/version.rb

Constant Summary collapse

NO_OWNER =
'UNOWNED'
VERSION =
"1.0.7"

Class Method Summary collapse

Class Method Details

.git_owner_info(patterns) ⇒ Object



67
68
69
70
71
72
# 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) ⇒ Object

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



18
19
20
# File 'lib/code_owners.rb', line 18

def log(message)
  puts message
end

.ownershipsObject



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 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_ownersObject

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



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# 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) ⇒ Object

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



76
77
78
79
80
81
82
# 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_fileObject



38
39
40
41
42
43
44
45
# 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