michael 0.1.1 → 0.2.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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +4 -1
  3. data/Gemfile.lock +6 -71
  4. data/exe/michael +2 -2
  5. data/lib/michael.rb +1 -0
  6. data/lib/michael/cli.rb +15 -1
  7. data/lib/michael/commands/auth.rb +11 -21
  8. data/lib/michael/commands/repos.rb +39 -2
  9. data/lib/michael/commands/repos/edit.rb +13 -9
  10. data/lib/michael/commands/repos/pull_requests.rb +38 -97
  11. data/lib/michael/constants.rb +7 -0
  12. data/lib/michael/models/pull_request.rb +139 -0
  13. data/lib/michael/models/repository.rb +45 -0
  14. data/lib/michael/models/review.rb +57 -0
  15. data/lib/michael/models/status.rb +43 -0
  16. data/lib/michael/models/user.rb +18 -0
  17. data/lib/michael/services/configuration.rb +44 -0
  18. data/lib/michael/{models/github/octokit_initializer.rb → services/github/initializer.rb} +9 -7
  19. data/lib/michael/services/github/pull_requests.rb +46 -0
  20. data/lib/michael/services/github/token.rb +41 -0
  21. data/lib/michael/services/github/users.rb +16 -0
  22. data/lib/michael/services/repositories.rb +34 -0
  23. data/lib/michael/version.rb +1 -1
  24. data/michael.gemspec +2 -18
  25. metadata +19 -245
  26. data/lib/michael/command.rb +0 -131
  27. data/lib/michael/models/configuration.rb +0 -69
  28. data/lib/michael/models/github/pr_wrapper.rb +0 -84
  29. data/lib/michael/models/github/pull_request.rb +0 -51
  30. data/lib/michael/models/github/review.rb +0 -41
  31. data/lib/michael/models/github/reviewer.rb +0 -17
  32. data/lib/michael/models/github/status.rb +0 -25
  33. data/lib/michael/models/github/team.rb +0 -17
  34. data/lib/michael/models/github/token_validator.rb +0 -30
  35. data/lib/michael/models/github/user.rb +0 -21
  36. data/lib/michael/models/guard.rb +0 -20
  37. data/lib/michael/models/pull_request_formatter.rb +0 -116
  38. data/lib/michael/models/repository_formatter.rb +0 -26
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 147eb2a1f8bb87dd4236aba99b6ff495fbb16a6b09cedf7ecb01edcac8124305
4
- data.tar.gz: 1304c1b36bc36d3066a876dc530f1dfa3dc11c38b8cc31767b77e6a211c3c390
3
+ metadata.gz: e7807c64c30fb214d1532da9a97d6abc4f5173b52bd2f1256aa9a3b4b44c43a8
4
+ data.tar.gz: 84ab17dfada0424db92341ab7911a338716d04cda02c35f2e6779cfb1cbb75bd
5
5
  SHA512:
6
- metadata.gz: 88ce61019ef7992ee575befae3902a2b5c359125ab1bc338a598e64273e27c01a134bc16dd33d4d27a6c944c49e6b0da18e4602e028794d9ba1ccbf4c484f2ca
7
- data.tar.gz: 9f6d158d879cce2ed941f88370d09bcc3d608cb505f1caabba690a0cc5a054215d9b41e72a7fee0f1536cdc200318f0e4e8169a67db2bae7a3980532f1a042ed
6
+ metadata.gz: d89f9e455b2048c657b6e3a152a0ac7d3691af7826eba6df3798065641d75d6cf6d39c368e34f9f5fa4d0961ecea820114279d649547610b34e50d06c6b1fae5
7
+ data.tar.gz: e1a8370c604d5de524384bce470dce5d3ee87bb2e052c2f2b7f98f8ad0e2f04af6698ac24ffc7c28a93fcbd0eca62ddf5f8fcd9f3e07d353e2d04b46308351cb
data/.rubocop.yml CHANGED
@@ -1,2 +1,5 @@
1
1
  Style/Documentation:
