Skip to content

Commit 155f787

Browse files
author
Alex Evanczuk
authored
Add bin/codeownership for_file CLI to power extension (#3)
* renest test * wip * Add CLI for getting ownership for a file * bump version * type check
1 parent 566d3be commit 155f787

File tree

5 files changed

+152
-20
lines changed

5 files changed

+152
-20
lines changed

Gemfile.lock

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
PATH
22
remote: .
33
specs:
4-
code_ownership (1.23.0)
4+
code_ownership (1.24.0)
55
bigrails-teams
66
parse_packwerk
77
sorbet-runtime
@@ -10,7 +10,7 @@ GEM
1010
remote: https://rubygems.org/
1111
specs:
1212
ast (2.4.2)
13-
bigrails-teams (0.1.0)
13+
bigrails-teams (0.1.1)
1414
sorbet-runtime
1515
coderay (1.1.3)
1616
diff-lcs (1.4.4)

code_ownership.gemspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Gem::Specification.new do |spec|
22
spec.name = "code_ownership"
3-
spec.version = '1.23.0'
3+
spec.version = '1.24.0'
44
spec.authors = ['Gusto Engineers']
55
spec.email = ['[email protected]']
66
spec.summary = 'A gem to help engineering teams declare ownership of code'

lib/code_ownership/cli.rb

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@
66
module CodeOwnership
77
class Cli
88
def self.run!(argv)
9-
# Someday we might support other subcommands. When we do that, we can call
10-
# argv.shift to get the first argument and check if it's a given subcommand.
119
command = argv.shift
1210
if command == 'validate'
1311
validate!(argv)
12+
elsif command == 'for_file'
13+
for_file(argv)
1414
end
1515
end
1616

@@ -55,6 +55,55 @@ def self.validate!(argv)
5555
)
5656
end
5757

58+
# For now, this just returns team ownership
59+
# Later, this could also return code ownership errors about that file.
60+
def self.for_file(argv)
61+
options = {}
62+
63+
# Long-term, we probably want to use something like `thor` so we don't have to implement logic
64+
# like this. In the short-term, this is a simple way for us to use the built-in OptionParser
65+
# while having an ergonomic CLI.
66+
files = argv.select { |arg| !arg.start_with?('--') }
67+
68+
parser = OptionParser.new do |opts|
69+
opts.banner = 'Usage: bin/codeownership for_file [options]'
70+
71+
opts.on('--json', 'Output as JSON') do
72+
options[:json] = true
73+
end
74+
75+
opts.on('--help', 'Shows this prompt') do
76+
puts opts
77+
exit
78+
end
79+
end
80+
args = parser.order!(argv) {}
81+
parser.parse!(args)
82+
83+
if files.count != 1
84+
raise "Please pass in one file. Use `bin/codeownership for_file --help` for more info"
85+
end
86+
87+
team = CodeOwnership.for_file(files.first)
88+
89+
team_name = team&.name || "Unowned"
90+
team_yml = team&.config_yml || "Unowned"
91+
92+
if options[:json]
93+
json = {
94+
team_name: team_name,
95+
team_yml: team_yml,
96+
}
97+
98+
puts json.to_json
99+
else
100+
puts <<~MSG
101+
Team: #{team_name}
102+
Team YML: #{team_yml}
103+
MSG
104+
end
105+
end
106+
58107
private_class_method :validate!
59108
end
60109
end

sorbet/rbi/manual.rbi

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
class Hash
2+
def to_json; end
3+
end
Lines changed: 95 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,106 @@
11
RSpec.describe CodeOwnership::Cli do
22
subject { CodeOwnership::Cli.run!(argv) }
33

4-
let(:argv) { ['validate'] }
4+
describe 'validate' do
5+
let(:argv) { ['validate'] }
56

