code_owners 1.0.4 → 1.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/Gemfile.lock +14 -14
- data/README.md +12 -6
- data/bin/code_owners +35 -0
- data/code_owners.gemspec +2 -2
- data/lib/code_owners/version.rb +1 -1
- data/lib/code_owners.rb +41 -10
- data/spec/code_owners_spec.rb +57 -12
- data/spec/files//342/230/203.txt +1 -0
- metadata +8 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 0c170835202aa29d1d863ac544a039dfa2aaca9b
|
4
|
+
data.tar.gz: 732f6a87815efa4092ea1404b55180d9bde78f39
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b205789714617196c0e067504e8beae29dc585a6601a79279475a3a17cc36b249d61e896c6972d8d2fc2b4780e5df406a86c92226ef6013fd6fb262087733ae6
|
7
|
+
data.tar.gz: 4e7766d5ee8b1adf676974e8dce8345ce93d2b4b9e868d4295578d35b5656897621c0dcf00e227377024946e2d7429c695e2bf86539fbd24a5992fdce2c9e4fd
|
data/Gemfile.lock
CHANGED
@@ -1,27 +1,27 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
code_owners (1.0.
|
4
|
+
code_owners (1.0.8)
|
5
5
|
rake
|
6
6
|
|
7
7
|
GEM
|
8
8
|
remote: http://rubygems.org/
|
9
9
|
specs:
|
10
10
|
diff-lcs (1.3)
|
11
|
-
rake (
|
12
|
-
rspec (3.
|
13
|
-
rspec-core (~> 3.
|
14
|
-
rspec-expectations (~> 3.
|
15
|
-
rspec-mocks (~> 3.
|
16
|
-
rspec-core (3.
|
17
|
-
rspec-support (~> 3.
|
18
|
-
rspec-expectations (3.
|
11
|
+
rake (13.0.6)
|
12
|
+
rspec (3.8.0)
|
13
|
+
rspec-core (~> 3.8.0)
|
14
|
+
rspec-expectations (~> 3.8.0)
|
15
|
+
rspec-mocks (~> 3.8.0)
|
16
|
+
rspec-core (3.8.2)
|
17
|
+
rspec-support (~> 3.8.0)
|
18
|
+
rspec-expectations (3.8.4)
|
19
19
|
diff-lcs (>= 1.2.0, < 2.0)
|
20
|
-
rspec-support (~> 3.
|
21
|
-
rspec-mocks (3.
|
20
|
+
rspec-support (~> 3.8.0)
|
21
|
+
rspec-mocks (3.8.1)
|
22
22
|
diff-lcs (>= 1.2.0, < 2.0)
|
23
|
-
rspec-support (~> 3.
|
24
|
-
rspec-support (3.
|
23
|
+
rspec-support (~> 3.8.0)
|
24
|
+
rspec-support (3.8.2)
|
25
25
|
|
26
26
|
PLATFORMS
|
27
27
|
ruby
|
@@ -31,4 +31,4 @@ DEPENDENCIES
|
|
31
31
|
rspec
|
32
32
|
|
33
33
|
BUNDLED WITH
|
34
|
-
1.
|
34
|
+
1.17.3
|
data/README.md
CHANGED
@@ -5,6 +5,12 @@ Install
|
|
5
5
|
|
6
6
|
gem install code_owners
|
7
7
|
|
8
|
+
Requirements
|
9
|
+
============
|
10
|
+
|
11
|
+
* Ruby
|
12
|
+
* Git
|
13
|
+
|
8
14
|
Usage
|
9
15
|
=====
|
10
16
|
|
@@ -24,12 +30,12 @@ Development
|
|
24
30
|
|
25
31
|
Maybe put it in a cleanliness test, like:
|
26
32
|
|
27
|
-
```
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
+
```ruby
|
34
|
+
it "does not introduce new unowned files" do
|
35
|
+
unowned_files = CodeOwners.ownerships.select { |f| f[:owner] == CodeOwners::NO_OWNER }
|
36
|
+
# this number should only decrease, never increase!
|
37
|
+
assert_equal 12345, unowned_files.count, "Claim ownership of your new files in .github/CODEOWNERS to fix this test!"
|
38
|
+
end
|
33
39
|
```
|
34
40
|
|
35
41
|
Author
|
data/bin/code_owners
CHANGED
@@ -1,10 +1,45 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
+
unless system('git --version > /dev/null')
|
4
|
+
STDERR.puts 'Git does not appear to be installed.'
|
5
|
+
exit 2
|
6
|
+
end
|
7
|
+
|
8
|
+
unless system('git rev-parse --is-inside-work-tree > /dev/null')
|
9
|
+
STDERR.puts 'The current working directory must be a Git repo.'
|
10
|
+
exit 3
|
11
|
+
end
|
12
|
+
|
3
13
|
$LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib')
|
4
14
|
require 'code_owners'
|
15
|
+
require 'code_owners/version'
|
16
|
+
require 'optparse'
|
17
|
+
|
18
|
+
options = {}
|
19
|
+
OptionParser.new do |opts|
|
20
|
+
opts.banner = "usage: code_owners [options]"
|
21
|
+
opts.on('-u', '--unowned', TrueClass, 'Display unowned files only') do |u|
|
22
|
+
options[:unowned] = u
|
23
|
+
end
|
24
|
+
opts.on('-e', '--error-unowned', TrueClass, 'Exit with error status if any files are unowned') do |e|
|
25
|
+
options[:error_unowned] = e
|
26
|
+
end
|
27
|
+
opts.on('-v', '--version', TrueClass, 'Display the version of the gem') do |_|
|
28
|
+
puts "Version: #{CodeOwners::VERSION}"
|
29
|
+
exit 0
|
30
|
+
end
|
31
|
+
end.parse!
|
5
32
|
|
33
|
+
unowned_error = false
|
6
34
|
CodeOwners.ownerships.each do |ownership_status|
|
7
35
|
owner_info = ownership_status[:owner].dup
|
36
|
+
if owner_info != CodeOwners::NO_OWNER
|
37
|
+
next if options[:unowned]
|
38
|
+
else
|
39
|
+
unowned_error ||= options[:error_unowned]
|
40
|
+
end
|
8
41
|
owner_info += " per line #{ownership_status[:line]}, #{ownership_status[:pattern]}" if owner_info != "UNOWNED"
|
9
42
|
puts "#{ownership_status[:file].ljust(100,' ')} #{owner_info}"
|
10
43
|
end
|
44
|
+
|
45
|
+
exit(unowned_error && 1 || 0)
|
data/code_owners.gemspec
CHANGED
@@ -8,10 +8,10 @@ Gem::Specification.new name, CodeOwners::VERSION do |s|
|
|
8
8
|
s.description = "utility gem for .github/CODEOWNERS introspection"
|
9
9
|
s.authors = "Jonathan Cheatham"
|
10
10
|
s.email = "coaxis@gmail.com"
|
11
|
-
s.homepage = "
|
11
|
+
s.homepage = "https://github.com/jcheatham/#{s.name}"
|
12
12
|
s.licenses = "MIT"
|
13
13
|
|
14
|
-
s.files = `git ls-files`.split("\n")
|
14
|
+
s.files = `git -c "core.quotepath=off" ls-files`.split("\n")
|
15
15
|
s.bindir = 'bin'
|
16
16
|
s.test_files = `git ls-files -- test/*`.split("\n")
|
17
17
|
s.require_paths = ["lib"]
|
data/lib/code_owners/version.rb
CHANGED
data/lib/code_owners.rb
CHANGED
@@ -2,7 +2,9 @@ require "code_owners/version"
|
|
2
2
|
require "tempfile"
|
3
3
|
|
4
4
|
module CodeOwners
|
5
|
+
NO_OWNER = 'UNOWNED'
|
5
6
|
class << self
|
7
|
+
|
6
8
|
# github's CODEOWNERS rules (https://help.github.com/articles/about-codeowners/) are allegedly based on the gitignore format.
|
7
9
|
# but you can't tell ls-files to ignore tracked files via an arbitrary pattern file
|
8
10
|
# so we need to jump through some hacky git-fu hoops
|
@@ -13,24 +15,53 @@ module CodeOwners
|
|
13
15
|
# -v -> verbose, outputs details about the matching pattern (if any) for each given pathname
|
14
16
|
# -n -> non-matching, shows given paths which don't match any pattern
|
15
17
|
|
16
|
-
def
|
17
|
-
|
18
|
+
def log(message)
|
19
|
+
puts message
|
20
|
+
end
|
18
21
|
|
19
|
-
|
22
|
+
def ownerships
|
23
|
+
patterns = pattern_owners
|
24
|
+
git_owner_info(patterns.map { |p| p[0] }).map do |line, pattern, file|
|
20
25
|
if line.empty?
|
21
|
-
{ file: file, owner:
|
26
|
+
{ file: file, owner: NO_OWNER, line: nil, pattern: nil }
|
22
27
|
else
|
23
|
-
{
|
28
|
+
{
|
29
|
+
file: file,
|
30
|
+
owner: patterns.fetch(line.to_i-1)[1],
|
31
|
+
line: line,
|
32
|
+
pattern: pattern
|
33
|
+
}
|
24
34
|
end
|
25
35
|
end
|
26
36
|
end
|
27
37
|
|
38
|
+
def search_codeowners_file
|
39
|
+
paths = ["CODEOWNERS", "docs/CODEOWNERS", ".github/CODEOWNERS"]
|
40
|
+
for path in paths
|
41
|
+
current_file_path = File.join(current_repo_path, path)
|
42
|
+
return current_file_path if File.exist?(current_file_path)
|
43
|
+
end
|
44
|
+
abort("[ERROR] CODEOWNERS file does not exist.")
|
45
|
+
end
|
46
|
+
|
28
47
|
# read the github file and spit out a slightly formatted list of patterns and their owners
|
48
|
+
# Empty/invalid/commented lines are still included in order to preserve line numbering
|
29
49
|
def pattern_owners
|
30
|
-
codeowner_path =
|
31
|
-
|
32
|
-
|
33
|
-
|
50
|
+
codeowner_path = search_codeowners_file
|
51
|
+
patterns = []
|
52
|
+
File.read(codeowner_path).split("\n").each_with_index { |line, i|
|
53
|
+
path_owner = line.split(/\s+@/, 2)
|
54
|
+
if line.match(/^\s*(?:#.*)?$/)
|
55
|
+
patterns.push ['', ''] # Comment/empty line
|
56
|
+
elsif path_owner.length != 2 || (path_owner[0].empty? && !path_owner[1].empty?)
|
57
|
+
log "Parse error line #{(i+1).to_s}: \"#{line}\""
|
58
|
+
patterns.push ['', ''] # Invalid line
|
59
|
+
else
|
60
|
+
path_owner[1] = '@'+path_owner[1]
|
61
|
+
patterns.push path_owner
|
62
|
+
end
|
63
|
+
}
|
64
|
+
return patterns
|
34
65
|
end
|
35
66
|
|
36
67
|
def git_owner_info(patterns)
|
@@ -46,7 +77,7 @@ module CodeOwners
|
|
46
77
|
Tempfile.open('codeowner_patterns') do |file|
|
47
78
|
file.write(patterns.join("\n"))
|
48
79
|
file.rewind
|
49
|
-
`cd #{current_repo_path} && git ls-files | xargs -- git -c \"core.excludesfile=#{file.path}\" check-ignore --no-index -v -n`
|
80
|
+
`cd #{current_repo_path} && git -c \"core.quotepath=off\" ls-files | xargs -- git -c \"core.quotepath=off\" -c \"core.excludesfile=#{file.path}\" check-ignore --no-index -v -n`
|
50
81
|
end
|
51
82
|
end
|
52
83
|
|
data/spec/code_owners_spec.rb
CHANGED
@@ -1,38 +1,71 @@
|
|
1
1
|
require 'code_owners'
|
2
|
+
require 'tmpdir'
|
2
3
|
|
3
|
-
RSpec.describe CodeOwners do
|
4
|
+
RSpec.describe CodeOwners do |rspec|
|
4
5
|
describe ".ownerships" do
|
5
6
|
it "assigns owners to things" do
|
6
|
-
expect(CodeOwners).to receive(:pattern_owners).and_return([["pat1", "own1"], ["pat2", "own2"], ["pat3", "own3"]])
|
7
|
+
expect(CodeOwners).to receive(:pattern_owners).and_return([["pat1", "own1"], ["pat2*", "own2"], ["pat3", "own3"]])
|
7
8
|
expect(CodeOwners).to receive(:git_owner_info).and_return(
|
8
9
|
[
|
9
|
-
["2", "
|
10
|
+
["2", "pat2*", "pat2file"],
|
10
11
|
["", "", "unowned/file"]
|
11
12
|
]
|
12
13
|
)
|
13
14
|
expect(CodeOwners.ownerships).to eq(
|
14
15
|
[
|
15
|
-
{ file: "
|
16
|
-
{ file: "unowned/file",
|
16
|
+
{ file: "pat2file", owner: "own2", line: "2", pattern: "pat2*" },
|
17
|
+
{ file: "unowned/file", owner: "UNOWNED", line: nil, pattern: nil }
|
17
18
|
]
|
18
19
|
)
|
19
20
|
end
|
20
21
|
end
|
21
22
|
|
22
23
|
describe ".pattern_owners" do
|
24
|
+
around(:each) do |example|
|
25
|
+
Dir.mktmpdir { |d|
|
26
|
+
@d = d
|
27
|
+
f = File.new(File.join(d, 'CODEOWNERS'), 'w+')
|
28
|
+
f.write <<-CODEOWNERS
|
29
|
+
lib/* @jcheatham
|
30
|
+
some/path/** @someoneelse
|
31
|
+
other/path/* @someoneelse @anotherperson
|
32
|
+
invalid/codeowners/line
|
33
|
+
@AnotherInvalidLine
|
34
|
+
#comment-line (empty line next)
|
35
|
+
|
36
|
+
# another comment line
|
37
|
+
CODEOWNERS
|
38
|
+
f.close
|
39
|
+
example.run
|
40
|
+
}
|
41
|
+
end
|
42
|
+
|
23
43
|
it "returns a list of patterns and owners" do
|
24
|
-
|
25
|
-
expect(
|
26
|
-
|
44
|
+
expect(CodeOwners).to receive(:current_repo_path).and_return(@d)
|
45
|
+
expect(CodeOwners).to receive(:log).twice
|
46
|
+
pattern_owners = CodeOwners.pattern_owners
|
47
|
+
expect(pattern_owners).to include(["other/path/*", "@someoneelse @anotherperson"])
|
27
48
|
end
|
28
49
|
|
29
50
|
it "works when invoked in a repo's subdirectory" do
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
51
|
+
expect(CodeOwners).to receive(:current_repo_path).and_return(@d)
|
52
|
+
expect(CodeOwners).to receive(:log).twice
|
53
|
+
subdir = File.join(@d, 'spec')
|
54
|
+
Dir.mkdir(subdir)
|
55
|
+
Dir.chdir(subdir) do
|
56
|
+
pattern_owners = CodeOwners.pattern_owners
|
57
|
+
expect(pattern_owners).to include(["lib/*", "@jcheatham"])
|
34
58
|
end
|
35
59
|
end
|
60
|
+
|
61
|
+
it "prints validation errors and skips lines that aren't the expected format" do
|
62
|
+
expect(CodeOwners).to receive(:current_repo_path).and_return(@d)
|
63
|
+
expect(CodeOwners).to receive(:log).with("Parse error line 4: \"invalid/codeowners/line \"")
|
64
|
+
expect(CodeOwners).to receive(:log).with("Parse error line 5: \" @AnotherInvalidLine\"")
|
65
|
+
pattern_owners = CodeOwners.pattern_owners
|
66
|
+
expect(pattern_owners).not_to include(["", "@AnotherInvalidLine"])
|
67
|
+
expect(pattern_owners).to include(["", ""])
|
68
|
+
end
|
36
69
|
end
|
37
70
|
|
38
71
|
describe ".git_owner_info" do
|
@@ -54,4 +87,16 @@ RSpec.describe CodeOwners do
|
|
54
87
|
expect(raw_ownership).to match(/^(?:.*:\d*:.*\t.*\n)+$/)
|
55
88
|
end
|
56
89
|
end
|
90
|
+
|
91
|
+
describe "code_owners" do
|
92
|
+
VERSION_REGEX = /Version: \d+\.\d+\.\d+(-[a-z0-9]+)?/i
|
93
|
+
|
94
|
+
it "prints a version number with the short option" do
|
95
|
+
expect(`bin#{File::SEPARATOR}code_owners -v`).to match VERSION_REGEX
|
96
|
+
end
|
97
|
+
|
98
|
+
it "prints a version number with the short option" do
|
99
|
+
expect(`bin#{File::SEPARATOR}code_owners --version`).to match VERSION_REGEX
|
100
|
+
end
|
101
|
+
end
|
57
102
|
end
|
@@ -0,0 +1 @@
|
|
1
|
+
☃☃☃☃☃☃☃☃☃
|
metadata
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: code_owners
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jonathan Cheatham
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
date: 2018-03-29 00:00:00.000000000 Z
|
@@ -57,11 +57,12 @@ files:
|
|
57
57
|
- lib/code_owners.rb
|
58
58
|
- lib/code_owners/version.rb
|
59
59
|
- spec/code_owners_spec.rb
|
60
|
-
|
60
|
+
- spec/files/☃.txt
|
61
|
+
homepage: https://github.com/jcheatham/code_owners
|
61
62
|
licenses:
|
62
63
|
- MIT
|
63
64
|
metadata: {}
|
64
|
-
post_install_message:
|
65
|
+
post_install_message:
|
65
66
|
rdoc_options: []
|
66
67
|
require_paths:
|
67
68
|
- lib
|
@@ -76,9 +77,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
76
77
|
- !ruby/object:Gem::Version
|
77
78
|
version: '0'
|
78
79
|
requirements: []
|
79
|
-
rubyforge_project:
|
80
|
-
rubygems_version: 2.
|
81
|
-
signing_key:
|
80
|
+
rubyforge_project:
|
81
|
+
rubygems_version: 2.5.2.3
|
82
|
+
signing_key:
|
82
83
|
specification_version: 4
|
83
84
|
summary: ".github/CODEOWNERS introspection utility gem"
|
84
85
|
test_files: []
|