2
- Enabled: false
2
+ Enabled: false
3
+
4
+ Metrics/LineLength:
5
+ Max: 120
data/Gemfile.lock CHANGED
@@ -1,42 +1,26 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- michael (0.1.0)
4
+ michael (0.2.0)
5
5
  octokit (~> 4.0)
6
6
  parallel (~> 1.18.0)
7
7
  pastel (~> 0.7.2)
8
8
  ruby-duration (~> 3.2.3)
9
9
  thor (~> 0.20.0)
10
- tty-box (~> 0.4.1)
11
- tty-color (~> 0.5)
12
- tty-command (~> 0.9.0)
13
10
  tty-config (~> 0.3.2)
14
- tty-cursor (~> 0.7)
15
11
  tty-editor (~> 0.5)
16
- tty-file (~> 0.8.0)
17
- tty-font (~> 0.4.0)
18
12
  tty-logger (~> 0.2.0)
19
- tty-markdown (~> 0.6.0)
20
- tty-pager (~> 0.12)
21
- tty-pie (~> 0.3.0)
22
- tty-platform (~> 0.2)
23
- tty-progressbar (~> 0.17)
24
13
  tty-prompt (~> 0.19)
25
- tty-screen (~> 0.7)
26
- tty-spinner (~> 0.9)
27
- tty-table (~> 0.11.0)
28
- tty-tree (~> 0.3)
29
- tty-which (~> 0.4)
30
14
 
31
15
  GEM
32
16
  remote: https://rubygems.org/
33
17
  specs:
34
- activesupport (6.0.0)
18
+ activesupport (6.0.1)
35
19
  concurrent-ruby (~> 1.0, >= 1.0.2)
36
20
  i18n (>= 0.7, < 2)
37
21
  minitest (~> 5.1)
38
22
  tzinfo (~> 1.1)
39
- zeitwerk (~> 2.1, >= 2.1.8)
23
+ zeitwerk (~> 2.2)
40
24
  addressable (2.7.0)
41
25
  public_suffix (>= 2.0.2, < 5.0)
42
26
  coderay (1.1.2)
@@ -50,9 +34,8 @@ GEM
50
34
  concurrent-ruby (~> 1.0)
51
35
  iso8601 (0.12.1)
52
36
  json (2.2.0)
53
- kramdown (1.16.2)
54
37
  method_source (0.9.2)
55
- minitest (5.12.2)
38
+ minitest (5.13.0)
56
39
  multipart-post (2.1.1)
57
40
  necromancer (0.5.0)
58
41
  octokit (4.14.0)
@@ -66,7 +49,6 @@ GEM
66
49
  method_source (~> 0.9.0)
67
50
  public_suffix (4.0.1)
68
51
  rake (10.5.0)
69
- rouge (3.12.0)
70
52
  rspec (3.9.0)
71
53
  rspec-core (~> 3.9.0)
72
54
  rspec-expectations (~> 3.9.0)
@@ -92,52 +74,16 @@ GEM
92
74
  json (>= 1.8, < 3)
93
75
  simplecov-html (~> 0.10.0)
94
76
  simplecov-html (0.10.2)
95
- strings (0.1.6)
96
- strings-ansi (~> 0.1)
97
- unicode-display_width (~> 1.5)
98
- unicode_utils (~> 1.4)
99
- strings-ansi (0.1.0)
100
77
  thor (0.20.3)
101
78
  thread_safe (0.3.6)
102
- tty-box (0.4.1)
103
- pastel (~> 0.7.2)
104
- strings (~> 0.1.6)
105
- tty-cursor (~> 0.7)
106
79
  tty-color (0.5.0)
107
- tty-command (0.9.0)
108
- pastel (~> 0.7.0)
109
80
  tty-config (0.3.2)
110
81
  tty-cursor (0.7.0)
111
82
  tty-editor (0.5.1)
112
83
  tty-prompt (~> 0.19)
113
84
  tty-which (~> 0.4)
