pronto 0.10.0 → 0.11.1

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.
@@ -10,7 +10,7 @@ module Pronto
10
10
 
11
11
  approve_pull_request(comments.count, additions.count, client) if defined?(self.approve_pull_request)
12
12
 
13
- "#{additions.count} Pronto messages posted to #{pretty_name}"
13
+ "#{additions.count} Pronto messages posted to #{pretty_name} (#{existing.count} existing)"
14
14
  end
15
15
 
16
16
  def client_module
@@ -0,0 +1,24 @@
1
+ require_relative 'github_status_formatter/status_builder'
2
+
3
+ module Pronto
4
+ module Formatter
5
+ class GithubCombinedStatusFormatter
6
+ def format(messages, repo, _)
7
+ client = Github.new(repo)
8
+ head = repo.head_commit_sha
9
+
10
+ create_status(client, head, messages.uniq || [])
11
+ end
12
+
13
+ private
14
+
15
+ def create_status(client, sha, messages)
16
+ builder = GithubStatusFormatter::StatusBuilder.new(nil, messages)
17
+ status = Status.new(sha, builder.state,
18
+ 'pronto', builder.description)
19
+
20
+ client.create_commit_status(status)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -10,7 +10,7 @@ module Pronto
10
10
  end
11
11
 
12
12
  def submit_comments(client, comments)
13
- client.create_pull_request_review(comments)
13
+ client.publish_pull_request_comments(comments)
14
14
  rescue Octokit::UnprocessableEntity, HTTParty::Error => e
15
15
  $stderr.puts "Failed to post: #{e.message}"
16
16
  end
@@ -0,0 +1,29 @@
1
+ module Pronto
2
+ module Formatter
3
+ class GitlabMergeRequestReviewFormatter < PullRequestFormatter
4
+ def client_module
5
+ Gitlab
6
+ end
7
+
8
+ def pretty_name
9
+ 'Gitlab'
10
+ end
11
+
12
+ def existing_comments(_, client, repo)
13
+ sha = repo.head_commit_sha
14
+ comments = client.pull_comments(sha)
15
+ grouped_comments(comments)
16
+ end
17
+
18
+ def submit_comments(client, comments)
19
+ client.create_pull_request_review(comments)
20
+ rescue => e
21
+ $stderr.puts "Failed to post: #{e.message}"
22
+ end
23
+
24
+ def line_number(message, _)
25
+ message.line.line.new_lineno if message.line
26
+ end
27
+ end
28
+ end
29
+ end
@@ -3,7 +3,7 @@ require 'json'
3
3
  module Pronto
4
4
  module Formatter
5
5
  class JsonFormatter < Base
6
- def format(messages, _, _)
6
+ def format(messages, _repo, _patches)
7
7
  messages.map do |message|
8
8
  lineno = message.line.new_lineno if message.line
9
9
 
@@ -1,7 +1,7 @@
1
1
  module Pronto
2
2
  module Formatter
3
3
  class NullFormatter < Base
4
- def format(_, _, _); end
4
+ def format(_messages, _repo, _patches); end
5
5
  end
6
6
  end
7
7
  end
@@ -3,7 +3,7 @@ require 'pronto/formatter/text_message_decorator'
3
3
  module Pronto
4
4
  module Formatter
5
5
  class TextFormatter < Base
6
- def format(messages, _, _)
6
+ def format(messages, _repo, _patches)
7
7
  messages.map do |message|
8
8
  message_format = config.message_format(self.class.name)
9
9
  message_data = TextMessageDecorator.new(message).to_h
@@ -37,8 +37,6 @@ module Pronto
37
37
  repo.path.join(new_file_path)
38
38
  end
39
39
 
40
- private
41
-
42
40
  def new_file_path
43
41
  delta.new_file[:path]
44
42
  end
@@ -13,6 +13,18 @@ module Pronto
13
13
  [head_commit_sha, @repo.index.diff(options)]
14
14
  when :staged
15
15
  [head_commit_sha, head.diff(@repo.index, options)]
16
+ when :workdir
17
+ [
18
+ head_commit_sha,
19
+ @repo.diff_workdir(
20
+ head,
21
+ {
22
+ include_untracked: true,
23
+ include_untracked_content: true,
24
+ recurse_untracked_dirs: true
25
+ }.merge(options || {})
26
+ )
27
+ ]
16
28
  else
17
29
  merge_base = merge_base(commit)
18
30
  patches = @repo.diff(merge_base, head, options)
