codeowners-checker 1.0.0
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 +7 -0
- data/.gitignore +12 -0
- data/.projections.json +4 -0
- data/.rspec +3 -0
- data/.rubocop.yml +28 -0
- data/.travis.yml +5 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +78 -0
- data/Guardfile +39 -0
- data/LICENSE.txt +21 -0
- data/README.md +171 -0
- data/Rakefile +10 -0
- data/bin/codeowners-checker +8 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/codeowners-checker.gemspec +34 -0
- data/demos/missing_reference.svg +1 -0
- data/demos/useless_pattern.svg +1 -0
- data/lib/codeowners/checker.rb +123 -0
- data/lib/codeowners/checker/array.rb +15 -0
- data/lib/codeowners/checker/code_owners.rb +54 -0
- data/lib/codeowners/checker/file_as_array.rb +29 -0
- data/lib/codeowners/checker/group.rb +148 -0
- data/lib/codeowners/checker/group/comment.rb +32 -0
- data/lib/codeowners/checker/group/empty.rb +16 -0
- data/lib/codeowners/checker/group/group_begin_comment.rb +17 -0
- data/lib/codeowners/checker/group/group_end_comment.rb +17 -0
- data/lib/codeowners/checker/group/line.rb +83 -0
- data/lib/codeowners/checker/group/pattern.rb +46 -0
- data/lib/codeowners/checker/group/unrecognized_line.rb +13 -0
- data/lib/codeowners/checker/line_grouper.rb +106 -0
- data/lib/codeowners/checker/version.rb +7 -0
- data/lib/codeowners/cli/base.rb +34 -0
- data/lib/codeowners/cli/check.rb +8 -0
- data/lib/codeowners/cli/config.rb +24 -0
- data/lib/codeowners/cli/filter.rb +63 -0
- data/lib/codeowners/cli/main.rb +183 -0
- data/lib/codeowners/config.rb +48 -0
- metadata +239 -0
@@ -0,0 +1,183 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'fuzzy_match'
|
4
|
+
|
5
|
+
require_relative '../checker'
|
6
|
+
require_relative 'base'
|
7
|
+
require_relative 'config'
|
8
|
+
require_relative 'filter'
|
9
|
+
|
10
|
+
module Codeowners
|
11
|
+
module Cli
|
12
|
+
# Command Line Interface used by bin/codeowners-checker.
|
13
|
+
class Main < Base
|
14
|
+
LABEL = { missing_ref: 'Missing references', useless_pattern: 'No files matching with the pattern' }.freeze
|
15
|
+
option :from, default: 'origin/master'
|
16
|
+
option :to, default: 'HEAD'
|
17
|
+
option :interactive, default: true, type: :boolean, aliases: '-i'
|
18
|
+
desc 'check REPO', 'Checks .github/CODEOWNERS consistency'
|
19
|
+
# for pre-commit: --from HEAD --to index
|
20
|
+
def check(repo = '.')
|
21
|
+
@codeowners_changed = false
|
22
|
+
@repo = repo
|
23
|
+
setup_checker
|
24
|
+
@checker.check!
|
25
|
+
write_codeowners if @codeowners_changed
|
26
|
+
@checker.commit_changes! if options[:interactive] && yes?('Commit changes?')
|
27
|
+
end
|
28
|
+
|
29
|
+
desc 'filter <by-owner>', 'List owners of changed files'
|
30
|
+
subcommand 'filter', Codeowners::Cli::Filter
|
31
|
+
|
32
|
+
desc 'config', 'Checks config is consistent or configure it'
|
33
|
+
subcommand 'config', Codeowners::Cli::Config
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def setup_checker
|
38
|
+
to = options[:to] != 'index' ? options[:to] : nil
|
39
|
+
@checker = Codeowners::Checker.new(@repo, options[:from], to)
|
40
|
+
@checker.when_useless_pattern = method(:suggest_fix_for)
|
41
|
+
@checker.when_new_file = method(:suggest_add_to_codeowners)
|
42
|
+
@checker.transformers << method(:unrecognized_line)
|
43
|
+
end
|
44
|
+
|
45
|
+
def write_codeowners
|
46
|
+
@checker.codeowners.persist!
|
47
|
+
end
|
48
|
+
|
49
|
+
def suggest_add_to_codeowners(file)
|
50
|
+
return unless yes?("File added: #{file.inspect}. Add owner to the CODEOWNERS file?")
|
51
|
+
|
52
|
+
owner = ask('File owner(s): ')
|
53
|
+
new_line = create_new_pattern(file, owner)
|
54
|
+
|
55
|
+
unless new_line.pattern?
|
56
|
+
puts "#{owner.inspect} is not a valid owner name."
|
57
|
+
return
|
58
|
+
end
|
59
|
+
|
60
|
+
subgroups = @checker.main_group.subgroups_owned_by(new_line.owner)
|
61
|
+
add_pattern(new_line, subgroups)
|
62
|
+
|
63
|
+
@codeowners_changed = true
|
64
|
+
end
|
65
|
+
|
66
|
+
def create_new_pattern(file, owner)
|
67
|
+
line = "#{file} #{owner}"
|
68
|
+
Codeowners::Checker::Group::Line.build(line)
|
69
|
+
end
|
70
|
+
|
71
|
+
def add_pattern(pattern, subgroups)
|
72
|
+
unless subgroups.empty?
|
73
|
+
return if insert_pattern_into_subgroup(pattern, subgroups) == true
|
74
|
+
end
|
75
|
+
|
76
|
+
@checker.main_group.add(pattern) if yes?('Add to the end of the CODEOWNERS file?')
|
77
|
+
end
|
78
|
+
|
79
|
+
def insert_pattern_into_subgroup(pattern, subgroups)
|
80
|
+
subgroup = suggest_subgroups_for_pattern(subgroups).to_i - 1
|
81
|
+
return unless subgroup >= 0 && subgroup < subgroups.length
|
82
|
+
|
83
|
+
subgroups[subgroup].insert(pattern)
|
84
|
+
true
|
85
|
+
end
|
86
|
+
|
87
|
+
def suggest_subgroups_for_pattern(subgroups)
|
88
|
+
puts 'Possible groups to which the pattern belongs: '
|
89
|
+
subgroups.each_with_index { |group, i| puts "#{i + 1} - #{group.title}" }
|
90
|
+
ask('Choose group: ')
|
91
|
+
end
|
92
|
+
|
93
|
+
def suggest_fix_for(line)
|
94
|
+
search = FuzzyMatch.new(line.suggest_files_for_pattern)
|
95
|
+
suggestion = search.find(line.pattern)
|
96
|
+
|
97
|
+
puts "Pattern #{line.pattern.inspect} doesn't match."
|
98
|
+
|
99
|
+
# TODO: Handle duplicate patterns.
|
100
|
+
if suggestion
|
101
|
+
apply_suggestion(line, suggestion)
|
102
|
+
else
|
103
|
+
pattern_fix(line)
|
104
|
+
end
|
105
|
+
|
106
|
+
@codeowners_changed = true
|
107
|
+
end
|
108
|
+
|
109
|
+
def apply_suggestion(line, suggestion)
|
110
|
+
case make_suggestion(suggestion)
|
111
|
+
when 'i' then nil
|
112
|
+
when 'y'
|
113
|
+
line.pattern = suggestion
|
114
|
+
when 'e'
|
115
|
+
pattern_change(line)
|
116
|
+
when 'd'
|
117
|
+
line.remove!
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def make_suggestion(suggestion)
|
122
|
+
ask(<<~QUESTION, limited_to: %w[y i e d])
|
123
|
+
Replace with: #{suggestion.inspect}?
|
124
|
+
(y) yes
|
125
|
+
(i) ignore
|
126
|
+
(e) edit the pattern
|
127
|
+
(d) delete the pattern
|
128
|
+
QUESTION
|
129
|
+
end
|
130
|
+
|
131
|
+
def pattern_fix(line)
|
132
|
+
case pattern_suggest_fixing
|
133
|
+
when 'e' then pattern_change(line)
|
134
|
+
when 'i' then nil
|
135
|
+
when 'd' then line.remove!
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def pattern_suggest_fixing
|
140
|
+
ask(<<~QUESTION, limited_to: %w[i e d])
|
141
|
+
(e) edit the pattern
|
142
|
+
(d) delete the pattern
|
143
|
+
(i) ignore
|
144
|
+
QUESTION
|
145
|
+
end
|
146
|
+
|
147
|
+
def pattern_change(line)
|
148
|
+
new_pattern = ask("Replace pattern #{line.pattern.inspect} with: ")
|
149
|
+
return if new_pattern.empty?
|
150
|
+
|
151
|
+
line.pattern = new_pattern
|
152
|
+
end
|
153
|
+
|
154
|
+
def unrecognized_line(line)
|
155
|
+
return line unless line.is_a?(Codeowners::Checker::Group::UnrecognizedLine)
|
156
|
+
|
157
|
+
case unrecognized_line_suggest_fixing(line)
|
158
|
+
when 'i' then line
|
159
|
+
when 'y' then unrecognized_line_new_line
|
160
|
+
when 'd' then nil
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def unrecognized_line_suggest_fixing(line)
|
165
|
+
ask(<<~QUESTION, limited_to: %w[y i d])
|
166
|
+
#{line.to_s.inspect} is in unrecognized format. Would you like to edit?
|
167
|
+
(y) yes
|
168
|
+
(i) ignore
|
169
|
+
(d) delete the line
|
170
|
+
QUESTION
|
171
|
+
end
|
172
|
+
|
173
|
+
def unrecognized_line_new_line
|
174
|
+
line = nil
|
175
|
+
begin
|
176
|
+
new_line_string = ask('New line: ')
|
177
|
+
line = Codeowners::Checker::Group::Line.build(new_line_string)
|
178
|
+
end while line.is_a?(Codeowners::Checker::Group::UnrecognizedLine)
|
179
|
+
line
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Git::Lib.class_eval do
|
4
|
+
def config_set(name, value)
|
5
|
+
command('config', [name, value])
|
6
|
+
rescue Git::GitExecuteError
|
7
|
+
command('config', ['--add', name, value])
|
8
|
+
end
|
9
|
+
|
10
|
+
def config_get(name)
|
11
|
+
do_get = proc do |_path|
|
12
|
+
command('config', ['--get', name])
|
13
|
+
end
|
14
|
+
|
15
|
+
if @git_dir
|
16
|
+
Dir.chdir(@git_dir, &do_get)
|
17
|
+
else
|
18
|
+
do_get.call
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
module Codeowners
|
24
|
+
class AnonymousGit
|
25
|
+
include Git
|
26
|
+
end
|
27
|
+
|
28
|
+
# Connfigure and manage the git config file.
|
29
|
+
class Config
|
30
|
+
def initialize(git = AnonymousGit.new)
|
31
|
+
@git = git
|
32
|
+
end
|
33
|
+
|
34
|
+
def default_owner
|
35
|
+
@git.config('user.owner')
|
36
|
+
end
|
37
|
+
|
38
|
+
def default_owner=(name)
|
39
|
+
@git.config('user.owner', name)
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_h
|
43
|
+
{
|
44
|
+
default_owner: default_owner
|
45
|
+
}
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
metadata
ADDED
@@ -0,0 +1,239 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: codeowners-checker
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jônatas Davi Paganini
|
8
|
+
- Eva Kadlecová
|
9
|
+
- Michal Papis
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2019-03-01 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: fuzzy_match
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
requirements:
|
19
|
+
- - "~>"
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '2.1'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
requirements:
|
26
|
+
- - "~>"
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
version: '2.1'
|
29
|
+
- !ruby/object:Gem::Dependency
|
30
|
+
name: git
|
31
|
+
requirement: !ruby/object:Gem::Requirement
|
32
|
+
requirements:
|
33
|
+
- - "~>"
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: '1.5'
|
36
|
+
type: :runtime
|
37
|
+
prerelease: false
|
38
|
+
version_requirements: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - "~>"
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '1.5'
|
43
|
+
- !ruby/object:Gem::Dependency
|
44
|
+
name: thor
|
45
|
+
requirement: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - "~>"
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: 0.20.3
|
50
|
+
type: :runtime
|
51
|
+
prerelease: false
|
52
|
+
version_requirements: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - "~>"
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: 0.20.3
|
57
|
+
- !ruby/object:Gem::Dependency
|
58
|
+
name: bundler
|
59
|
+
requirement: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - "~>"
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: '1.16'
|
64
|
+
type: :development
|
65
|
+
prerelease: false
|
66
|
+
version_requirements: !ruby/object:Gem::Requirement
|
67
|
+
requirements:
|
68
|
+
- - "~>"
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '1.16'
|
71
|
+
- !ruby/object:Gem::Dependency
|
72
|
+
name: pry
|
73
|
+
requirement: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - "~>"
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 0.12.2
|
78
|
+
type: :development
|
79
|
+
prerelease: false
|
80
|
+
version_requirements: !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - "~>"
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: 0.12.2
|
85
|
+
- !ruby/object:Gem::Dependency
|
86
|
+
name: rake
|
87
|
+
requirement: !ruby/object:Gem::Requirement
|
88
|
+
requirements:
|
89
|
+
- - "~>"
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: '10.0'
|
92
|
+
type: :development
|
93
|
+
prerelease: false
|
94
|
+
version_requirements: !ruby/object:Gem::Requirement
|
95
|
+
requirements:
|
96
|
+
- - "~>"
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: '10.0'
|
99
|
+
- !ruby/object:Gem::Dependency
|
100
|
+
name: rb-readline
|
101
|
+
requirement: !ruby/object:Gem::Requirement
|
102
|
+
requirements:
|
103
|
+
- - "~>"
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: 0.5.5
|
106
|
+
type: :development
|
107
|
+
prerelease: false
|
108
|
+
version_requirements: !ruby/object:Gem::Requirement
|
109
|
+
requirements:
|
110
|
+
- - "~>"
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: 0.5.5
|
113
|
+
- !ruby/object:Gem::Dependency
|
114
|
+
name: rspec
|
115
|
+
requirement: !ruby/object:Gem::Requirement
|
116
|
+
requirements:
|
117
|
+
- - "~>"
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: '3.0'
|
120
|
+
type: :development
|
121
|
+
prerelease: false
|
122
|
+
version_requirements: !ruby/object:Gem::Requirement
|
123
|
+
requirements:
|
124
|
+
- - "~>"
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
version: '3.0'
|
127
|
+
- !ruby/object:Gem::Dependency
|
128
|
+
name: rubocop
|
129
|
+
requirement: !ruby/object:Gem::Requirement
|
130
|
+
requirements:
|
131
|
+
- - "~>"
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: 0.61.1
|
134
|
+
type: :development
|
135
|
+
prerelease: false
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
requirements:
|
138
|
+
- - "~>"
|
139
|
+
- !ruby/object:Gem::Version
|
140
|
+
version: 0.61.1
|
141
|
+
- !ruby/object:Gem::Dependency
|
142
|
+
name: rubocop-rspec
|
143
|
+
requirement: !ruby/object:Gem::Requirement
|
144
|
+
requirements:
|
145
|
+
- - "~>"
|
146
|
+
- !ruby/object:Gem::Version
|
147
|
+
version: '1.30'
|
148
|
+
type: :development
|
149
|
+
prerelease: false
|
150
|
+
version_requirements: !ruby/object:Gem::Requirement
|
151
|
+
requirements:
|
152
|
+
- - "~>"
|
153
|
+
- !ruby/object:Gem::Version
|
154
|
+
version: '1.30'
|
155
|
+
- !ruby/object:Gem::Dependency
|
156
|
+
name: simplecov
|
157
|
+
requirement: !ruby/object:Gem::Requirement
|
158
|
+
requirements:
|
159
|
+
- - "~>"
|
160
|
+
- !ruby/object:Gem::Version
|
161
|
+
version: 0.16.1
|
162
|
+
type: :development
|
163
|
+
prerelease: false
|
164
|
+
version_requirements: !ruby/object:Gem::Requirement
|
165
|
+
requirements:
|
166
|
+
- - "~>"
|
167
|
+
- !ruby/object:Gem::Version
|
168
|
+
version: 0.16.1
|
169
|
+
description:
|
170
|
+
email:
|
171
|
+
- open-source@toptal.com
|
172
|
+
executables:
|
173
|
+
- codeowners-checker
|
174
|
+
extensions: []
|
175
|
+
extra_rdoc_files: []
|
176
|
+
files:
|
177
|
+
- ".gitignore"
|
178
|
+
- ".projections.json"
|
179
|
+
- ".rspec"
|
180
|
+
- ".rubocop.yml"
|
181
|
+
- ".travis.yml"
|
182
|
+
- CODE_OF_CONDUCT.md
|
183
|
+
- Gemfile
|
184
|
+
- Gemfile.lock
|
185
|
+
- Guardfile
|
186
|
+
- LICENSE.txt
|
187
|
+
- README.md
|
188
|
+
- Rakefile
|
189
|
+
- bin/codeowners-checker
|
190
|
+
- bin/console
|
191
|
+
- bin/setup
|
192
|
+
- codeowners-checker.gemspec
|
193
|
+
- demos/missing_reference.svg
|
194
|
+
- demos/useless_pattern.svg
|
195
|
+
- lib/codeowners/checker.rb
|
196
|
+
- lib/codeowners/checker/array.rb
|
197
|
+
- lib/codeowners/checker/code_owners.rb
|
198
|
+
- lib/codeowners/checker/file_as_array.rb
|
199
|
+
- lib/codeowners/checker/group.rb
|
200
|
+
- lib/codeowners/checker/group/comment.rb
|
201
|
+
- lib/codeowners/checker/group/empty.rb
|
202
|
+
- lib/codeowners/checker/group/group_begin_comment.rb
|
203
|
+
- lib/codeowners/checker/group/group_end_comment.rb
|
204
|
+
- lib/codeowners/checker/group/line.rb
|
205
|
+
- lib/codeowners/checker/group/pattern.rb
|
206
|
+
- lib/codeowners/checker/group/unrecognized_line.rb
|
207
|
+
- lib/codeowners/checker/line_grouper.rb
|
208
|
+
- lib/codeowners/checker/version.rb
|
209
|
+
- lib/codeowners/cli/base.rb
|
210
|
+
- lib/codeowners/cli/check.rb
|
211
|
+
- lib/codeowners/cli/config.rb
|
212
|
+
- lib/codeowners/cli/filter.rb
|
213
|
+
- lib/codeowners/cli/main.rb
|
214
|
+
- lib/codeowners/config.rb
|
215
|
+
homepage:
|
216
|
+
licenses:
|
217
|
+
- MIT
|
218
|
+
metadata: {}
|
219
|
+
post_install_message:
|
220
|
+
rdoc_options: []
|
221
|
+
require_paths:
|
222
|
+
- lib
|
223
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
224
|
+
requirements:
|
225
|
+
- - ">="
|
226
|
+
- !ruby/object:Gem::Version
|
227
|
+
version: '0'
|
228
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
229
|
+
requirements:
|
230
|
+
- - ">="
|
231
|
+
- !ruby/object:Gem::Version
|
232
|
+
version: '0'
|
233
|
+
requirements: []
|
234
|
+
rubyforge_project:
|
235
|
+
rubygems_version: 2.7.7
|
236
|
+
signing_key:
|
237
|
+
specification_version: 4
|
238
|
+
summary: Check consistency of Github CODEOWNERS and git changes.
|
239
|
+
test_files: []
|