114
- tty-file (0.8.0)
115
- diff-lcs (~> 1.3)
116
- pastel (~> 0.7.2)
117
- tty-prompt (~> 0.18)
118
- tty-font (0.4.0)
119
85
  tty-logger (0.2.0)
120
86
  pastel (~> 0.7.0)
121
- tty-markdown (0.6.0)
122
- kramdown (~> 1.16.2)
123
- pastel (~> 0.7.2)
124
- rouge (~> 3.3)
125
- strings (~> 0.1.4)
126
- tty-color (~> 0.4)
127
- tty-screen (~> 0.6)
128
- tty-pager (0.12.1)
129
- strings (~> 0.1.4)
130
- tty-screen (~> 0.6)
131
- tty-which (~> 0.4)
132
- tty-pie (0.3.0)
133
- pastel (~> 0.7.3)
134
- tty-cursor (~> 0.7)
135
- tty-platform (0.2.1)
136
- tty-progressbar (0.17.0)
137
- strings-ansi (~> 0.1.0)
138
- tty-cursor (~> 0.7)
139
- tty-screen (~> 0.7)
140
- unicode-display_width (~> 1.6)
141
87
  tty-prompt (0.19.0)
142
88
  necromancer (~> 0.5.0)
143
89
  pastel (~> 0.7.0)
@@ -147,22 +93,11 @@ GEM
147
93
  tty-screen (~> 0.7)
148
94
  wisper (~> 2.0.0)
149
95
  tty-screen (0.7.0)
150
- tty-spinner (0.9.1)
151
- tty-cursor (~> 0.7)
152
- tty-table (0.11.0)
153
- equatable (~> 0.6)
154
- necromancer (~> 0.5)
155
- pastel (~> 0.7.2)
156
- strings (~> 0.1.5)
157
- tty-screen (~> 0.7)
158
- tty-tree (0.3.0)
159
96
  tty-which (0.4.1)
160
97
  tzinfo (1.2.5)
161
98
  thread_safe (~> 0.1)
162
- unicode-display_width (1.6.0)
163
- unicode_utils (1.4.0)
164
99
  wisper (2.0.1)
165
- zeitwerk (2.2.0)
100
+ zeitwerk (2.2.1)
166
101
 
167
102
  PLATFORMS
168
103
  ruby
@@ -170,7 +105,7 @@ PLATFORMS
170
105
  DEPENDENCIES
171
106
  bundler (~> 1.17)
172
107
  michael!
173
- pry
108
+ pry (~> 0.12.2)
174
109
  rake (~> 10.0)
175
110
  rspec (~> 3.0)
176
111
  simplecov (~> 0.17.0)
data/exe/michael CHANGED
@@ -17,7 +17,7 @@ begin
17
17
  rescue TTY::Reader::InputInterrupt
18
18
  warn('interrupted')
19
19
  exit(1)
20
- rescue Michael::CLI::Error => e
21
- puts "ERROR: #{e.message}"
20
+ rescue Michael::Error => e
21
+ puts "Error: #{e.message}"
22
22
  exit 1
23
23
  end
data/lib/michael.rb CHANGED
@@ -4,5 +4,6 @@ require 'michael/version'
4
4
 
5
5
  module Michael
6
6
  class Error < StandardError; end
7
+ class Fatal < StandardError; end
7
8
 
8
9
  end
data/lib/michael/cli.rb CHANGED
@@ -1,6 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'thor'
4
+ require 'tty-prompt'
5
+ require 'tty-config'
6
+
7
+ require_relative 'constants'
8
+ require_relative 'services/github/token'
9
+ require_relative 'services/configuration'
4
10
 
5
11
  module Michael
6
12
  # Handle the application command line parsing
@@ -35,7 +41,15 @@ module Michael
35
41
  invoke :help, ['auth']
36
42
  else
37
43
  require_relative 'commands/auth'