@@ -38,7 +50,7 @@ module Pronto
38
50
 
39
51
  def commits_until(sha)
40
52
  result = []
41
- @repo.walk(head, Rugged::SORT_TOPO).take_while do |commit|
53
+ @repo.walk(head_commit_sha, Rugged::SORT_TOPO).take_while do |commit|
42
54
  result << commit.oid
43
55
  !commit.oid.start_with?(sha)
44
56
  end
@@ -46,7 +58,7 @@ module Pronto
46
58
  end
47
59
 
48
60
  def path
49
- Pathname.new(@repo.path).parent
61
+ Pathname.new(@repo.workdir).cleanpath
50
62
  end
51
63
 
52
64
  def blame(path, lineno)
data/lib/pronto/github.rb CHANGED
@@ -45,17 +45,12 @@ module Pronto
45
45
  end
46
46
  end
47
47
 
48
- def create_pull_request_review(comments)
49
- return if comments.empty?
50
-
51
- options = {
52
- event: 'COMMENT',
53
- accept: 'application/vnd.github.v3.diff+json', # https://developer.github.com/v3/pulls/reviews/#create-a-pull-request-review
54
- comments: comments.map do |c|
55
- { path: c.path, position: c.position, body: c.body }
56
- end
57
- }
58
- client.create_pull_request_review(slug, pull_id, options)
48
+ def publish_pull_request_comments(comments)
49
+ comments_left = comments.clone
50
+ while comments_left.any?
51
+ comments_to_publish = comments_left.slice!(0, warnings_per_review)
52
+ create_pull_request_review(comments_to_publish)
53
+ end
59
54
  end
60
55
 
61
56
  def create_commit_status(status)
@@ -68,6 +63,21 @@ module Pronto
68
63
 
69
64
  private
70
65
 
66
+ def create_pull_request_review(comments)
67
+ options = {
68
+ event: @config.github_review_type,
69
+ accept: 'application/vnd.github.v3.diff+json', # https://developer.github.com/v3/pulls/reviews/#create-a-pull-request-review
70
+ comments: comments.map do |comment|
71
+ {
72
+ path: comment.path,
73
+ position: comment.position,
74
+ body: comment.body
75
+ }
76
+ end
77
+ }
78
+ client.create_pull_request_review(slug, pull_id, options)
79
+ end
80
+
71
81
  def slug
72
82
  return @config.github_slug if @config.github_slug
73
83
  @slug ||= begin
@@ -103,5 +113,9 @@ module Pronto
103
113
  @github_pull.pull_by_commit(@repo.head_commit_sha)
104
114
  end
105
115
  end
116
+
117
+ def warnings_per_review
118
+ @warnings_per_review ||= @config.warnings_per_review
119
+ end
106
120
  end
107
121
  end
data/lib/pronto/gitlab.rb CHANGED
@@ -2,12 +2,49 @@ module Pronto
2
2
  class Gitlab < Client
3
3
  def commit_comments(sha)
4
4
  @comment_cache[sha.to_s] ||= begin
5
- client.commit_comments(slug, sha, per_page: 500).map do |comment|
5
+ client.commit_comments(slug, sha).auto_paginate.map do |comment|
6
6
  Comment.new(sha, comment.note, comment.path, comment.line)
7
7
  end
8
8
  end
9
9
  end
10
10
 
11
+ def pull_comments(sha)
12
+ @comment_cache["#{slug}/#{pull_id}"] ||= begin
13
+ arr = []
14
+ client.merge_request_discussions(slug, pull_id).auto_paginate.each do |comment|
15
+ comment.notes.each do |note|
16
+ next unless note['position']
17
+
18
+ arr << Comment.new(
19
+ sha,
20
+ note['body'],
21
+ note['position']['new_path'],
22
+ note['position']['new_line']
23
+ )
24
+ end
25
+ end
26
+ arr
27
+ end
28
+ end
29
+
30
+ def create_pull_request_review(comments)
31
+ return if comments.empty?
32
+
33
+ comments.each do |comment|
34
+ options = {
35
+ body: comment.body,
36
+ position: position_sha.dup.merge(
37
+ new_path: comment.path,
38
+ position_type: 'text',
39
+ new_line: comment.position,
40
+ old_line: nil,
41
+ )
42
+ }
43
+
44
+ client.create_merge_request_discussion(slug, pull_id, options)
45
+ end
46
+ end
47
+
11
48
  def create_commit_comment(comment)
12
49
  @config.logger.log("Creating commit comment on #{comment.sha}")
