danger-additional-logging 0.0.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.
Files changed (127) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +22 -0
  3. data/README.md +93 -0
  4. data/bin/danger +5 -0
  5. data/lib/assets/DangerfileTemplate +13 -0
  6. data/lib/danger/ci_source/appcenter.rb +55 -0
  7. data/lib/danger/ci_source/appcircle.rb +83 -0
  8. data/lib/danger/ci_source/appveyor.rb +64 -0
  9. data/lib/danger/ci_source/azure_pipelines.rb +61 -0
  10. data/lib/danger/ci_source/bamboo.rb +41 -0
  11. data/lib/danger/ci_source/bitbucket_pipelines.rb +37 -0
  12. data/lib/danger/ci_source/bitrise.rb +78 -0
  13. data/lib/danger/ci_source/buddybuild.rb +62 -0
  14. data/lib/danger/ci_source/buildkite.rb +51 -0
  15. data/lib/danger/ci_source/ci_source.rb +37 -0
  16. data/lib/danger/ci_source/circle.rb +94 -0
  17. data/lib/danger/ci_source/circle_api.rb +51 -0
  18. data/lib/danger/ci_source/cirrus.rb +31 -0
  19. data/lib/danger/ci_source/code_build.rb +71 -0
  20. data/lib/danger/ci_source/codefresh.rb +47 -0
  21. data/lib/danger/ci_source/codemagic.rb +58 -0
  22. data/lib/danger/ci_source/codeship.rb +44 -0
  23. data/lib/danger/ci_source/concourse.rb +60 -0
  24. data/lib/danger/ci_source/custom_ci_with_github.rb +49 -0
  25. data/lib/danger/ci_source/dotci.rb +50 -0
  26. data/lib/danger/ci_source/drone.rb +71 -0
  27. data/lib/danger/ci_source/github_actions.rb +44 -0
  28. data/lib/danger/ci_source/gitlab_ci.rb +89 -0
  29. data/lib/danger/ci_source/jenkins.rb +148 -0
  30. data/lib/danger/ci_source/local_git_repo.rb +117 -0
  31. data/lib/danger/ci_source/local_only_git_repo.rb +44 -0
  32. data/lib/danger/ci_source/screwdriver.rb +48 -0
  33. data/lib/danger/ci_source/semaphore.rb +37 -0
  34. data/lib/danger/ci_source/support/commits.rb +19 -0
  35. data/lib/danger/ci_source/support/find_repo_info_from_logs.rb +35 -0
  36. data/lib/danger/ci_source/support/find_repo_info_from_url.rb +43 -0
  37. data/lib/danger/ci_source/support/local_pull_request.rb +14 -0
  38. data/lib/danger/ci_source/support/no_pull_request.rb +7 -0
  39. data/lib/danger/ci_source/support/no_repo_info.rb +5 -0
  40. data/lib/danger/ci_source/support/pull_request_finder.rb +190 -0
  41. data/lib/danger/ci_source/support/remote_pull_request.rb +15 -0
  42. data/lib/danger/ci_source/support/repo_info.rb +10 -0
  43. data/lib/danger/ci_source/surf.rb +37 -0
  44. data/lib/danger/ci_source/teamcity.rb +163 -0
  45. data/lib/danger/ci_source/travis.rb +51 -0
  46. data/lib/danger/ci_source/xcode_cloud.rb +38 -0
  47. data/lib/danger/ci_source/xcode_server.rb +48 -0
  48. data/lib/danger/clients/rubygems_client.rb +14 -0
  49. data/lib/danger/commands/dangerfile/gem.rb +43 -0
  50. data/lib/danger/commands/dangerfile/init.rb +30 -0
  51. data/lib/danger/commands/dry_run.rb +54 -0
  52. data/lib/danger/commands/init.rb +297 -0
  53. data/lib/danger/commands/init_helpers/interviewer.rb +92 -0
  54. data/lib/danger/commands/local.rb +83 -0
  55. data/lib/danger/commands/local_helpers/http_cache.rb +38 -0
  56. data/lib/danger/commands/local_helpers/local_setup.rb +48 -0
  57. data/lib/danger/commands/local_helpers/pry_setup.rb +32 -0
  58. data/lib/danger/commands/plugins/plugin_json.rb +44 -0
  59. data/lib/danger/commands/plugins/plugin_lint.rb +52 -0
  60. data/lib/danger/commands/plugins/plugin_readme.rb +42 -0
  61. data/lib/danger/commands/pr.rb +93 -0
  62. data/lib/danger/commands/runner.rb +94 -0
  63. data/lib/danger/commands/staging.rb +53 -0
  64. data/lib/danger/commands/systems.rb +41 -0
  65. data/lib/danger/comment_generators/bitbucket_server.md.erb +20 -0
  66. data/lib/danger/comment_generators/bitbucket_server_inline.md.erb +15 -0
  67. data/lib/danger/comment_generators/bitbucket_server_message_group.md.erb +12 -0
  68. data/lib/danger/comment_generators/github.md.erb +55 -0
  69. data/lib/danger/comment_generators/github_inline.md.erb +26 -0
  70. data/lib/danger/comment_generators/gitlab.md.erb +40 -0
  71. data/lib/danger/comment_generators/gitlab_inline.md.erb +21 -0
  72. data/lib/danger/comment_generators/vsts.md.erb +20 -0
  73. data/lib/danger/comment_generators/vsts_inline.md.erb +17 -0
  74. data/lib/danger/core_ext/file_list.rb +18 -0
  75. data/lib/danger/core_ext/string.rb +20 -0
  76. data/lib/danger/danger_core/dangerfile.rb +348 -0
  77. data/lib/danger/danger_core/dangerfile_dsl.rb +29 -0
  78. data/lib/danger/danger_core/dangerfile_generator.rb +11 -0
  79. data/lib/danger/danger_core/environment_manager.rb +126 -0
  80. data/lib/danger/danger_core/executor.rb +91 -0
  81. data/lib/danger/danger_core/message_aggregator.rb +50 -0
  82. data/lib/danger/danger_core/message_group.rb +68 -0
  83. data/lib/danger/danger_core/messages/base.rb +57 -0
  84. data/lib/danger/danger_core/messages/markdown.rb +41 -0
  85. data/lib/danger/danger_core/messages/violation.rb +53 -0
  86. data/lib/danger/danger_core/plugins/dangerfile_bitbucket_cloud_plugin.rb +142 -0
  87. data/lib/danger/danger_core/plugins/dangerfile_bitbucket_server_plugin.rb +211 -0
  88. data/lib/danger/danger_core/plugins/dangerfile_danger_plugin.rb +274 -0
  89. data/lib/danger/danger_core/plugins/dangerfile_git_plugin.rb +159 -0
  90. data/lib/danger/danger_core/plugins/dangerfile_github_plugin.rb +264 -0
  91. data/lib/danger/danger_core/plugins/dangerfile_gitlab_plugin.rb +275 -0
  92. data/lib/danger/danger_core/plugins/dangerfile_local_only_plugin.rb +43 -0
  93. data/lib/danger/danger_core/plugins/dangerfile_messaging_plugin.rb +220 -0
  94. data/lib/danger/danger_core/plugins/dangerfile_vsts_plugin.rb +191 -0
  95. data/lib/danger/danger_core/standard_error.rb +142 -0
  96. data/lib/danger/helpers/array_subclass.rb +61 -0
  97. data/lib/danger/helpers/comment.rb +32 -0
  98. data/lib/danger/helpers/comments_helper.rb +179 -0
  99. data/lib/danger/helpers/comments_parsing_helper.rb +71 -0
  100. data/lib/danger/helpers/emoji_mapper.rb +41 -0
  101. data/lib/danger/helpers/find_max_num_violations.rb +31 -0
  102. data/lib/danger/helpers/message_groups_array_helper.rb +31 -0
  103. data/lib/danger/plugin_support/gems_resolver.rb +77 -0
  104. data/lib/danger/plugin_support/plugin.rb +52 -0
  105. data/lib/danger/plugin_support/plugin_file_resolver.rb +30 -0
  106. data/lib/danger/plugin_support/plugin_linter.rb +162 -0
  107. data/lib/danger/plugin_support/plugin_parser.rb +199 -0
  108. data/lib/danger/plugin_support/templates/readme_table.html.erb +26 -0
  109. data/lib/danger/request_sources/bitbucket_cloud.rb +169 -0
  110. data/lib/danger/request_sources/bitbucket_cloud_api.rb +181 -0
  111. data/lib/danger/request_sources/bitbucket_server.rb +210 -0
  112. data/lib/danger/request_sources/bitbucket_server_api.rb +129 -0
  113. data/lib/danger/request_sources/code_insights_api.rb +142 -0
  114. data/lib/danger/request_sources/github/github.rb +535 -0
  115. data/lib/danger/request_sources/github/github_review.rb +127 -0
  116. data/lib/danger/request_sources/github/github_review_resolver.rb +17 -0
  117. data/lib/danger/request_sources/github/github_review_unsupported.rb +23 -0
  118. data/lib/danger/request_sources/gitlab.rb +557 -0
  119. data/lib/danger/request_sources/local_only.rb +50 -0
  120. data/lib/danger/request_sources/request_source.rb +97 -0
  121. data/lib/danger/request_sources/support/get_ignored_violation.rb +17 -0
  122. data/lib/danger/request_sources/vsts.rb +278 -0
  123. data/lib/danger/request_sources/vsts_api.rb +172 -0
  124. data/lib/danger/scm_source/git_repo.rb +198 -0
  125. data/lib/danger/version.rb +4 -0
  126. data/lib/danger.rb +45 -0
  127. metadata +351 -0
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "danger/danger_core/message_group"
4
+ require "danger/helpers/message_groups_array_helper"
5
+
6
+ module Danger
7
+ class MessageAggregator
8
+ def self.aggregate(*args)
9
+ new(*args).aggregate
10
+ end
11
+
12
+ def initialize(warnings: [],
13
+ errors: [],
14
+ messages: [],
15
+ markdowns: [],
16
+ danger_id: "danger")
17
+ @messages = warnings + errors + messages + markdowns
18
+ @danger_id = danger_id
19
+ end
20
+
21
+ # aggregates the messages into an array of MessageGroups
22
+ # @return [[MessageGroup]]
23
+ def aggregate
24
+ # oookay I took some shortcuts with this one.
25
+ # first, sort messages by file and line
26
+ @messages.sort! { |a, b| a.compare_by_file_and_line(b) }
27
+
28
+ # now create an initial empty message group
29
+ first_group = MessageGroup.new(file: nil,
30
+ line: nil)
31
+ @message_groups = @messages.reduce([first_group]) do |groups, msg|
32
+ # We get to take a shortcut because we sorted the messages earlier - only
33
+ # have to see if we can append msg to the last group in the list
34
+ if groups.last << msg
35
+ # we appended it, so return groups unchanged
36
+ groups
37
+ else
38
+ # have to create a new group since msg wasn't appended to the other
39
+ # group
40
+ new_group = MessageGroup.new(file: msg.file,
41
+ line: msg.line)
42
+ new_group << msg
43
+ groups << new_group
44
+ end
45
+ end
46
+
47
+ @message_groups.extend(Helpers::MessageGroupsArrayHelper)
48
+ end
49
+ end
50
+ 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,57 @@
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
+
25
+ # compares a and b based entirely on whether one or the other is nil
26
+ # arguments are in the same order as `a <=> b`
27
+ # nil is sorted earlier - so cmp_nils(nil, 1) => -1
28
+ #
29
+ # If neither are nil, rather than returning `a <=> b` which would seem
30
+ # like the obvious shortcut, `nil` is returned.
31
+ # This allows us to distinguish between cmp_nils returning 0 for a
32
+ # comparison of filenames, which means "a comparison on the lines is
33
+ # meaningless - you cannot have a line number for a nil file - so they
34
+ # should be sorted the same", and a <=> b returning 0, which means "the
35
+ # files are the same, so compare on the lines"
36
+ #
37
+ # @return 0, 1, -1, or nil
38
+ def cmp_nils(a, b)
39
+ if a.nil? && b.nil?
40
+ 0
41
+ elsif a.nil?
42
+ -1
43
+ elsif b.nil?
44
+ 1
45
+ end
46
+ end
47
+
48
+ def eql?(other)
49
+ return self == other
50
+ end
51
+
52
+ # @return [Boolean] returns true if is a file or line, false otherwise
53
+ def inline?
54
+ file || line
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "danger/danger_core/messages/base"
4
+
5
+ module Danger
6
+ class Markdown < BaseMessage
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 * 17 + line.hash
25
+ end
26
+
27
+ def to_s
28
+ extra = []
29
+ extra << "file: #{file}" unless file
30
+ extra << "line: #{line}" unless line
31
+
32
+ "Markdown #{message} { #{extra.join ', '} }"
33
+ end
34
+
35
+ def <=>(other)
36
+ return 1 if other.type != :markdown
37
+
38
+ compare_by_file_and_line(other)
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "danger/danger_core/messages/base"
4
+
5
+ module Danger
6
+ class Violation < BaseMessage
7
+ VALID_TYPES = %I[error warning message].freeze
8
+ attr_accessor :sticky
9
+
10
+ def initialize(message, sticky, file = nil, line = nil, type: :warning)
11
+ raise ArgumentError unless VALID_TYPES.include?(type)
12
+
13
+ super(type: type, message: message, file: file, line: line)
14
+ self.sticky = sticky
15
+ end
16
+
17
+ def ==(other)
18
+ return false if other.nil?
19
+ return false unless other.kind_of? self.class
20
+
21
+ other.message == message &&
22
+ other.sticky == sticky &&
23
+ other.file == file &&
24
+ other.line == line
25
+ end
26
+
27
+ def hash
28
+ h = 1
29
+ h = h * 31 + message.hash
30
+ h = h * 13 + sticky.hash
31
+ h = h * 17 + file.hash
32
+ h * 17 + line.hash
33
+ end
34
+
35
+ def <=>(other)
36
+ types = VALID_TYPES + [:markdown]
37
+ order = types.index(type) <=> types.index(other.type)
38
+ return order unless order.zero?
39
+
40
+ compare_by_file_and_line(other)
41
+ end
42
+
43
+ def to_s
44
+ extra = []
45
+ extra << "sticky: #{sticky}"
46
+ extra << "file: #{file}" if file
47
+ extra << "line: #{line}" if line
48
+ extra << "type: #{type}"
49
+
50
+ "Violation #{message} { #{extra.join ', '} }"
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,142 @@
1
+ require "danger/plugin_support/plugin"
2
+
3
+ module Danger
4
+ # Handles interacting with Bitbucket Cloud inside a Dangerfile. Provides a few functions which wrap `pr_json` and also
5
+ # through a few standard functions to simplify your code.
6
+ #
7
+ # @example Warn when a PR is classed as work in progress
8
+ #
9
+ # warn "PR is classed as Work in Progress" if bitbucket_cloud.pr_title.include? "[WIP]"
10
+ #
11
+ # @example Declare a PR to be simple to avoid specific Danger rules
12
+ #
13
+ # declared_trivial = (bitbucket_cloud.pr_title + bitbucket_cloud.pr_body).include?("#trivial")
14
+ #
15
+ # @example Ensure that labels have been used on the PR
16
+ #
17
+ # failure "Please add labels to this PR" if bitbucket_cloud.pr_labels.empty?
18
+ #
19
+ # @example Ensure there is a summary for a PR
20
+ #
21
+ # failure "Please provide a summary in the Pull Request description" if bitbucket_cloud.pr_body.length < 5
22
+ #
23
+ # @example Only accept PRs to the develop branch
24
+ #
25
+ # failure "Please re-submit this PR to develop, we may have already fixed your issue." if bitbucket_cloud.branch_for_base != "develop"
26
+ #
27
+ # @example Highlight when a celebrity makes a pull request
28
+ #
29
+ # message "Welcome, Danger." if bitbucket_cloud.pr_author == "dangermcshane"
30
+ #
31
+ # @example Ensure that all PRs have an assignee
32
+ #
33
+ # warn "This PR does not have any assignees yet." if bitbucket_cloud.pr_json[:reviewers].length == 0
34
+ #
35
+ # @example Send a message with links to a collection of specific files
36
+ #
37
+ # if git.modified_files.include? "config/*.js"
38
+ # config_files = git.modified_files.select { |path| path.include? "config/" }
39
+ # message "This PR changes #{ bitbucket_cloud.html_link(config_files) }"
40
+ # end
41
+ #
42
+ # @example Highlight with a clickable link if a Package.json is changed
43
+ #
44
+ # warn "#{bitbucket_cloud.html_link("Package.json")} was edited." if git.modified_files.include? "Package.json"
45
+ #
46
+ # @see danger/danger
47
+ # @tags core, bitbucket_cloud
48
+ #
49
+ class DangerfileBitbucketCloudPlugin < Plugin
50
+ # So that this init can fail.
51
+ def self.new(dangerfile)
52
+ return nil if dangerfile.env.request_source.class != Danger::RequestSources::BitbucketCloud
53
+
54
+ super
55
+ end
56
+
57
+ # The instance name used in the Dangerfile
58
+ # @return [String]
59
+ #
60
+ def self.instance_name
61
+ "bitbucket_cloud"
62
+ end
63
+
64
+ def initialize(dangerfile)
65
+ super(dangerfile)
66
+ @bs = dangerfile.env.request_source
67
+ end
68
+
69
+ # @!group Bitbucket Cloud Misc
70
+ # The hash that represents the PR's JSON. For an example of what this looks like
71
+ # see the [Danger Fixture'd one](https://raw.githubusercontent.com/danger/danger/master/spec/fixtures/bitbucket_cloud_api/pr_response.json).
72
+ # @return [Hash]
73
+ def pr_json
74
+ @bs.pr_json
75
+ end
76
+
77
+ # @!group PR Metadata
78
+ # The title of the Pull Request.
79
+ # @return [String]
80
+ #
81
+ def pr_title
82
+ @bs.pr_json[:title].to_s
83
+ end
84
+
85
+ # @!group PR Metadata
86
+ # The body text of the Pull Request.
87
+ # @return [String]
88
+ #
89
+ def pr_description
90
+ @bs.pr_json[:description].to_s
91
+ end
92
+ alias pr_body pr_description
93
+
94
+ # @!group PR Metadata
95
+ # The username of the author of the Pull Request.
96
+ # @return [String]
97
+ #
98
+ def pr_author
99
+ @bs.pr_json[:author][:nickname]
100
+ end
101
+
102
+ # @!group PR Commit Metadata
103
+ # The branch to which the PR is going to be merged into.
104
+ # @return [String]
105
+ #
106
+ def branch_for_base
107
+ @bs.pr_json[:destination][:branch][:name]
108
+ end
109
+
110
+ # @!group PR Commit Metadata
111
+ # A href that represents the current PR
112
+ # @return [String]
113
+ #
114
+ def pr_link
115
+ @bs.pr_json[:links][:self][:href]
116
+ end
117
+
118
+ # @!group PR Commit Metadata
119
+ # The branch to which the PR is going to be merged from.
120
+ # @return [String]
121
+ #
122
+ def branch_for_head
123
+ @bs.pr_json[:source][:branch][:name]
124
+ end
125
+
126
+ # @!group PR Commit Metadata
127
+ # The base commit to which the PR is going to be merged as a parent.
128
+ # @return [String]
129
+ #
130
+ def base_commit
131
+ @bs.pr_json[:destination][:commit][:hash]
132
+ end
133
+
134
+ # @!group PR Commit Metadata
135
+ # The head commit to which the PR is requesting to be merged from.
136
+ # @return [String]
137
+ #
138
+ def head_commit
139
+ @bs.pr_json[:source][:commit][:hash]
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,211 @@
1
+ require "danger/plugin_support/plugin"
2
+
3
+ module Danger
4
+ # Handles interacting with Bitbucket Server inside a Dangerfile. Provides a few functions which wrap `pr_json` and also
5
+ # through a few standard functions to simplify your code.
6
+ #
7
+ # @example Warn when a PR is classed as work in progress
8
+ #
9
+ # warn "PR is classed as Work in Progress" if bitbucket_server.pr_title.include? "[WIP]"
10
+ #
11
+ # @example Declare a PR to be simple to avoid specific Danger rules
12
+ #
13
+ # declared_trivial = (bitbucket_server.pr_title + bitbucket_server.pr_body).include?("#trivial")
14
+ #
15
+ # @example Ensure that labels have been used on the PR
16
+ #
17
+ # failure "Please add labels to this PR" if bitbucket_server.pr_labels.empty?
18
+ #
19
+ # @example Ensure there is a summary for a PR
20
+ #
21
+ # failure "Please provide a summary in the Pull Request description" if bitbucket_server.pr_body.length < 5
22
+ #
23
+ # @example Only accept PRs to the develop branch
24
+ #
25
+ # failure "Please re-submit this PR to develop, we may have already fixed your issue." if bitbucket_server.branch_for_base != "develop"
26
+ #
27
+ # @example Highlight when a celebrity makes a pull request
28
+ #
29
+ # message "Welcome, Danger." if bitbucket_server.pr_author == "dangermcshane"
30
+ #
31
+ # @example Ensure that all PRs have an assignee
32
+ #
33
+ # warn "This PR does not have any assignees yet." if bitbucket_server.pr_json[:reviewers].length == 0
34
+ #
35
+ # @example Send a message with links to a collection of specific files
36
+ #
37
+ # if git.modified_files.include? "config/*.js"
38
+ # config_files = git.modified_files.select { |path| path.include? "config/" }
39
+ # message "This PR changes #{ bitbucket_server.html_link(config_files) }"
40
+ # end
41
+ #
42
+ # @example Highlight with a clickable link if a Package.json is changed
43
+ #
44
+ # warn "#{bitbucket_server.html_link("Package.json")} was edited." if git.modified_files.include? "Package.json"
45
+ #
46
+ # @see danger/danger
47
+ # @tags core, bitbucket_server
48
+ #
49
+ class DangerfileBitbucketServerPlugin < Plugin
50
+ # So that this init can fail.
51
+ def self.new(dangerfile)
52
+ return nil if dangerfile.env.request_source.class != Danger::RequestSources::BitbucketServer
53
+
54
+ super
55
+ end
56
+
57
+ # The instance name used in the Dangerfile
58
+ # @return [String]
59
+ #
60
+ def self.instance_name
61
+ "bitbucket_server"
62
+ end
63
+
64
+ def initialize(dangerfile)
65
+ super(dangerfile)
66
+ @bs = dangerfile.env.request_source
67
+ end
68
+
69
+ # @!group Bitbucket Server Misc
70
+ # The hash that represents the PR's JSON. For an example of what this looks like
71
+ # see the [Danger Fixture'd one](https://raw.githubusercontent.com/danger/danger/master/spec/fixtures/bitbucket_server_api/pr_response.json).
72
+ # @return [Hash]
73
+ def pr_json
74
+ @bs.pr_json
75
+ end
76
+
77
+ # @!group PR Metadata
78
+ # The title of the Pull Request.
79
+ # @return [String]
80
+ #
81
+ def pr_title
82
+ @bs.pr_json[:title].to_s
83
+ end
84
+
85
+ # @!group PR Metadata
86
+ # The body text of the Pull Request.
87
+ # @return [String]
88
+ #
89
+ def pr_description
90
+ @bs.pr_json[:description].to_s
91
+ end
92
+ alias pr_body pr_description
93
+
94
+ # @!group PR Metadata
95
+ # The username of the author of the Pull Request.
96
+ # @return [String]
97
+ #
98
+ def pr_author
99
+ @bs.pr_json[:author][:user][:slug].to_s
100
+ end
101
+
102
+ # @!group PR Commit Metadata
103
+ # The branch to which the PR is going to be merged into.
104
+ # @return [String]
105
+ #
106
+ def branch_for_base
107
+ @bs.pr_json[:toRef][:displayId].to_s
108
+ end
109
+
110
+ # @!group PR Commit Metadata
111
+ # A href that represents the current PR
112
+ # @return [String]
113
+ #
114
+ def pr_link
115
+ @bs.pr_json[:links][:self].flat_map { |l| l[:href] }.first.to_s
116
+ end
117
+
118
+ # @!group PR Commit Metadata
119
+ # The branch to which the PR is going to be merged from.
120
+ # @return [String]
121
+ #
122
+ def branch_for_head
123
+ @bs.pr_json[:fromRef][:displayId].to_s
124
+ end
125
+
126
+ # @!group PR Commit Metadata
127
+ # The base commit to which the PR is going to be merged as a parent.
128
+ # @return [String]
129
+ #
130
+ def base_commit
131
+ @bs.pr_json[:toRef][:latestCommit].to_s
132
+ end
133
+
134
+ # @!group PR Commit Metadata
135
+ # The head commit to which the PR is requesting to be merged from.
136
+ # @return [String]
137
+ #
138
+ def head_commit
139
+ @bs.pr_json[:fromRef][:latestCommit].to_s
140
+ end
141
+
142
+ # @!group Bitbucket Server Misc
143
+ # Returns a list of Markdown links for a file, or files in the head repository.
144
+ # It returns a string of multiple anchors if passed an array.
145
+ # @note Atlassian [disabled inline HTML support](https://jira.atlassian.com/browse/BSERV-7147).
146
+ # This method method left for backward compatibility.
147
+ # @param [String or Array<String>] paths
148
+ # A list of strings to convert to github anchors
149
+ # @param [Bool] full_path
150
+ # Shows the full path as the link's text, defaults to `true`.
151
+ #
152
+ # @return [String]
153
+ #
154
+ def html_link(paths, full_path: true)
155
+ markdown_link(paths, full_path: full_path)
156
+ end
157
+
158
+ # @!group Bitbucket Server Misc
159
+ # Returns a list of Markdown links for a file, or files in the head repository.
160
+ # It returns a string of multiple links if passed an array.
161
+ # @param [String or Array<String>] paths
162
+ # A list of strings to convert to Markdown links
163
+ # @param [Bool] full_path
164
+ # Shows the full path as the link's text, defaults to `true`.
165
+ #
166
+ # @return [String]
167
+ #
168
+ def markdown_link(paths, full_path: true)
169
+ create_link(paths, full_path) { |href, text| create_markdown_link(href, text) }
170
+ end
171
+
172
+ # @!group Bitbucket Server Misc
173
+ # Updates the PR with build status and build server job link.
174
+ # @param [String] status
175
+ # SUCCESSFUL, FAILED and INPROGRESS
176
+ # @param [String] build_job_link
177
+ # Build server job link
178
+ # @param [String] description
179
+ # Build status description
180
+ # @return [String]
181
+ #
182
+ def update_pr_build_status(status, build_job_link, description)
183
+ @bs.update_pr_build_status(status, build_job_link, description)
184
+ end
185
+
186
+ private
187
+
188
+ def create_link(paths, full_path)
189
+ paths = [paths] unless paths.kind_of?(Array)
190
+ commit = head_commit
191
+ repo = pr_json[:fromRef][:repository][:links][:self].flat_map { |l| l[:href] }.first
192
+
193
+ paths = paths.map do |path|
194
+ path, line = path.split("#")
195
+ url_path = path.start_with?("/") ? path : "/#{path}"
196
+ text = full_path ? path : File.basename(path)
197
+ url_path.gsub!(" ", "%20")
198
+ line_ref = line ? "##{line}" : ""
199
+ yield("#{repo}#{url_path}?at=#{commit}#{line_ref}", text)
200
+ end
201
+
202
+ return paths.first if paths.count < 2
203
+
204
+ paths.first(paths.count - 1).join(", ") + " & " + paths.last
205
+ end
206
+
207
+ def create_markdown_link(href, text)
208
+ "[#{text}](#{href})"
209
+ end
210
+ end
211
+ end