6-
before do
7-
write_file('config/code_ownership.yml', <<~YML)
8-
owned_globs:
9-
- 'app/**/*.rb'
10-
YML
7+
before do
8+
write_file('config/code_ownership.yml', <<~YML)
9+
owned_globs:
10+
- 'app/**/*.rb'
11+
YML
12+
13+
write_file('app/services/my_file.rb')
14+
write_file('frontend/javascripts/my_file.jsx')
15+
end
16+
17+
context 'when run without arguments' do
18+
it 'runs validations with the right defaults' do
19+
expect(CodeOwnership).to receive(:validate!) do |args| # rubocop:disable RSpec/MessageSpies
20+
expect(args[:autocorrect]).to eq true
21+
expect(args[:stage_changes]).to eq true
22+
expect(args[:files]).to match_array(['app/services/my_file.rb'])
23+
end
24+
subject
25+
end
26+
end
1127

12-
write_file('app/services/my_file.rb')
13-
write_file('frontend/javascripts/my_file.jsx')
1428
end
1529

16-
context 'when run without arguments' do
17-
it 'runs validations with the right defaults' do
18-
expect(CodeOwnership).to receive(:validate!) do |args| # rubocop:disable RSpec/MessageSpies
19-
expect(args[:autocorrect]).to eq true
20-
expect(args[:stage_changes]).to eq true
21-
expect(args[:files]).to match_array(['app/services/my_file.rb'])
30+
describe 'for_file' do
31+
before do
32+
write_file('config/code_ownership.yml', <<~YML)
33+
owned_globs:
34+
- 'app/**/*.rb'
35+
YML
36+
37+
write_file('app/services/my_file.rb')
38+
write_file('config/teams/my_team.yml', <<~YML)
39+
name: My Team
40+
owned_globs:
41+
- 'app/**/*.rb'
42+
YML
43+
end
44+
45+
context 'when run with no flags' do
46+
context 'when run with one file' do
47+
let(:argv) { ['for_file', 'app/services/my_file.rb'] }
48+
49+
it 'outputs the team info in human readable format' do
50+
expect(CodeOwnership::Cli).to receive(:puts).with(<<~MSG)
51+
Team: My Team
52+
Team YML: config/teams/my_team.yml
53+
MSG
54+
subject
55+
end
56+
end
57+
58+
context 'when run with no files' do
59+
let(:argv) { ['for_file'] }
60+
61+
it 'outputs the team info in human readable format' do
62+
expect { subject }.to raise_error "Please pass in one file. Use `bin/codeownership for_file --help` for more info"
63+
end
64+
end
65+
66+
context 'when run with multiple files' do
67+
let(:argv) { ['for_file', 'app/services/my_file.rb', 'app/services/my_file2.rb'] }
68+
69+
it 'outputs the team info in human readable format' do
70+
expect { subject }.to raise_error "Please pass in one file. Use `bin/codeownership for_file --help` for more info"
71+
end
72+
end
73+
end
74+
75+
context 'when run with --json' do
76+
let(:argv) { ['for_file', '--json', 'app/services/my_file.rb'] }
77+
78+
context 'when run with one file' do
79+
it 'outputs JSONified information to the console' do
80+
json = {
81+
team_name: 'My Team',
82+
team_yml: 'config/teams/my_team.yml'
83+
}
84+
expect(CodeOwnership::Cli).to receive(:puts).with(json.to_json)
85+
subject
86+
end
87+
end
88+
89+
context 'when run with no files' do
90+
let(:argv) { ['for_file', '--json'] }
91+
92+
it 'outputs the team info in human readable format' do
93+
expect { subject }.to raise_error "Please pass in one file. Use `bin/codeownership for_file --help` for more info"
94+
end
95+
end
96+
97+
context 'when run with multiple files' do
98+
let(:argv) { ['for_file', 'app/services/my_file.rb', 'app/services/my_file2.rb'] }
99+
100+
it 'outputs the team info in human readable format' do
101+
expect { subject }.to raise_error "Please pass in one file. Use `bin/codeownership for_file --help` for more info"
102+
end
22103
end
23-
subject
24104
end
25105
end
26106
end

0 commit comments

Comments
 (0)