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.
- checksums.yaml +4 -4
- data/.github/CODEOWNERS +3 -0
- data/.github/workflows/pronto.yml +20 -0
- data/.github/workflows/specs.yml +41 -0
- data/CHANGELOG.md +58 -1
- data/README.md +113 -5
- data/lib/pronto/cli.rb +6 -2
- data/lib/pronto/clients/bitbucket_client.rb +46 -18
- data/lib/pronto/config.rb +56 -3
- data/lib/pronto/config_file.rb +8 -2
- data/lib/pronto/formatter/checkstyle_formatter.rb +1 -1
- data/lib/pronto/formatter/formatter.rb +2 -0
- data/lib/pronto/formatter/git_formatter.rb +1 -1
- data/lib/pronto/formatter/github_combined_status_formatter.rb +24 -0
- data/lib/pronto/formatter/github_pull_request_review_formatter.rb +1 -1
- data/lib/pronto/formatter/gitlab_merge_request_review_formatter.rb +29 -0
- data/lib/pronto/formatter/json_formatter.rb +1 -1
- data/lib/pronto/formatter/null_formatter.rb +1 -1
- data/lib/pronto/formatter/text_formatter.rb +1 -1
- data/lib/pronto/git/patch.rb +0 -2
- data/lib/pronto/git/repository.rb +14 -2
- data/lib/pronto/github.rb +25 -11
- data/lib/pronto/gitlab.rb +61 -2
- data/lib/pronto/runner.rb +8 -1
- data/lib/pronto/runners.rb +23 -6
- data/lib/pronto/version.rb +1 -1
- data/lib/pronto.rb +8 -2
- data/pronto.gemspec +9 -7
- metadata +113 -28
@@ -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.
|
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 '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
|
data/lib/pronto/git/patch.rb
CHANGED
@@ -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(
|
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.
|
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
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
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
|
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
|
-
|
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) ||
|
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)
|
data/lib/pronto/runners.rb
CHANGED
@@ -6,25 +6,42 @@ module Pronto
|
|
6
6
|
end
|
7
7
|
|
8
8
|
def run(patches)
|
9
|
-
patches = reject_excluded(
|
9
|
+
patches = reject_excluded(config.excluded_files('all'), patches)
|
10
10
|
return [] if patches.none?
|
11
11
|
|
12
12
|
result = []
|
13
|
-
|
13
|
+
active_runners.each do |runner|
|
14
14
|
next if exceeds_max?(result)
|
15
|
-
|
15
|
+
config.logger.log("Running #{runner}")
|
16
16
|
runner_patches = reject_excluded(
|
17
|
-
|
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(
|
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
|
-
|
54
|
+
config.max_warnings && warnings.count >= config.max_warnings
|
38
55
|
end
|
39
56
|
end
|
40
57
|
end
|
data/lib/pronto/version.rb
CHANGED
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 =
|
58
|
+
def self.run(commit = nil, repo_path = '.',
|
57
59
|
formatters = [Formatter::TextFormatter.new], file = nil)
|
58
|
-
commit ||=
|
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', '
|
44
|
-
s.add_runtime_dependency('httparty', '>= 0.13.7')
|
45
|
-
s.add_runtime_dependency('octokit', '
|
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('
|
48
|
-
s.add_runtime_dependency('
|
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.
|
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.
|
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
|