13
50
  client.create_commit_comment(slug, comment.sha, comment.body,
@@ -17,6 +54,15 @@ module Pronto
17
54
 
18
55
  private
19
56
 
57
+ def position_sha
58
+ # Better to get those informations from Gitlab API directly than trying to look for them here.
59
+ # (FYI you can't use `pull` method because index api does not contains those informations)
60
+ @position_sha ||= begin
61
+ data = client.merge_request(slug, pull_id)
62
+ data.diff_refs.to_h
63
+ end
64
+ end
65
+
20
66
  def slug
21
67
  return @config.gitlab_slug if @config.gitlab_slug
22
68
  @slug ||= begin
@@ -27,11 +73,24 @@ module Pronto
27
73
  end
28
74
  end
29
75
 
76
+ def pull_id
77
+ env_pull_id || raise(Pronto::Error, "Unable to determine merge request id. Specify either `PRONTO_PULL_REQUEST_ID` or `CI_MERGE_REQUEST_IID`.")
78
+ end
79
+
80
+ def env_pull_id
81
+ pull_request = super
82
+
83
+ pull_request ||= ENV['CI_MERGE_REQUEST_IID']
84
+ pull_request.to_i if pull_request
85
+ end
86
+
30
87
  def slug_regex(url)
31
88
  if url =~ %r{^ssh:\/\/}
32
89
  %r{.*#{host}(:[0-9]+)?(:|\/)(?<slug>.*).git}
33
- else
90
+ elsif url =~ /#{host}/
34
91
  %r{.*#{host}(:|\/)(?<slug>.*).git}
92
+ else
93
+ %r{\/\/.*?(\/)(?<slug>.*).git}
35
94
  end
36
95
  end
37
96
 
data/lib/pronto/runner.rb CHANGED
@@ -28,7 +28,10 @@ module Pronto
28
28
  end
29
29
 
30
30
  def ruby_file?(path)
31
- rb_file?(path) || rake_file?(path) || ruby_executable?(path)
31
+ rb_file?(path) ||
32
+ rake_file?(path) ||
33
+ gem_file?(path) ||
34
+ ruby_executable?(path)
32
35
  end
33
36
 
34
37
  def repo_path
@@ -45,6 +48,10 @@ module Pronto
45
48
  File.extname(path) == '.rake'
46
49
  end
47
50
 
51
+ def gem_file?(path)
52
+ File.basename(path) == 'Gemfile' || File.extname(path) == '.gemspec'
53
+ end
54
+
48
55
  def ruby_executable?(path)
49
56
  return false if File.directory?(path)
50
57
  line = File.open(path, &:readline)
@@ -6,25 +6,42 @@ module Pronto
6
6
  end
7
7
 
8
8
  def run(patches)
9
- patches = reject_excluded(@config.excluded_files('all'), patches)
9
+ patches = reject_excluded(config.excluded_files('all'), patches)
10
10
  return [] if patches.none?
11
11
 
12
12
  result = []
13
- @runners.each do |runner|
13
+ active_runners.each do |runner|
14
14
  next if exceeds_max?(result)
15
- @config.logger.log("Running #{runner}")
15
+ config.logger.log("Running #{runner}")
16
16
  runner_patches = reject_excluded(
17
- @config.excluded_files(runner.title), patches
17
+ config.excluded_files(runner.title), patches
18
18
  )
19
19
  next if runner_patches.none?
20
20
  result += runner.new(runner_patches, patches.commit).run.flatten.compact
21
21
  end
22
- result = result.take(@config.max_warnings) if @config.max_warnings
22
+ result = result.take(config.max_warnings) if config.max_warnings
23
23
  result
24
24
  end
25
25
 
26
26
  private
27
27
 
28
+ attr_reader :config, :runners
29
+
30
+ def active_runners
31
+ runners.select { |runner| active_runner?(runner) }
32
+ end
33
+
34
+ def active_runner?(runner)
35
+ return true if config.runners.empty? && config.skip_runners.empty?
36
+
37
+ if config.runners.empty?
38
+ !config.skip_runners.include?(runner.title)
39
+ else
40
+ active_runner_names = config.runners - config.skip_runners
41
+ active_runner_names.include?(runner.title)
42
+ end
43
+ end
44
+
28
45
  def reject_excluded(excluded_files, patches)
29
46
  return patches unless excluded_files.any?
30
47
 
@@ -34,7 +51,7 @@ module Pronto
34
51
  end
35
52
 
36
53
  def exceeds_max?(warnings)
37
- @config.max_warnings && warnings.count >= @config.max_warnings
54
+ config.max_warnings && warnings.count >= config.max_warnings
38
55
  end
39
56
  end
40
57
  end
@@ -1,6 +1,6 @@
1
1
  module Pronto
2
2
  module Version
3
- STRING = '0.10.0'.freeze
3
+ STRING = '0.11.1'.freeze
4
4
 
5
5
  MSG = '%s (running on %s %s %s)'.freeze
6
6
 
data/lib/pronto.rb CHANGED
@@ -42,9 +42,11 @@ require 'pronto/formatter/commit_formatter'
42
42
  require 'pronto/formatter/pull_request_formatter'
43
43
  require 'pronto/formatter/github_formatter'
44
44
  require 'pronto/formatter/github_status_formatter'
45
+ require 'pronto/formatter/github_combined_status_formatter'
45
46
  require 'pronto/formatter/github_pull_request_formatter'
46
47
  require 'pronto/formatter/github_pull_request_review_formatter'
47
48
  require 'pronto/formatter/gitlab_formatter'
49
+ require 'pronto/formatter/gitlab_merge_request_review_formatter'
48
50
  require 'pronto/formatter/bitbucket_formatter'
49
51
  require 'pronto/formatter/bitbucket_pull_request_formatter'
50
52
  require 'pronto/formatter/bitbucket_server_pull_request_formatter'
@@ -53,9 +55,9 @@ require 'pronto/formatter/null_formatter'
53
55
  require 'pronto/formatter/formatter'
54
56
 
55
57
  module Pronto
56
- def self.run(commit = 'master', repo_path = '.',
58
+ def self.run(commit = nil, repo_path = '.',
57
59
  formatters = [Formatter::TextFormatter.new], file = nil)
58
- commit ||= 'master'
60
+ commit ||= default_commit
59
61
 
60
62
  repo = Git::Repository.new(repo_path)
61
63
  options = { paths: [file] } if file
@@ -70,4 +72,8 @@ module Pronto
70
72
 
71
73
  result
72
74
  end
75
+
76
+ def self.default_commit
77
+ Config.new.default_commit
78
+ end
73
79
  end
data/pronto.gemspec CHANGED
@@ -40,18 +40,20 @@ Gem::Specification.new do |s|
40
40
  s.require_paths = ['lib']
41
41
  s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
42
42
 
43
- s.add_runtime_dependency('gitlab', '~> 4.0', '>= 4.0.0')
44
- s.add_runtime_dependency('httparty', '>= 0.13.7')
45
- s.add_runtime_dependency('octokit', '~> 4.7', '>= 4.7.0')
43
+ s.add_runtime_dependency('gitlab', '>= 4.4.0', '< 5.0')
44
+ s.add_runtime_dependency('httparty', '>= 0.13.7', '< 1.0')
45
+ s.add_runtime_dependency('octokit', '>= 4.7.0', '< 7.0')
46
46
  s.add_runtime_dependency('rainbow', '>= 2.2', '< 4.0')
47
- s.add_runtime_dependency('rugged', '~> 0.24', '>= 0.23.0')
48
- s.add_runtime_dependency('thor', '~> 0.20.0')
47
+ s.add_runtime_dependency('rexml', '>= 3.2.5', '< 4.0')
48
+ s.add_runtime_dependency('rugged', '>= 0.23.0', '< 2.0')
49
+ s.add_runtime_dependency('thor', '>= 0.20.3', '< 2.0')
49
50
  s.add_development_dependency('bundler', '>= 1.15')
50
- s.add_development_dependency('pronto-rubocop', '~> 0.9.0')
51
+ s.add_development_dependency('pronto-rubocop', '~> 0.10.0')
51
52
  s.add_development_dependency('rake', '~> 12.0')
52
53
  s.add_development_dependency('rspec', '~> 3.4')
53
54
  s.add_development_dependency('rspec-its', '~> 1.2')
54
55
  s.add_development_dependency('rspec-expectations', '~> 3.4')
55
56
  s.add_development_dependency('rubocop', '~> 0.58')
56
- s.add_development_dependency('simplecov', '~> 0.14')
57
+ s.add_development_dependency('simplecov', '~> 0.17', '!= 0.18.0', '!= 0.18.1', '!= 0.18.2', '!= 0.18.3', '!= 0.18.4',
58
+ '!= 0.18.5', '!= 0.19.0', '!= 0.19.1') # see https://docs.codeclimate.com/docs/configuring-test-coverage
57
59
  end