code_owners 1.0.4 → 1.0.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA256:
3
- metadata.gz: f5e06c6fbdfd8cead1030d3d76b302885321095972e7fcab54d1ba887f1fceb0
4
- data.tar.gz: 54438d6312527c987d98f181f6242777badf72dad7b7561c39d3bc491dae5f2b
2
+ SHA1:
3
+ metadata.gz: 0c170835202aa29d1d863ac544a039dfa2aaca9b
4
+ data.tar.gz: 732f6a87815efa4092ea1404b55180d9bde78f39
5
5
  SHA512:
6
- metadata.gz: 05ba4fbc4d3c3613b458b30a7370c832622ffc1ffee7cb4f2cefb408277a167cde5c5204a2fa53f2584b3a62c01abddce662a1ada86d22c30c97612fd53359ab
7
- data.tar.gz: dfbd672e42dcd6944cfd3a9f246a8fd4e813cf77eeedf2b3bba7c1d38e58919335833c5ca49b1c8a45a61cec14da580b2fcabc4d1f2eeaf587da42f0a60bb692
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)
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.3.1)
12
- rspec (3.7.0)
13
- rspec-core (~> 3.7.0)
14
- rspec-expectations (~> 3.7.0)
15
- rspec-mocks (~> 3.7.0)
16
- rspec-core (3.7.1)
17
- rspec-support (~> 3.7.0)
18
- rspec-expectations (3.7.0)
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.7.0)
21
- rspec-mocks (3.7.0)
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.7.0)
24
- rspec-support (3.7.1)
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.16.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
- it "does not introduce new unowned files" do
29
- unowned_files = CodeOwners.ownerships.select { |f| f[:owner] == "UNOWNED" }
30
- # this number should only decrease, never increase!
31
- assert_equal 12345, unowned_files.count, "Claim ownership of your new files in .github/CODEOWNERS to fix this test!"
32
- end
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 = "http://github.com/jcheatham/#{s.name}"
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"]
@@ -1,3 +1,3 @@
1
1
  module CodeOwners
2
- VERSION = "1.0.4"
2
+ VERSION = "1.0.8"
3
3
  end
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 ownerships
17
- patterns, owners = pattern_owners.transpose
18
+ def log(message)
19
+ puts message
20
+ end
18
21
 
19
- git_owner_info(patterns).map do |line, pattern, file|
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: "UNOWNED" }
26
+ { file: file, owner: NO_OWNER, line: nil, pattern: nil }
22
27
  else
23
- { file: file, owner: owners[line.to_i - 1], line: line, pattern: pattern }
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 = File.join(current_repo_path, ".github/CODEOWNERS")
31
- File.read(codeowner_path).split("\n").map do |line|
32
- line.gsub(/#.*/, '').gsub(/^$/, " @").split(/\s+@/, 2)
33
- end
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
 
@@ -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", "whatever/pattern/thing", "this/is/a/file"],
10
+ ["2", "pat2*", "pat2file"],
10
11
  ["", "", "unowned/file"]
11
12
  ]
12
13
  )
13
14
  expect(CodeOwners.ownerships).to eq(
14
15
  [
15
- { file: "this/is/a/file", owner: "own2", line: "2", pattern: "whatever/pattern/thing" },
16
- { file: "unowned/file", owner: "UNOWNED"}
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
- patterns, owners = CodeOwners.pattern_owners.transpose
25
- expect(owners).to include("jcheatham")
26
- expect(patterns).to include("lib/*")
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
- Dir.chdir("spec") do
31
- patterns, owners = CodeOwners.pattern_owners.transpose
32
- expect(owners).to include("jcheatham")
33
- expect(patterns).to include("lib/*")
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
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
- homepage: http://github.com/jcheatham/code_owners
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.7.3
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: []