danger 8.0.4
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 +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 +161 -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
|