38
- Michael::Commands::Auth.new(options).execute
44
+ ttycfg = TTY::Config.new
45
+ ttycfg.append_path(Michael::CONFIG_DIR_ABSOLUTE_PATH)
46
+ ttycfg.filename = Michael::CONFIG_FILENAME
47
+
48
+ cfg = Michael::Services::Configuration.new(ttycfg)
49
+
50
+ token = Michael::Services::Github::Token.new(cfg)
51
+
52
+ Michael::Commands::Auth.new(TTY::Prompt.new, token, options).execute
39
53
  end
40
54
  end
41
55
  end
@@ -1,35 +1,25 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'tty-prompt'
4
-
5
- require_relative '../command'
6
- require_relative '../models/github/token_validator'
7
-
8
3
  module Michael
9
4
  module Commands
10
- class Auth < Michael::Command
11
- attr_reader :prompt
12
-
13
- def initialize(options)
14
- @prompt = TTY::Prompt.new
5
+ class Auth
6
+ def initialize(prompt, token, options)
7
+ @prompt = prompt
8
+ @token = token
15
9
  @options = options
16
10
  end
17
11
 
18
- def execute(out: $stdout)
19
- token = read_token
20
- unless Michael::Models::Github::TokenValidator.token_valid?(token)
21
- return out.puts 'Specified token is invalid'
22
- end
23
-
24
- unless Michael::Models::Github::TokenValidator.save_token(token)
25
- return out.puts 'Failed to save the token'
26
- end
27
-
28
- out.puts 'Token saved'
12
+ def execute
13
+ tkn = read_token
14
+ token.validate(tkn)
15
+ token.store(tkn)
16
+ puts 'Token saved!'
29
17
  end
30
18
 
31
19
  private
32
20
 
21
+ attr_reader :prompt, :token, :options
22
+
33
23
  def read_token
34
24
  prompt.mask('Please specify github token:', echo: false) do |q|
35
25
  q.modify :strip
@@ -1,6 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'thor'
4
+ require 'tty-editor'
5
+
6
+ require 'michael/constants'
7
+ require 'michael/services/github/users'
8
+ require 'michael/services/configuration'
9
+ require 'michael/services/github/token'
10
+ require 'michael/services/github/pull_requests'
11
+ require 'michael/services/repositories'
4
12
 
5
13
  module Michael
6
14
  module Commands
@@ -15,7 +23,22 @@ module Michael
15
23
  invoke :help, ['edit']
16
24
  else
17
25
  require_relative 'repos/edit'
18
- Michael::Commands::Repos::Edit.new(options).execute
26
+ ttycfg = TTY::Config.new
27
+ ttycfg.append_path(Michael::CONFIG_DIR_ABSOLUTE_PATH)
28
+ ttycfg.filename = Michael::CONFIG_FILENAME
29
+
30
+ ttycfgrepos = TTY::Config.new
31
+ ttycfgrepos.append_path(Michael::CONFIG_DIR_ABSOLUTE_PATH)
32
+ ttycfgrepos.filename = Michael::CONFIG_REPOS_FILENAME
33
+
34
+ cfg = Michael::Services::Configuration.new(ttycfg)
35
+ repocfg = Michael::Services::Configuration.new(ttycfgrepos)
36
+
37
+ token = Michael::Services::Github::Token.new(cfg)
38
+ token.validate(cfg.fetch(:token))
39
+
40
+ repos_filepath = Michael::CONFIG_DIR_ABSOLUTE_PATH + '/' + Michael::CONFIG_REPOS_FILENAME + '.yml'
41
+ Michael::Commands::Repos::Edit.new(repos_filepath, repocfg, TTY::Editor, options).execute
19
42
  end
20
43
  end
21
44
 
@@ -41,7 +64,21 @@ module Michael
41
64
  invoke :help, ['prs']
42
65
  else
43
66
  require_relative 'repos/pull_requests'
