danger 8.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +22 -0
- data/README.md +94 -0
- data/bin/danger +5 -0
- data/lib/assets/DangerfileTemplate +13 -0
- data/lib/danger.rb +44 -0
- data/lib/danger/ci_source/appcenter.rb +55 -0
- data/lib/danger/ci_source/appveyor.rb +60 -0
- data/lib/danger/ci_source/azure_pipelines.rb +44 -0
- data/lib/danger/ci_source/bamboo.rb +41 -0
- data/lib/danger/ci_source/bitbucket_pipelines.rb +37 -0
- data/lib/danger/ci_source/bitrise.rb +65 -0
- data/lib/danger/ci_source/buddybuild.rb +62 -0
- data/lib/danger/ci_source/buildkite.rb +51 -0
- data/lib/danger/ci_source/ci_source.rb +37 -0
- data/lib/danger/ci_source/circle.rb +94 -0
- data/lib/danger/ci_source/circle_api.rb +51 -0
- data/lib/danger/ci_source/cirrus.rb +31 -0
- data/lib/danger/ci_source/code_build.rb +57 -0
- data/lib/danger/ci_source/codefresh.rb +53 -0
- data/lib/danger/ci_source/codeship.rb +44 -0
- data/lib/danger/ci_source/dotci.rb +52 -0
- data/lib/danger/ci_source/drone.rb +71 -0
- data/lib/danger/ci_source/github_actions.rb +43 -0
- data/lib/danger/ci_source/gitlab_ci.rb +86 -0
- data/lib/danger/ci_source/jenkins.rb +149 -0
- data/lib/danger/ci_source/local_git_repo.rb +119 -0
- data/lib/danger/ci_source/local_only_git_repo.rb +47 -0
- data/lib/danger/ci_source/screwdriver.rb +47 -0
- data/lib/danger/ci_source/semaphore.rb +37 -0
- data/lib/danger/ci_source/support/commits.rb +17 -0
- data/lib/danger/ci_source/support/find_repo_info_from_logs.rb +35 -0
- data/lib/danger/ci_source/support/find_repo_info_from_url.rb +42 -0
- data/lib/danger/ci_source/support/local_pull_request.rb +14 -0
- data/lib/danger/ci_source/support/no_pull_request.rb +7 -0
- data/lib/danger/ci_source/support/no_repo_info.rb +5 -0
- data/lib/danger/ci_source/support/pull_request_finder.rb +179 -0
- data/lib/danger/ci_source/support/remote_pull_request.rb +15 -0
- data/lib/danger/ci_source/support/repo_info.rb +10 -0
- data/lib/danger/ci_source/surf.rb +37 -0
- data/lib/danger/ci_source/teamcity.rb +159 -0
- data/lib/danger/ci_source/travis.rb +51 -0
- data/lib/danger/ci_source/vsts.rb +73 -0
- data/lib/danger/ci_source/xcode_server.rb +48 -0
- data/lib/danger/clients/rubygems_client.rb +14 -0
- data/lib/danger/commands/dangerfile/gem.rb +43 -0
- data/lib/danger/commands/dangerfile/init.rb +30 -0
- data/lib/danger/commands/dry_run.rb +54 -0
- data/lib/danger/commands/init.rb +297 -0
- data/lib/danger/commands/init_helpers/interviewer.rb +92 -0
- data/lib/danger/commands/local.rb +83 -0
- data/lib/danger/commands/local_helpers/http_cache.rb +36 -0
- data/lib/danger/commands/local_helpers/local_setup.rb +46 -0
- data/lib/danger/commands/local_helpers/pry_setup.rb +31 -0
- data/lib/danger/commands/plugins/plugin_json.rb +46 -0
- data/lib/danger/commands/plugins/plugin_lint.rb +54 -0
- data/lib/danger/commands/plugins/plugin_readme.rb +45 -0
- data/lib/danger/commands/pr.rb +92 -0
- data/lib/danger/commands/runner.rb +94 -0
- data/lib/danger/commands/staging.rb +53 -0
- data/lib/danger/commands/systems.rb +43 -0
- data/lib/danger/comment_generators/bitbucket_server.md.erb +20 -0
- data/lib/danger/comment_generators/bitbucket_server_inline.md.erb +15 -0
- data/lib/danger/comment_generators/bitbucket_server_message_group.md.erb +12 -0
- data/lib/danger/comment_generators/github.md.erb +55 -0
- data/lib/danger/comment_generators/github_inline.md.erb +26 -0
- data/lib/danger/comment_generators/gitlab.md.erb +40 -0
- data/lib/danger/comment_generators/gitlab_inline.md.erb +26 -0
- data/lib/danger/comment_generators/vsts.md.erb +20 -0
- data/lib/danger/core_ext/file_list.rb +18 -0
- data/lib/danger/core_ext/string.rb +20 -0
- data/lib/danger/danger_core/dangerfile.rb +341 -0
- data/lib/danger/danger_core/dangerfile_dsl.rb +29 -0
- data/lib/danger/danger_core/dangerfile_generator.rb +11 -0
- data/lib/danger/danger_core/environment_manager.rb +123 -0
- data/lib/danger/danger_core/executor.rb +92 -0
- data/lib/danger/danger_core/message_aggregator.rb +49 -0
- data/lib/danger/danger_core/message_group.rb +68 -0
- data/lib/danger/danger_core/messages/base.rb +56 -0
- data/lib/danger/danger_core/messages/markdown.rb +42 -0
- data/lib/danger/danger_core/messages/violation.rb +54 -0
- data/lib/danger/danger_core/plugins/dangerfile_bitbucket_cloud_plugin.rb +144 -0
- data/lib/danger/danger_core/plugins/dangerfile_bitbucket_server_plugin.rb +211 -0
- data/lib/danger/danger_core/plugins/dangerfile_danger_plugin.rb +248 -0
- data/lib/danger/danger_core/plugins/dangerfile_git_plugin.rb +158 -0
- data/lib/danger/danger_core/plugins/dangerfile_github_plugin.rb +254 -0
- data/lib/danger/danger_core/plugins/dangerfile_gitlab_plugin.rb +240 -0
- data/lib/danger/danger_core/plugins/dangerfile_local_only_plugin.rb +42 -0
- data/lib/danger/danger_core/plugins/dangerfile_messaging_plugin.rb +218 -0
- data/lib/danger/danger_core/plugins/dangerfile_vsts_plugin.rb +191 -0
- data/lib/danger/danger_core/standard_error.rb +143 -0
- data/lib/danger/helpers/array_subclass.rb +61 -0
- data/lib/danger/helpers/comment.rb +32 -0
- data/lib/danger/helpers/comments_helper.rb +178 -0
- data/lib/danger/helpers/comments_parsing_helper.rb +70 -0
- data/lib/danger/helpers/emoji_mapper.rb +41 -0
- data/lib/danger/helpers/find_max_num_violations.rb +31 -0
- data/lib/danger/helpers/message_groups_array_helper.rb +31 -0
- data/lib/danger/plugin_support/gems_resolver.rb +77 -0
- data/lib/danger/plugin_support/plugin.rb +49 -0
- data/lib/danger/plugin_support/plugin_file_resolver.rb +30 -0
- data/lib/danger/plugin_support/plugin_linter.rb +161 -0
- data/lib/danger/plugin_support/plugin_parser.rb +199 -0
- data/lib/danger/plugin_support/templates/readme_table.html.erb +26 -0
- data/lib/danger/request_sources/bitbucket_cloud.rb +171 -0
- data/lib/danger/request_sources/bitbucket_cloud_api.rb +181 -0
- data/lib/danger/request_sources/bitbucket_server.rb +105 -0
- data/lib/danger/request_sources/bitbucket_server_api.rb +117 -0
- data/lib/danger/request_sources/github/github.rb +530 -0
- data/lib/danger/request_sources/github/github_review.rb +126 -0
- data/lib/danger/request_sources/github/github_review_resolver.rb +19 -0
- data/lib/danger/request_sources/github/github_review_unsupported.rb +25 -0
- data/lib/danger/request_sources/gitlab.rb +525 -0
- data/lib/danger/request_sources/local_only.rb +53 -0
- data/lib/danger/request_sources/request_source.rb +85 -0
- data/lib/danger/request_sources/support/get_ignored_violation.rb +17 -0
- data/lib/danger/request_sources/vsts.rb +118 -0
- data/lib/danger/request_sources/vsts_api.rb +138 -0
- data/lib/danger/scm_source/git_repo.rb +181 -0
- data/lib/danger/version.rb +4 -0
- metadata +339 -0
@@ -0,0 +1,29 @@
|
|
1
|
+
module Danger
|
2
|
+
class Dangerfile
|
3
|
+
# Anything inside this module is considered public API, and in the future
|
4
|
+
# documentation will be generated from it via rdoc.
|
5
|
+
|
6
|
+
module DSL
|
7
|
+
# @!group Danger Zone
|
8
|
+
# Provides access to the raw Travis/Circle/Buildkite/GitHub objects, which
|
9
|
+
# you can use to pull out extra bits of information. _Warning_
|
10
|
+
# the interfaces of these objects is **not** considered a part of the Dangerfile public
|
11
|
+
# API, and is viable to change occasionally on the whims of developers.
|
12
|
+
# @return [EnvironmentManager]
|
13
|
+
|
14
|
+
attr_reader :env
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def initialize
|
19
|
+
load_default_plugins
|
20
|
+
end
|
21
|
+
|
22
|
+
def load_default_plugins
|
23
|
+
Dir["./danger_plugins/*.rb"].each do |file|
|
24
|
+
require File.expand_path(file)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Danger
|
2
|
+
class DangerfileGenerator
|
3
|
+
# returns the string for a Dangerfile based on a folder's contents'
|
4
|
+
def self.create_dangerfile(_path, _ui)
|
5
|
+
# Use this template for now, but this is a really ripe place to
|
6
|
+
# improve now!
|
7
|
+
dir = Danger.gem_path
|
8
|
+
File.read(File.join(dir, "lib", "assets", "DangerfileTemplate"))
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require "danger/ci_source/ci_source"
|
2
|
+
require "danger/request_sources/request_source"
|
3
|
+
|
4
|
+
module Danger
|
5
|
+
class EnvironmentManager
|
6
|
+
attr_accessor :ci_source, :request_source, :scm, :ui, :danger_id
|
7
|
+
|
8
|
+
# Finds a Danger::CI class based on the ENV
|
9
|
+
def self.local_ci_source(env)
|
10
|
+
CI.available_ci_sources.find { |ci| ci.validates_as_ci? env }
|
11
|
+
end
|
12
|
+
|
13
|
+
# Uses the current Danger::CI subclass, and sees if it is a PR
|
14
|
+
def self.pr?(env)
|
15
|
+
local_ci_source(env).validates_as_pr?(env)
|
16
|
+
end
|
17
|
+
|
18
|
+
# @return [String] danger's default head branch
|
19
|
+
def self.danger_head_branch
|
20
|
+
"danger_head".freeze
|
21
|
+
end
|
22
|
+
|
23
|
+
# @return [String] danger's default base branch
|
24
|
+
def self.danger_base_branch
|
25
|
+
"danger_base".freeze
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize(env, ui = nil, danger_id = "danger")
|
29
|
+
ci_klass = self.class.local_ci_source(env)
|
30
|
+
self.ci_source = ci_klass.new(env)
|
31
|
+
self.ui = ui || Cork::Board.new(silent: false, verbose: false)
|
32
|
+
self.danger_id = danger_id
|
33
|
+
|
34
|
+
RequestSources::RequestSource.available_request_sources.each do |klass|
|
35
|
+
next unless self.ci_source.supports?(klass)
|
36
|
+
|
37
|
+
request_source = klass.new(self.ci_source, env)
|
38
|
+
next unless request_source.validates_as_ci?
|
39
|
+
next unless request_source.validates_as_api_source?
|
40
|
+
self.request_source = request_source
|
41
|
+
end
|
42
|
+
|
43
|
+
raise_error_for_no_request_source(env, self.ui) unless self.request_source
|
44
|
+
self.scm = self.request_source.scm
|
45
|
+
end
|
46
|
+
|
47
|
+
def pr?
|
48
|
+
self.ci_source != nil
|
49
|
+
end
|
50
|
+
|
51
|
+
def fill_environment_vars
|
52
|
+
request_source.fetch_details
|
53
|
+
end
|
54
|
+
|
55
|
+
def ensure_danger_branches_are_setup
|
56
|
+
clean_up
|
57
|
+
|
58
|
+
self.request_source.setup_danger_branches
|
59
|
+
end
|
60
|
+
|
61
|
+
def clean_up
|
62
|
+
[EnvironmentManager.danger_base_branch, EnvironmentManager.danger_head_branch].each do |branch|
|
63
|
+
scm.exec("branch -D #{branch}") unless scm.exec("rev-parse --quiet --verify #{branch}").empty?
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def meta_info_for_head
|
68
|
+
scm.exec("--no-pager log #{EnvironmentManager.danger_head_branch} -n1")
|
69
|
+
end
|
70
|
+
|
71
|
+
def meta_info_for_base
|
72
|
+
scm.exec("--no-pager log #{EnvironmentManager.danger_base_branch} -n1")
|
73
|
+
end
|
74
|
+
|
75
|
+
def raise_error_for_no_request_source(env, ui)
|
76
|
+
title, subtitle = extract_title_and_subtitle_from_source(ci_source.repo_url)
|
77
|
+
subtitle += travis_note if env["TRAVIS_SECURE_ENV_VARS"] == "true"
|
78
|
+
|
79
|
+
ui_display_no_request_source_error_message(ui, env, title, subtitle)
|
80
|
+
|
81
|
+
exit(1)
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def get_repo_source(repo_url)
|
87
|
+
if repo_url =~ /github/i
|
88
|
+
RequestSources::GitHub
|
89
|
+
elsif repo_url =~ /gitlab/i
|
90
|
+
RequestSources::GitLab
|
91
|
+
elsif repo_url =~ /bitbucket\.(org|com)/i
|
92
|
+
RequestSources::BitbucketCloud
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def extract_title_and_subtitle_from_source(repo_url)
|
97
|
+
source = get_repo_source(repo_url)
|
98
|
+
|
99
|
+
if source
|
100
|
+
title = "For your #{source.source_name} repo, you need to expose: " + source.env_vars.join(", ").yellow
|
101
|
+
subtitle = "You may also need: #{source.optional_env_vars.join(', ')}" if source.optional_env_vars.any?
|
102
|
+
else
|
103
|
+
title = "For Danger to run on this project, you need to expose a set of following the ENV vars:\n#{RequestSources::RequestSource.available_source_names_and_envs.join("\n")}"
|
104
|
+
end
|
105
|
+
|
106
|
+
[title, (subtitle || "")]
|
107
|
+
end
|
108
|
+
|
109
|
+
def ui_display_no_request_source_error_message(ui, env, title, subtitle)
|
110
|
+
ui.title "Could not set up API to Code Review site for Danger\n".freeze
|
111
|
+
ui.puts title
|
112
|
+
ui.puts subtitle
|
113
|
+
ui.puts "\nFound these keys in your ENV: #{env.keys.join(', '.freeze)}."
|
114
|
+
ui.puts "\nFailing the build, Danger cannot run without API access.".freeze
|
115
|
+
ui.puts "You can see more information at https://danger.systems/guides/getting_started.html".freeze
|
116
|
+
end
|
117
|
+
|
118
|
+
def travis_note
|
119
|
+
"\nTravis note: If you have an open source project, you should ensure 'Display value in build log' enabled for these flags, so that PRs from forks work." \
|
120
|
+
"\nThis also means that people can see this token, so this account should have no write access to repos."
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
module Danger
|
2
|
+
class Executor
|
3
|
+
def initialize(system_env)
|
4
|
+
@system_env = system_env
|
5
|
+
end
|
6
|
+
|
7
|
+
def run(env: nil,
|
8
|
+
dm: nil,
|
9
|
+
cork: nil,
|
10
|
+
base: nil,
|
11
|
+
head: nil,
|
12
|
+
dangerfile_path: nil,
|
13
|
+
danger_id: nil,
|
14
|
+
new_comment: nil,
|
15
|
+
fail_on_errors: nil,
|
16
|
+
fail_if_no_pr: nil,
|
17
|
+
remove_previous_comments: nil)
|
18
|
+
# Create a silent Cork instance if cork is nil, as it's likely a test
|
19
|
+
cork ||= Cork::Board.new(silent: false, verbose: false)
|
20
|
+
|
21
|
+
# Run some validations
|
22
|
+
validate!(cork, fail_if_no_pr: fail_if_no_pr)
|
23
|
+
|
24
|
+
# OK, we now know that Danger can run in this environment
|
25
|
+
env ||= EnvironmentManager.new(system_env, cork, danger_id)
|
26
|
+
dm ||= Dangerfile.new(env, cork)
|
27
|
+
|
28
|
+
ran_status = begin
|
29
|
+
dm.run(
|
30
|
+
base_branch(base),
|
31
|
+
head_branch(head),
|
32
|
+
dangerfile_path,
|
33
|
+
danger_id,
|
34
|
+
new_comment,
|
35
|
+
remove_previous_comments
|
36
|
+
)
|
37
|
+
end
|
38
|
+
|
39
|
+
# By default Danger will use the status API to fail a build,
|
40
|
+
# allowing execution to continue, this behavior isn't always
|
41
|
+
# optimal for everyone.
|
42
|
+
exit(1) if fail_on_errors && ran_status
|
43
|
+
end
|
44
|
+
|
45
|
+
def validate!(cork, fail_if_no_pr: false)
|
46
|
+
validate_ci!
|
47
|
+
validate_pr!(cork, fail_if_no_pr)
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
attr_reader :system_env
|
53
|
+
|
54
|
+
# Could we find a CI source at all?
|
55
|
+
def validate_ci!
|
56
|
+
unless EnvironmentManager.local_ci_source(system_env)
|
57
|
+
abort("Could not find the type of CI for Danger to run on.".red)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Could we determine that the CI source is inside a PR?
|
62
|
+
def validate_pr!(cork, fail_if_no_pr)
|
63
|
+
unless EnvironmentManager.pr?(system_env)
|
64
|
+
ci_name = EnvironmentManager.local_ci_source(system_env).name.split("::").last
|
65
|
+
|
66
|
+
msg = "Not a #{ci_name} #{commit_request(ci_name)} - skipping `danger` run. "
|
67
|
+
# circle won't run danger properly if the commit is pushed and build runs before the PR exists
|
68
|
+
# https://danger.systems/guides/troubleshooting.html#circle-ci-doesnt-run-my-build-consistently
|
69
|
+
# the best solution is to enable `fail_if_no_pr`, and then re-run the job once the PR is up
|
70
|
+
if ci_name == "CircleCI"
|
71
|
+
msg << "If you only created the PR recently, try re-running your workflow."
|
72
|
+
end
|
73
|
+
cork.puts msg.strip.yellow
|
74
|
+
|
75
|
+
exit(fail_if_no_pr ? 1 : 0)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def base_branch(user_specified_base_branch)
|
80
|
+
user_specified_base_branch || EnvironmentManager.danger_base_branch
|
81
|
+
end
|
82
|
+
|
83
|
+
def head_branch(user_specified_head_branch)
|
84
|
+
user_specified_head_branch || EnvironmentManager.danger_head_branch
|
85
|
+
end
|
86
|
+
|
87
|
+
def commit_request(ci_name)
|
88
|
+
return "Merge Request" if ci_name == 'GitLabCI'
|
89
|
+
return "Pull Request"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "danger/danger_core/message_group"
|
3
|
+
require "danger/helpers/message_groups_array_helper"
|
4
|
+
|
5
|
+
module Danger
|
6
|
+
class MessageAggregator
|
7
|
+
def self.aggregate(*args)
|
8
|
+
new(*args).aggregate
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(warnings: [],
|
12
|
+
errors: [],
|
13
|
+
messages: [],
|
14
|
+
markdowns: [],
|
15
|
+
danger_id: "danger")
|
16
|
+
@messages = warnings + errors + messages + markdowns
|
17
|
+
@danger_id = danger_id
|
18
|
+
end
|
19
|
+
|
20
|
+
# aggregates the messages into an array of MessageGroups
|
21
|
+
# @return [[MessageGroup]]
|
22
|
+
def aggregate
|
23
|
+
# oookay I took some shortcuts with this one.
|
24
|
+
# first, sort messages by file and line
|
25
|
+
@messages.sort! { |a, b| a.compare_by_file_and_line(b) }
|
26
|
+
|
27
|
+
# now create an initial empty message group
|
28
|
+
first_group = MessageGroup.new(file: nil,
|
29
|
+
line: nil)
|
30
|
+
@message_groups = @messages.reduce([first_group]) do |groups, msg|
|
31
|
+
# We get to take a shortcut because we sorted the messages earlier - only
|
32
|
+
# have to see if we can append msg to the last group in the list
|
33
|
+
if groups.last << msg
|
34
|
+
# we appended it, so return groups unchanged
|
35
|
+
groups
|
36
|
+
else
|
37
|
+
# have to create a new group since msg wasn't appended to the other
|
38
|
+
# group
|
39
|
+
new_group = MessageGroup.new(file: msg.file,
|
40
|
+
line: msg.line)
|
41
|
+
new_group << msg
|
42
|
+
groups << new_group
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
@message_groups.extend(Helpers::MessageGroupsArrayHelper)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Danger
|
4
|
+
class MessageGroup
|
5
|
+
def initialize(file: nil, line: nil)
|
6
|
+
@file = file
|
7
|
+
@line = line
|
8
|
+
end
|
9
|
+
|
10
|
+
# Returns whether this `MessageGroup` is for the same line of code as
|
11
|
+
# `other`, taking which file they are in to account.
|
12
|
+
# @param other [MessageGroup, Markdown, Violation]
|
13
|
+
# @return [Boolean] whether this `MessageGroup` is for the same line of code
|
14
|
+
def same_line?(other)
|
15
|
+
other.file == file && other.line == line
|
16
|
+
end
|
17
|
+
|
18
|
+
# Merges two `MessageGroup`s that represent the same line of code
|
19
|
+
# In future, perhaps `MessageGroup` will be able to represent a group of
|
20
|
+
# messages for multiple lines.
|
21
|
+
def merge(other)
|
22
|
+
raise ArgumentError, "Cannot merge with MessageGroup for a different line" unless same_line?(other)
|
23
|
+
|
24
|
+
@messages = (messages + other.messages).uniq
|
25
|
+
end
|
26
|
+
|
27
|
+
# Adds a message to the group.
|
28
|
+
# @param message [Markdown, Violation] the message to add
|
29
|
+
def <<(message)
|
30
|
+
# TODO: insertion sort
|
31
|
+
return nil unless same_line?(message)
|
32
|
+
|
33
|
+
inserted = false
|
34
|
+
messages.each.with_index do |other, idx|
|
35
|
+
if (message <=> other) == -1
|
36
|
+
inserted = true
|
37
|
+
messages.insert(idx, message)
|
38
|
+
break
|
39
|
+
end
|
40
|
+
end
|
41
|
+
messages << message unless inserted
|
42
|
+
messages
|
43
|
+
end
|
44
|
+
|
45
|
+
# The list of messages in this group. This list will be sorted in decreasing
|
46
|
+
# order of severity (error, warning, message, markdown)
|
47
|
+
def messages
|
48
|
+
@messages ||= []
|
49
|
+
end
|
50
|
+
|
51
|
+
attr_reader :file, :line
|
52
|
+
|
53
|
+
# @return a hash of statistics. Currently only :warnings_count and
|
54
|
+
# :errors_count
|
55
|
+
def stats
|
56
|
+
stats = { warnings_count: 0, errors_count: 0 }
|
57
|
+
messages.each do |msg|
|
58
|
+
stats[:warnings_count] += 1 if msg.type == :warning
|
59
|
+
stats[:errors_count] += 1 if msg.type == :error
|
60
|
+
end
|
61
|
+
stats
|
62
|
+
end
|
63
|
+
|
64
|
+
def markdowns
|
65
|
+
messages.select { |x| x.type == :markdown }
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Danger
|
2
|
+
class BaseMessage
|
3
|
+
attr_accessor :message, :file, :line, :type
|
4
|
+
|
5
|
+
def initialize(type:, message:, file: nil, line: nil)
|
6
|
+
@type = type
|
7
|
+
@message = message
|
8
|
+
@file = file
|
9
|
+
@line = line
|
10
|
+
end
|
11
|
+
|
12
|
+
def compare_by_file_and_line(other)
|
13
|
+
order = cmp_nils(file, other.file)
|
14
|
+
return order unless order.nil?
|
15
|
+
|
16
|
+
order = file <=> other.file
|
17
|
+
return order unless order.zero?
|
18
|
+
|
19
|
+
order = cmp_nils(line, other.line)
|
20
|
+
return order unless order.nil?
|
21
|
+
|
22
|
+
line <=> other.line
|
23
|
+
end
|
24
|
+
# compares a and b based entirely on whether one or the other is nil
|
25
|
+
# arguments are in the same order as `a <=> b`
|
26
|
+
# nil is sorted earlier - so cmp_nils(nil, 1) => -1
|
27
|
+
#
|
28
|
+
# If neither are nil, rather than returning `a <=> b` which would seem
|
29
|
+
# like the obvious shortcut, `nil` is returned.
|
30
|
+
# This allows us to distinguish between cmp_nils returning 0 for a
|
31
|
+
# comparison of filenames, which means "a comparison on the lines is
|
32
|
+
# meaningless - you cannot have a line number for a nil file - so they
|
33
|
+
# should be sorted the same", and a <=> b returning 0, which means "the
|
34
|
+
# files are the same, so compare on the lines"
|
35
|
+
#
|
36
|
+
# @return 0, 1, -1, or nil
|
37
|
+
def cmp_nils(a, b)
|
38
|
+
if a.nil? && b.nil?
|
39
|
+
0
|
40
|
+
elsif a.nil?
|
41
|
+
-1
|
42
|
+
elsif b.nil?
|
43
|
+
1
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def eql?(other)
|
48
|
+
return self == other
|
49
|
+
end
|
50
|
+
|
51
|
+
# @return [Boolean] returns true if is a file or line, false otherwise
|
52
|
+
def inline?
|
53
|
+
file || line
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "danger/danger_core/messages/base"
|
3
|
+
|
4
|
+
module Danger
|
5
|
+
class Markdown < BaseMessage
|
6
|
+
|
7
|
+
def initialize(message, file = nil, line = nil)
|
8
|
+
super(type: :markdown, message: message, file: file, line: line)
|
9
|
+
end
|
10
|
+
|
11
|
+
def ==(other)
|
12
|
+
return false if other.nil?
|
13
|
+
return false unless other.kind_of? self.class
|
14
|
+
|
15
|
+
other.message == message &&
|
16
|
+
other.file == file &&
|
17
|
+
other.line == line
|
18
|
+
end
|
19
|
+
|
20
|
+
def hash
|
21
|
+
h = 1
|
22
|
+
h = h * 31 + message.hash
|
23
|
+
h = h * 17 + file.hash
|
24
|
+
h = h * 17 + line.hash
|
25
|
+
h
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_s
|
29
|
+
extra = []
|
30
|
+
extra << "file: #{file}" unless file
|
31
|
+
extra << "line: #{line}" unless line
|
32
|
+
|
33
|
+
"Markdown #{message} { #{extra.join ', '.freeze} }"
|
34
|
+
end
|
35
|
+
|
36
|
+
def <=>(other)
|
37
|
+
return 1 if other.type != :markdown
|
38
|
+
|
39
|
+
compare_by_file_and_line(other)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|