Module: CodeOwners
- Defined in:
- lib/code_owners.rb,
lib/code_owners/version.rb
Constant Summary collapse
- NO_OWNER =
'UNOWNED'- CODEOWNER_PATTERN =
/(.*?)\s+((?:[^\s]*@[^\s]+\s*)+)/- POTENTIAL_LOCATIONS =
["CODEOWNERS", "docs/CODEOWNERS", ".github/CODEOWNERS"]
- VERSION =
"2.0.0"
Class Method Summary collapse
-
.file_ownerships(opts = {}) ⇒ Object
helper function to create the lookup for when we have a file and want to find its owner.
- .git_owner_info(patterns) ⇒ Object
- .log(message, opts = {}) ⇒ Object
-
.ownerships(opts = {}) ⇒ Object
this maps the collection of ownership patterns and owners to actual files.
-
.ownerships_by_gitignore(patterns, opts = {}) ⇒ Object
gitignore approach.
-
.ownerships_by_ruby(patterns, files, opts = {}) ⇒ Object
ruby approach.
-
.pattern_owners(codeowner_data, opts = {}) ⇒ Object
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.
-
.raw_git_owner_info(patterns) ⇒ Object
IN: an array of gitignore* check-ignore compliant patterns OUT: a check-ignore formatted string for each file in the repo.
Class Method Details
.file_ownerships(opts = {}) ⇒ Object
helper function to create the lookup for when we have a file and want to find its owner
14 15 16 |
# File 'lib/code_owners.rb', line 14 def file_ownerships(opts = {}) Hash[ ownerships(opts).map { |o| [o[:file], o] } ] end |
.git_owner_info(patterns) ⇒ Object
49 50 51 52 53 54 |
# File 'lib/code_owners.rb', line 49 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, opts = {}) ⇒ Object
130 131 132 |
# File 'lib/code_owners.rb', line 130 def log(, opts = {}) puts if opts[:log] end |
.ownerships(opts = {}) ⇒ Object
this maps the collection of ownership patterns and owners to actual files
19 20 21 22 23 24 25 26 27 28 |
# File 'lib/code_owners.rb', line 19 def ownerships(opts = {}) log("Calculating ownerships for #{opts.inspect}", opts) patterns = pattern_owners(codeowners_data(opts), opts) if opts[:no_git] files = files_to_own(opts) ownerships_by_ruby(patterns, files, opts) else ownerships_by_gitignore(patterns, opts) end end |
.ownerships_by_gitignore(patterns, opts = {}) ⇒ Object
gitignore approach
34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
# File 'lib/code_owners.rb', line 34 def ownerships_by_gitignore(patterns, opts = {}) 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 |
.ownerships_by_ruby(patterns, files, opts = {}) ⇒ Object
ruby approach
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
# File 'lib/code_owners.rb', line 81 def ownerships_by_ruby(patterns, files, opts = {}) ownerships = files.map { |f| { file: f, owner: NO_OWNER, line: nil, pattern: nil } } patterns.each_with_index do |(pattern, owner), i| next if pattern == "" pattern = pattern.gsub(/\/\*$/, "/**") spec_pattern = PathSpec::GitIgnoreSpec.new(pattern) ownerships.each do |o| next unless spec_pattern.match(o[:file]) o[:owner] = owner o[:line] = i+1 o[:pattern] = pattern end end ownerships end |
.pattern_owners(codeowner_data, opts = {}) ⇒ Object
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
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
# File 'lib/code_owners.rb', line 106 def pattern_owners(codeowner_data, opts = {}) patterns = [] codeowner_data.split("\n").each_with_index do |line, i| stripped_line = line.strip if stripped_line == "" || stripped_line.start_with?("#") patterns << ['', ''] # Comment / empty line elsif stripped_line.start_with?("!") # unsupported per github spec log("Parse error line #{(i+1).to_s}: \"#{line}\"", opts) patterns << ['', ''] elsif stripped_line.match(CODEOWNER_PATTERN) patterns << [$1, $2] else log("Parse error line #{(i+1).to_s}: \"#{line}\"", opts) patterns << ['', ''] end end patterns end |
.raw_git_owner_info(patterns) ⇒ Object
IN: an array of gitignore* check-ignore compliant patterns OUT: a check-ignore formatted string for each file in the repo
sadly you can’t tell ls-files to ignore tracked files via an arbitrary pattern file so we jump through some hacky git-fu hoops
-c “core.quotepath=off” ls-files -z # prevent quoting the path and null-terminate each line to assist with matching stuff with spaces -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
69 70 71 72 73 74 75 |
# File 'lib/code_owners.rb', line 69 def raw_git_owner_info(patterns) Tempfile.open('codeowner_patterns') do |file| file.write(patterns.join("\n")) file.rewind `cd #{current_repo_path} && git -c \"core.quotepath=off\" ls-files -z | xargs -0 -- git -c \"core.quotepath=off\" -c \"core.excludesfile=#{file.path}\" check-ignore --no-index -v -n` end end |