44
- Michael::Commands::Repos::PullRequests.new(options).execute
67
+ ttycfgrepos = TTY::Config.new
68
+ ttycfgrepos.append_path(Michael::CONFIG_DIR_ABSOLUTE_PATH)
69
+ ttycfgrepos.filename = Michael::CONFIG_REPOS_FILENAME
70
+
71
+ ttycfgtkn = TTY::Config.new
72
+ ttycfgtkn.append_path(Michael::CONFIG_DIR_ABSOLUTE_PATH)
73
+ ttycfgtkn.filename = Michael::CONFIG_FILENAME
74
+
75
+ repocfg = Michael::Services::Configuration.new(ttycfgrepos)
76
+ tkncfg = Michael::Services::Configuration.new(ttycfgtkn)
77
+
78
+ prs = Michael::Services::Github::PullRequests.new(tkncfg)
79
+ users = Michael::Services::Github::Users.new(tkncfg)
80
+ repos = Michael::Services::Repositories.new(prs)
81
+ Michael::Commands::Repos::PullRequests.new(repocfg, users, repos, options).execute
45
82
  end
46
83
  end
47
84
  end
@@ -1,24 +1,28 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../../command'
4
- require_relative '../../models/guard'
5
-
6
3
  module Michael
7
4
  module Commands
8
5
  class Repos
9
- class Edit < Michael::Models::Guard
10
- def initialize(options)
6
+ class Edit
7
+ def initialize(repos_filepath, cfg, editor, options)
11
8
  super()
12
9
 
10
+ @repos_filepath = repos_filepath
11
+ @cfg = cfg
12
+ @editor = editor
13
13
  @options = options
14
14
  end
15
15
 
16
- def execute(input: $stdin, output: $stdout)
17
- list = repos_config.fetch(:repos)
18
- repos_config.append('org/repo', to: :repos) if list.nil? || list.empty?
16
+ def execute
17
+ list = cfg.fetch(:repos)
18
+ cfg.append('org/repo', to: :repos) if list.nil? || list.empty?
19
19
 
20
- editor.open(repos_config.config_file_path)
20
+ editor.open(repos_filepath)
21
21
  end
22
+
23
+ private
24
+
25
+ attr_reader :repos_filepath, :cfg, :editor
22
26
  end
23
27
  end
24
28
  end
@@ -2,130 +2,71 @@
2
2
 
3
3
  require 'parallel'
4
4
 
5
- require_relative '../../command'
6
- require_relative '../../models/github/pull_request'
7
- require_relative '../../models/guard'
8
- require_relative '../../models/pull_request_formatter'
9
- require_relative '../../models/repository_formatter'
10
- require_relative '../../models/github/user'
11
-
12
5
  module Michael
13
6
  module Commands
14
7
  class Repos
15
- class PullRequests < Models::Guard
16
- def initialize(options)
17
- super()
18
-
19
- @prs = Michael::Models::Github::PullRequest.new
20
- @user = Michael::Models::Github::User.new
21
- @repos = repos_config.fetch(:repos)
22
- abort 'No repositories configured' if @repos.nil? || @repos.empty?
8
+ class PullRequests
9
+ attr_reader :config, :users, :repos, :options
23
10
 
11
+ def initialize(config, users, repos, options)
12
+ @config = config
13
+ @users = users
14
+ @repos = repos
24
15
  @options = options
25
16
  end
26
17
 
27
- def execute(out: $stdout)
28
- list = get_repos_with_spinner(out)
18
+ def execute
19
+ q = Queue.new
20
+ waiting = print_waiting(q)
21
+ list = config.fetch(:repos)
29
22
 
30
- print_good_prs(out, repos_with_prs(list))
31
- print_repos_w_no_prs(out, repos_no_prs(list)) if options[:show_empty]
32
- print_broken_repos(out, list.select { |item| item[:state] == :failed })
23
+ list = repos.pull_requests(list, q)
24
+ waiting.join
25
+
26
+ puts [filter_repos_w_prs(list), get_empty(list), get_broken(list)]
27
+ .reject(&:nil?).join("\n\n")
33
28
  end
34
29
 
35
30
  private
36
31
 
37
- attr_reader :prs, :options, :repos, :user
38
-
39
- def get_repos_with_spinner(out)
40
- progress = 0
41
- spin = spinner(
42
- "[:spinner] :progress/#{repos.length} processing...",
43
- output: out
44
- )
45
-
46
- spin.update(progress: 0)
47
-
48
- result = Parallel.map(repos, in_threads: 5) do |repo|
49
- progress += 1
50
- spin.update(progress: progress)
51
- spin.spin
52
- out = prs.process_repo(repo)
53
- spin.spin
54
- out
32
+ def filter_repos_w_prs(list)
33
+ list.each do |r|
34
+ r.prs.reject! { |pr| pr.author?(users.user.username) } if options[:skip_self]
35
+ r.prs.reject!(&:approved?) if options[:hide_approved]
36
+ r.prs.select!(&:needs_review?) if options[:needs_review]
37
+ r.prs.select! { |pr| pr.actionable?(users.user.username) } if options[:actionable]
55
38
  end
56
39
 
57
- spin.stop('done')
58
-
59
- result
40
+ list.select(&:has_prs?).map!(&:pretty_print).join("\n\n")
60
41
  end
61
42
 
62
- def repos_no_prs(list_all)
63
- list_all
64
- .select { |item| item[:state] == :success && item[:prs].empty? }
65
- .map { |item| item[:repo] }
66
- end
67
-
68
- def repos_with_prs(list)
69
- list = needs_review(list) if options[:needs_review]
70
- list = skip_self(list) if options[:skip_self]
71
- list = hide_approved_or_commented(list) if options[:hide_approved]
72
- list = actionable(list) if options[:actionable]
73
- list = select_repos_w_prs(list)
43
+ def get_broken(list)
44
+ broken = list.select(&:broken?)
45
+ return nil if broken.none?
74
46
 
75
- list.map { |item| Michael::Models::RepositoryFormatter.new(item[:repo], item[:prs]).pretty }
47
+ 'Broken repos: ' + broken.map(&:pretty_print).join(', ')
76
48
  end
77
49
 
78
- def skip_self(list)
79
- list.each do |item|
80
- item[:prs] = item[:prs].reject { |pr| pr.author == user.username }
81
- end
82
- end
50
+ def get_empty(list)
51
+ return nil unless options[:show_empty]
83
52
 
84
- def hide_approved_or_commented(list)
85
- list.each do |item|
86
- item[:prs] = item[:prs].reject do |pr|
87
- pr.reviews.all? { |review| review.approved? || review.commented? }
88
- end
89
- end
90
- end
53
+ empty = list.reject(&:has_prs?)
54
+ return nil if empty.none?
91
55
 
92
- def select_repos_w_prs(list)
93
- list.select { |item| item[:state] == :success && item[:prs].any? }
56
+ 'No PRs: ' + empty.map(&:pretty_print).join(', ')
94
57
  end
95
58
 
96
- def needs_review(list)
97
- list.each do |item|
98
- item[:prs] = item[:prs].reject do |pr|
99
- pr.reviews.any?
59
+ def print_waiting(queue)
60
+ Thread.new do
61
+ until queue.closed?
62
+ queue.pop
63
+ print '.'
64
+ $stdout.flush
100
65
  end
101
- end
102
- end
103
66
 
104
- def actionable(list)
105
- list.each do |item|
106
- item[:prs] = item[:prs].reject do |pr|
107
- reviewed = pr.reviews.any? { |review| review.author == user.username }
108
- new_changes = pr.last_update_head?
109
-
110
- reviewed && !new_changes
111
- end
67
+ puts
112
68
  end
113
69
  end
114
-
115
- def print_good_prs(out, list)
116
- out.puts "\n" + list.join("\n\n")
117
- end
118
-
119
- def print_repos_w_no_prs(out, list)
120
- out.puts "\nRepos with no opened PRs: #{list.join(', ')}" if list.any?
121
- end
122
-
123
- def print_broken_repos(out, list)
124
- return if list.empty?
125
-
126
- list = list.map { |repo| pastel.on_red.black(repo[:repo]) }
127
- out.puts "\nBad repos: #{list.join(', ')}"
128
- end
129
70
  end
130
71
  end
131
72
  end