danger 8.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (121) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +22 -0
  3. data/README.md +94 -0
  4. data/bin/danger +5 -0
  5. data/lib/assets/DangerfileTemplate +13 -0
  6. data/lib/danger.rb +44 -0
  7. data/lib/danger/ci_source/appcenter.rb +55 -0
  8. data/lib/danger/ci_source/appveyor.rb +60 -0
  9. data/lib/danger/ci_source/azure_pipelines.rb +44 -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 +65 -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 +57 -0
  20. data/lib/danger/ci_source/codefresh.rb +53 -0
  21. data/lib/danger/ci_source/codeship.rb +44 -0
  22. data/lib/danger/ci_source/dotci.rb +52 -0
  23. data/lib/danger/ci_source/drone.rb +71 -0
  24. data/lib/danger/ci_source/github_actions.rb +43 -0
  25. data/lib/danger/ci_source/gitlab_ci.rb +86 -0
  26. data/lib/danger/ci_source/jenkins.rb +149 -0
  27. data/lib/danger/ci_source/local_git_repo.rb +119 -0
  28. data/lib/danger/ci_source/local_only_git_repo.rb +47 -0
  29. data/lib/danger/ci_source/screwdriver.rb +47 -0
  30. data/lib/danger/ci_source/semaphore.rb +37 -0
  31. data/lib/danger/ci_source/support/commits.rb +17 -0
  32. data/lib/danger/ci_source/support/find_repo_info_from_logs.rb +35 -0
  33. data/lib/danger/ci_source/support/find_repo_info_from_url.rb +42 -0
  34. data/lib/danger/ci_source/support/local_pull_request.rb +14 -0
  35. data/lib/danger/ci_source/support/no_pull_request.rb +7 -0
  36. data/lib/danger/ci_source/support/no_repo_info.rb +5 -0
  37. data/lib/danger/ci_source/support/pull_request_finder.rb +179 -0
  38. data/lib/danger/ci_source/support/remote_pull_request.rb +15 -0
  39. data/lib/danger/ci_source/support/repo_info.rb +10 -0
  40. data/lib/danger/ci_source/surf.rb +37 -0
  41. data/lib/danger/ci_source/teamcity.rb +161 -0
  42. data/lib/danger/ci_source/travis.rb +51 -0
  43. data/lib/danger/ci_source/vsts.rb +73 -0
  44. data/lib/danger/ci_source/xcode_server.rb +48 -0
  45. data/lib/danger/clients/rubygems_client.rb +14 -0
  46. data/lib/danger/commands/dangerfile/gem.rb +43 -0
  47. data/lib/danger/commands/dangerfile/init.rb +30 -0
  48. data/lib/danger/commands/dry_run.rb +54 -0
  49. data/lib/danger/commands/init.rb +297 -0
  50. data/lib/danger/commands/init_helpers/interviewer.rb +92 -0
  51. data/lib/danger/commands/local.rb +83 -0
  52. data/lib/danger/commands/local_helpers/http_cache.rb +36 -0
  53. data/lib/danger/commands/local_helpers/local_setup.rb +46 -0
  54. data/lib/danger/commands/local_helpers/pry_setup.rb +31 -0
  55. data/lib/danger/commands/plugins/plugin_json.rb +46 -0
  56. data/lib/danger/commands/plugins/plugin_lint.rb +54 -0
  57. data/lib/danger/commands/plugins/plugin_readme.rb +45 -0
  58. data/lib/danger/commands/pr.rb +92 -0
  59. data/lib/danger/commands/runner.rb +94 -0
  60. data/lib/danger/commands/staging.rb +53 -0
  61. data/lib/danger/commands/systems.rb +43 -0
  62. data/lib/danger/comment_generators/bitbucket_server.md.erb +20 -0
  63. data/lib/danger/comment_generators/bitbucket_server_inline.md.erb +15 -0
  64. data/lib/danger/comment_generators/bitbucket_server_message_group.md.erb +12 -0
  65. data/lib/danger/comment_generators/github.md.erb +55 -0
  66. data/lib/danger/comment_generators/github_inline.md.erb +26 -0
  67. data/lib/danger/comment_generators/gitlab.md.erb +40 -0
  68. data/lib/danger/comment_generators/gitlab_inline.md.erb +26 -0
  69. data/lib/danger/comment_generators/vsts.md.erb +20 -0
  70. data/lib/danger/core_ext/file_list.rb +18 -0
  71. data/lib/danger/core_ext/string.rb +20 -0
  72. data/lib/danger/danger_core/dangerfile.rb +341 -0
  73. data/lib/danger/danger_core/dangerfile_dsl.rb +29 -0
  74. data/lib/danger/danger_core/dangerfile_generator.rb +11 -0
  75. data/lib/danger/danger_core/environment_manager.rb +123 -0
  76. data/lib/danger/danger_core/executor.rb +92 -0
  77. data/lib/danger/danger_core/message_aggregator.rb +49 -0
  78. data/lib/danger/danger_core/message_group.rb +68 -0
  79. data/lib/danger/danger_core/messages/base.rb +56 -0
  80. data/lib/danger/danger_core/messages/markdown.rb +42 -0
  81. data/lib/danger/danger_core/messages/violation.rb +54 -0
  82. data/lib/danger/danger_core/plugins/dangerfile_bitbucket_cloud_plugin.rb +144 -0
  83. data/lib/danger/danger_core/plugins/dangerfile_bitbucket_server_plugin.rb +211 -0
  84. data/lib/danger/danger_core/plugins/dangerfile_danger_plugin.rb +248 -0
  85. data/lib/danger/danger_core/plugins/dangerfile_git_plugin.rb +158 -0
  86. data/lib/danger/danger_core/plugins/dangerfile_github_plugin.rb +254 -0
  87. data/lib/danger/danger_core/plugins/dangerfile_gitlab_plugin.rb +240 -0
  88. data/lib/danger/danger_core/plugins/dangerfile_local_only_plugin.rb +42 -0
  89. data/lib/danger/danger_core/plugins/dangerfile_messaging_plugin.rb +218 -0
  90. data/lib/danger/danger_core/plugins/dangerfile_vsts_plugin.rb +191 -0
  91. data/lib/danger/danger_core/standard_error.rb +143 -0
  92. data/lib/danger/helpers/array_subclass.rb +61 -0
  93. data/lib/danger/helpers/comment.rb +32 -0
  94. data/lib/danger/helpers/comments_helper.rb +178 -0
  95. data/lib/danger/helpers/comments_parsing_helper.rb +70 -0
  96. data/lib/danger/helpers/emoji_mapper.rb +41 -0
  97. data/lib/danger/helpers/find_max_num_violations.rb +31 -0
  98. data/lib/danger/helpers/message_groups_array_helper.rb +31 -0
  99. data/lib/danger/plugin_support/gems_resolver.rb +77 -0
  100. data/lib/danger/plugin_support/plugin.rb +49 -0
  101. data/lib/danger/plugin_support/plugin_file_resolver.rb +30 -0
  102. data/lib/danger/plugin_support/plugin_linter.rb +161 -0
  103. data/lib/danger/plugin_support/plugin_parser.rb +199 -0
  104. data/lib/danger/plugin_support/templates/readme_table.html.erb +26 -0
  105. data/lib/danger/request_sources/bitbucket_cloud.rb +171 -0
  106. data/lib/danger/request_sources/bitbucket_cloud_api.rb +181 -0
  107. data/lib/danger/request_sources/bitbucket_server.rb +105 -0
  108. data/lib/danger/request_sources/bitbucket_server_api.rb +117 -0
  109. data/lib/danger/request_sources/github/github.rb +530 -0
  110. data/lib/danger/request_sources/github/github_review.rb +126 -0
  111. data/lib/danger/request_sources/github/github_review_resolver.rb +19 -0
  112. data/lib/danger/request_sources/github/github_review_unsupported.rb +25 -0
  113. data/lib/danger/request_sources/gitlab.rb +525 -0
  114. data/lib/danger/request_sources/local_only.rb +53 -0
  115. data/lib/danger/request_sources/request_source.rb +85 -0
  116. data/lib/danger/request_sources/support/get_ignored_violation.rb +17 -0
  117. data/lib/danger/request_sources/vsts.rb +118 -0
  118. data/lib/danger/request_sources/vsts_api.rb +138 -0
  119. data/lib/danger/scm_source/git_repo.rb +181 -0
  120. data/lib/danger/version.rb +4 -0
  121. metadata +339 -0
@@ -0,0 +1,158 @@
1
+ require "danger/plugin_support/plugin"
2
+ require "danger/core_ext/file_list"
3
+
4
+ # Danger
5
+ module Danger
6
+ # Handles interacting with git inside a Dangerfile. Providing access to files that have changed, and useful statistics. Also provides
7
+ # access to the commits in the form of [Git::Log](https://github.com/schacon/ruby-git/blob/master/lib/git/log.rb) objects.
8
+ #
9
+ # @example Do something to all new and edited markdown files
10
+ #
11
+ # markdowns = (git.added_files + git.modified_files)
12
+ # do_something markdowns.select{ |file| file.end_with? "md" }
13
+ #
14
+ # @example Don't allow a file to be deleted
15
+ #
16
+ # deleted = git.deleted_files.include? "my/favourite.file"
17
+ # failure "Don't delete my precious" if deleted
18
+ #
19
+ # @example Fail really big diffs
20
+ #
21
+ # failure "We cannot handle the scale of this PR" if git.lines_of_code > 50_000
22
+ #
23
+ # @example Warn when there are merge commits in the diff
24
+ #
25
+ # if git.commits.any? { |c| c.message =~ /^Merge branch 'master'/ }
26
+ # warn 'Please rebase to get rid of the merge commits in this PR'
27
+ # end
28
+ #
29
+ # @example Warn when somebody tries to add nokogiri to the project
30
+ #
31
+ # diff = git.diff_for_file("Gemfile.lock")
32
+ # if diff && diff.patch =~ "nokogiri"
33
+ # warn 'Please do not add nokogiri to the project. Thank you.'
34
+ # end
35
+ #
36
+ # @see danger/danger
37
+ # @tags core, git
38
+
39
+ class DangerfileGitPlugin < Plugin
40
+ # The instance name used in the Dangerfile
41
+ # @return [String]
42
+ #
43
+ def self.instance_name
44
+ "git"
45
+ end
46
+
47
+ def initialize(dangerfile)
48
+ super(dangerfile)
49
+ raise unless dangerfile.env.scm.class == Danger::GitRepo
50
+
51
+ @git = dangerfile.env.scm
52
+ end
53
+
54
+ # @!group Git Files
55
+ # Paths for files that were added during the diff
56
+ # @return [FileList<String>] an [Array] subclass
57
+ #
58
+ def added_files
59
+ Danger::FileList.new(@git.diff.select { |diff| diff.type == "new" }.map(&:path))
60
+ end
61
+
62
+ # @!group Git Files
63
+ # Paths for files that were removed during the diff
64
+ # @return [FileList<String>] an [Array] subclass
65
+ #
66
+ def deleted_files
67
+ Danger::FileList.new(@git.diff.select { |diff| diff.type == "deleted" }.map(&:path))
68
+ end
69
+
70
+ # @!group Git Files
71
+ # Paths for files that changed during the diff
72
+ # @return [FileList<String>] an [Array] subclass
73
+ #
74
+ def modified_files
75
+ Danger::FileList.new(@git.diff.select { |diff| diff.type == "modified" }.map(&:path))
76
+ end
77
+
78
+ # @!group Git Metadata
79
+ # List of renamed files
80
+ # @return [Array<Hash>] with keys `:before` and `:after`
81
+ #
82
+ def renamed_files
83
+ @git.renamed_files
84
+ end
85
+
86
+ # @!group Git Metadata
87
+ # Whole diff
88
+ # @return [Git::Diff] from the gem `git`
89
+ #
90
+ def diff
91
+ @git.diff
92
+ end
93
+
94
+ # @!group Git Metadata
95
+ # The overall lines of code added/removed in the diff
96
+ # @return [Fixnum]
97
+ #
98
+ def lines_of_code
99
+ @git.diff.lines
100
+ end
101
+
102
+ # @!group Git Metadata
103
+ # The overall lines of code removed in the diff
104
+ # @return [Fixnum]
105
+ #
106
+ def deletions
107
+ @git.diff.deletions
108
+ end
109
+
110
+ # @!group Git Metadata
111
+ # The overall lines of code added in the diff
112
+ # @return [Fixnum]
113
+ #
114
+ def insertions
115
+ @git.diff.insertions
116
+ end
117
+
118
+ # @!group Git Metadata
119
+ # The log of commits inside the diff
120
+ # @return [Git::Log] from the gem `git`
121
+ #
122
+ def commits
123
+ @git.log.to_a
124
+ end
125
+
126
+ # @!group Git Metadata
127
+ # Details for a specific file in this diff
128
+ # @return [Git::Diff::DiffFile] from the gem `git`
129
+ #
130
+ def diff_for_file(file)
131
+ (added_files + modified_files).include?(file) ? @git.diff[file] : nil
132
+ end
133
+
134
+ # @!group Git Metadata
135
+ # Statistics for a specific file in this diff
136
+ # @return [Hash] with keys `:insertions`, `:deletions` giving line counts, and `:before`, `:after` giving file contents, or nil if the file has no changes or does not exist
137
+ #
138
+ def info_for_file(file)
139
+ return nil unless modified_files.include?(file) || added_files.include?(file) || deleted_files.include?(file)
140
+ stats = @git.diff.stats[:files][file]
141
+ diff = @git.diff[file]
142
+ {
143
+ insertions: stats[:insertions],
144
+ deletions: stats[:deletions],
145
+ before: added_files.include?(file) || deleted_files.include?(file) ? nil : diff.blob(:src).contents,
146
+ after: added_files.include?(file) || deleted_files.include?(file) ? nil : diff.blob(:dst).contents
147
+ }
148
+ end
149
+
150
+ # @!group Git Metadata
151
+ # List of remote tags
152
+ # @return [String]
153
+ #
154
+ def tags
155
+ @git.tags.each_line
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,254 @@
1
+ require "danger/plugin_support/plugin"
2
+
3
+ module Danger
4
+ # Handles interacting with GitHub 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 github.pr_title.include? "[WIP]"
10
+ #
11
+ # @example Declare a PR to be simple to avoid specific Danger rules
12
+ #
13
+ # declared_trivial = (github.pr_title + github.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 github.pr_labels.empty?
18
+ #
19
+ # @example Check if a user is in a specific GitHub org, and message them if so
20
+ #
21
+ # unless github.api.organization_member?('danger', github.pr_author)
22
+ # message "@#{github.pr_author} is not a contributor yet, would you like to join the Danger org?"
23
+ # end
24
+ #
25
+ # @example Ensure there is a summary for a PR
26
+ #
27
+ # failure "Please provide a summary in the Pull Request description" if github.pr_body.length < 5
28
+ #
29
+ # @example Only accept PRs to the develop branch
30
+ #
31
+ # failure "Please re-submit this PR to develop, we may have already fixed your issue." if github.branch_for_base != "develop"
32
+ #
33
+ # @example Note when PRs don't reference a milestone, which goes away when it does
34
+ #
35
+ # has_milestone = github.pr_json["milestone"] != nil
36
+ # warn("This PR does not refer to an existing milestone", sticky: false) unless has_milestone
37
+ #
38
+ # @example Note when a PR cannot be manually merged, which goes away when you can
39
+ #
40
+ # can_merge = github.pr_json["mergeable"]
41
+ # warn("This PR cannot be merged yet.", sticky: false) unless can_merge
42
+ #
43
+ # @example Highlight when a celebrity makes a pull request
44
+ #
45
+ # message "Welcome, Danger." if github.pr_author == "dangermcshane"
46
+ #
47
+ # @example Ensure that all PRs have an assignee
48
+ #
49
+ # warn "This PR does not have any assignees yet." unless github.pr_json["assignee"]
50
+ #
51
+ # @example Send a message with links to a collection of specific files
52
+ #
53
+ # if git.modified_files.include? "config/*.js"
54
+ # config_files = git.modified_files.select { |path| path.include? "config/" }
55
+ # message "This PR changes #{ github.html_link(config_files) }"
56
+ # end
57
+ #
58
+ # @example Highlight with a clickable link if a Package.json is changed
59
+ #
60
+ # warn "#{github.html_link("Package.json")} was edited." if git.modified_files.include? "Package.json"
61
+ #
62
+ # @example Note an issue with a particular line on a file using the #L[num] syntax, e.g. `#L23`
63
+ #
64
+ # linter_json = `my_linter lint "file"`
65
+ # results = JSON.parse linter_json
66
+ # unless results.empty?
67
+ # file, line, warning = result.first
68
+ # warn "#{github.html_link("#{file}#L#{line}")} has linter issue: #{warning}."
69
+ # end
70
+ #
71
+ #
72
+ # @see danger/danger
73
+ # @tags core, github
74
+ #
75
+ class DangerfileGitHubPlugin < Plugin
76
+ # So that this init can fail.
77
+ def self.new(dangerfile)
78
+ return nil if dangerfile.env.request_source.class != Danger::RequestSources::GitHub
79
+ super
80
+ end
81
+
82
+ def initialize(dangerfile)
83
+ super(dangerfile)
84
+
85
+ @github = dangerfile.env.request_source
86
+ end
87
+
88
+ # The instance name used in the Dangerfile
89
+ # @return [String]
90
+ #
91
+ def self.instance_name
92
+ "github"
93
+ end
94
+
95
+ # @!group PR Review
96
+ #
97
+ # In Beta. Provides access to creating a GitHub Review instead of a typical GitHub comment.
98
+ #
99
+ # To use you announce the start of your review, and the end via the `start` and `submit` functions,
100
+ # for example:
101
+ #
102
+ # github.review.start
103
+ # github.review.fail(message)
104
+ # github.review.warn(message)
105
+ # github.review.message(message)
106
+ # github.review.markdown(message)
107
+ # github.review.submit
108
+ #
109
+ # @return [ReviewDSL]
110
+ def review
111
+ @github.review
112
+ end
113
+
114
+ # @!group PR Metadata
115
+ # The title of the Pull Request.
116
+ # @return [String]
117
+ #
118
+ def pr_title
119
+ @github.pr_json["title"].to_s
120
+ end
121
+
122
+ # @!group PR Metadata
123
+ # The body text of the Pull Request.
124
+ # @return [String]
125
+ #
126
+ def pr_body
127
+ pr_json["body"].to_s
128
+ end
129
+
130
+ # @!group PR Metadata
131
+ # The username of the author of the Pull Request.
132
+ # @return [String]
133
+ #
134
+ def pr_author
135
+ pr_json["user"]["login"].to_s
136
+ end
137
+
138
+ # @!group PR Metadata
139
+ # The labels assigned to the Pull Request.
140
+ # @return [String]
141
+ #
142
+ def pr_labels
143
+ @github.issue_json["labels"].map { |l| l[:name] }
144
+ end
145
+
146
+ # @!group PR Commit Metadata
147
+ # The branch to which the PR is going to be merged into.
148
+ # @return [String]
149
+ #
150
+ def branch_for_base
151
+ pr_json["base"]["ref"]
152
+ end
153
+
154
+ # @!group PR Commit Metadata
155
+ # The branch to which the PR is going to be merged from.
156
+ # @return [String]
157
+ #
158
+ def branch_for_head
159
+ pr_json["head"]["ref"]
160
+ end
161
+
162
+ # @!group PR Commit Metadata
163
+ # The base commit to which the PR is going to be merged as a parent.
164
+ # @return [String]
165
+ #
166
+ def base_commit
167
+ pr_json["base"]["sha"]
168
+ end
169
+
170
+ # @!group PR Commit Metadata
171
+ # The head commit to which the PR is requesting to be merged from.
172
+ # @return [String]
173
+ #
174
+ def head_commit
175
+ pr_json["head"]["sha"]
176
+ end
177
+
178
+ # @!group GitHub Misc
179
+ # The hash that represents the PR's JSON. For an example of what this looks like
180
+ # see the [Danger Fixture'd one](https://raw.githubusercontent.com/danger/danger/master/spec/fixtures/github_api/pr_response.json).
181
+ # @return [Hash]
182
+ #
183
+ def pr_json
184
+ @github.pr_json
185
+ end
186
+
187
+ # @!group GitHub Misc
188
+ # Provides access to the GitHub API client used inside Danger. Making
189
+ # it easy to use the GitHub API inside a Dangerfile.
190
+ # @return [Octokit::Client]
191
+ def api
192
+ @github.client
193
+ end
194
+
195
+ # @!group PR Content
196
+ # The unified diff produced by Github for this PR
197
+ # see [Unified diff](https://en.wikipedia.org/wiki/Diff_utility#Unified_format)
198
+ # @return [String]
199
+ def pr_diff
200
+ @github.pr_diff
201
+ end
202
+
203
+ # @!group GitHub Misc
204
+ # Returns a list of HTML anchors for a file, or files in the head repository. An example would be:
205
+ # `<a href='https://github.com/artsy/eigen/blob/561827e46167077b5e53515b4b7349b8ae04610b/file.txt'>file.txt</a>`. It returns a string of multiple anchors if passed an array.
206
+ # @param [String or Array<String>] paths
207
+ # A list of strings to convert to github anchors
208
+ # @param [Bool] full_path
209
+ # Shows the full path as the link's text, defaults to `true`.
210
+ #
211
+ # @return [String]
212
+ def html_link(paths, full_path: true)
213
+ paths = [paths] unless paths.kind_of?(Array)
214
+ commit = head_commit
215
+ repo = pr_json["head"]["repo"]["html_url"]
216
+
217
+ paths = paths.map do |path|
218
+ url_path = path.start_with?("/") ? path : "/#{path}"
219
+ text = full_path ? path : File.basename(path)
220
+ create_link("#{repo}/blob/#{commit}#{url_path}", text)
221
+ end
222
+
223
+ return paths.first if paths.count < 2
224
+ paths.first(paths.count - 1).join(", ") + " & " + paths.last
225
+ end
226
+
227
+ # @!group GitHub Misc
228
+ # Use to ignore inline messages which lay outside a diff's range, thereby not posting them in the main comment.
229
+ # You can set hash to change behavior per each kinds. (ex. `{warning: true, error: false}`)
230
+ # @param [Bool] or [Hash<Symbol, Bool>] dismiss
231
+ # Ignore out of range inline messages, defaults to `true`
232
+ #
233
+ # @return [void]
234
+ def dismiss_out_of_range_messages(dismiss = true)
235
+ if dismiss.kind_of?(Hash)
236
+ @github.dismiss_out_of_range_messages = dismiss
237
+ elsif dismiss.kind_of?(TrueClass)
238
+ @github.dismiss_out_of_range_messages = true
239
+ elsif dismiss.kind_of?(FalseClass)
240
+ @github.dismiss_out_of_range_messages = false
241
+ end
242
+ end
243
+
244
+ %i(title body author labels json).each do |suffix|
245
+ alias_method "mr_#{suffix}".to_sym, "pr_#{suffix}".to_sym
246
+ end
247
+
248
+ private
249
+
250
+ def create_link(href, text)
251
+ "<a href='#{href}'>#{text}</a>"
252
+ end
253
+ end
254
+ end
@@ -0,0 +1,240 @@
1
+ require "danger/plugin_support/plugin"
2
+
3
+ module Danger
4
+ # Handles interacting with GitLab inside a Dangerfile. Provides a few functions which wrap `mr_json` and also
5
+ # through a few standard functions to simplify your code.
6
+ #
7
+ # @example Warn when an MR is classed as work in progress.
8
+ #
9
+ # warn "MR is classed as Work in Progress" if gitlab.mr_title.include? "[WIP]"
10
+ #
11
+ # @example Declare a MR to be simple to avoid specific Danger rules.
12
+ #
13
+ # declared_trivial = (gitlab.mr_title + gitlab.mr_body).include?("#trivial")
14
+ #
15
+ # @example Ensure that labels have been applied to the MR.
16
+ #
17
+ # failure "Please add labels to this MR" if gitlab.mr_labels.empty?
18
+ #
19
+ # @example Ensure that all MRs have an assignee.
20
+ #
21
+ # warn "This MR does not have any assignees yet." unless gitlab.mr_json["assignee"]
22
+ #
23
+ # @example Ensure there is a summary for a MR.
24
+ #
25
+ # failure "Please provide a summary in the Merge Request description" if gitlab.mr_body.length < 5
26
+ #
27
+ # @example Only accept MRs to the develop branch.
28
+ #
29
+ # failure "Please re-submit this MR to develop, we may have already fixed your issue." if gitlab.branch_for_merge != "develop"
30
+ #
31
+ # @example Note when MRs don't reference a milestone, make the warning stick around on subsequent runs
32
+ #
33
+ # has_milestone = gitlab.mr_json["milestone"] != nil
34
+ # warn("This MR does not refer to an existing milestone", sticky: true) unless has_milestone
35
+ #
36
+ # @example Note when a MR cannot be manually merged
37
+ #
38
+ # can_merge = gitlab.mr_json["mergeable"]
39
+ # warn("This MR cannot be merged yet.") unless can_merge
40
+ #
41
+ # @example Highlight when a celebrity makes a merge request.
42
+ #
43
+ # message "Welcome, Danger." if gitlab.mr_author == "dangermcshane"
44
+ #
45
+ # @example Send a message with links to a collection of specific files.
46
+ #
47
+ # if git.modified_files.include? "config/*.js"
48
+ # config_files = git.modified_files.select { |path| path.include? "config/" }
49
+ # message "This MR changes #{ gitlab.html_link(config_files) }"
50
+ # end
51
+ #
52
+ # @example Highlight with a clickable link if a Package.json is changed.
53
+ #
54
+ # warn "#{gitlab.html_link("Package.json")} was edited." if git.modified_files.include? "Package.json"
55
+ #
56
+ # @example Select a random group member as assignee if no assignee is selected
57
+ #
58
+ # if gitlab.mr_json["assignee"].nil?
59
+ # reviewer = gitlab.api.group_members(gitlab.api.merge_request_approvals(project_id, mr_id).to_hash["approver_groups"].first["group"]["id"]).sample
60
+ # if gitlab.api.group_members(gitlab.api.merge_request_approvals(project_id, mr_id).to_hash["approver_groups"].first["group"]["id"]).length > 1
61
+ # while reviewer.to_hash["id"] == gitlab.mr_json["author"]["id"] do
62
+ # reviewer = gitlab.api.group_members(gitlab.api.merge_request_approvals(project_id, mr_id).to_hash["approver_groups"].first["group"]["id"]).sample
63
+ # end
64
+ # end
65
+ # message "Reviewer roulete rolled for: #{reviewer.to_hash['name']} (@#{reviewer.to_hash['username']})"
66
+ # gitlab.api.update_merge_request(project_id, mr_id, { assignee_id: reviewer.to_hash["id"] })
67
+ # end
68
+ #
69
+ #
70
+ # @see danger/danger
71
+ # @tags core, gitlab
72
+ #
73
+ class DangerfileGitLabPlugin < Plugin
74
+ # So that this init can fail.
75
+ def self.new(dangerfile)
76
+ return nil if dangerfile.env.request_source.class != Danger::RequestSources::GitLab
77
+ super
78
+ end
79
+
80
+ # The instance name used in the Dangerfile
81
+ # @return [String]
82
+ #
83
+ def self.instance_name
84
+ "gitlab"
85
+ end
86
+
87
+ def initialize(dangerfile)
88
+ super(dangerfile)
89
+
90
+ @gitlab = dangerfile.env.request_source
91
+ end
92
+
93
+ # @!group MR Metadata
94
+ # The title of the Merge Request
95
+ # @return [String]
96
+ #
97
+ def mr_title
98
+ @gitlab.mr_json.title.to_s
99
+ end
100
+
101
+ # @!group MR Metadata
102
+ # The body text of the Merge Request
103
+ # @return [String]
104
+ #
105
+ def mr_body
106
+ @gitlab.mr_json.description.to_s
107
+ end
108
+
109
+ # @!group MR Metadata
110
+ # The username of the author of the Merge Request
111
+ # @return [String]
112
+ #
113
+ def mr_author
114
+ @gitlab.mr_json.author.username.to_s
115
+ end
116
+
117
+ # @!group MR Metadata
118
+ # The labels assigned to the Merge Request
119
+ # @return [String]
120
+ #
121
+ def mr_labels
122
+ @gitlab.mr_json.labels
123
+ end
124
+
125
+ # @!group MR Content
126
+ # The unified diff produced by GitLab for this MR
127
+ # see [Unified diff](https://en.wikipedia.org/wiki/Diff_utility#Unified_format)
128
+ # @return [String]
129
+ #
130
+ def mr_diff
131
+ @gitlab.mr_diff
132
+ end
133
+
134
+ # @!group MR Commit Metadata
135
+ # The branch to which the MR is going to be merged into
136
+ # @deprecated Please use {#branch_for_base} instead
137
+ # @return [String]
138
+ #
139
+ def branch_for_merge
140
+ branch_for_base
141
+ end
142
+
143
+ # @!group MR Commit Metadata
144
+ # The branch to which the MR is going to be merged into.
145
+ # @return [String]
146
+ #
147
+ def branch_for_base
148
+ @gitlab.mr_json.target_branch
149
+ end
150
+
151
+ # @!group MR Commit Metadata
152
+ # The branch to which the MR is going to be merged from.
153
+ # @return [String]
154
+ #
155
+ def branch_for_head
156
+ @gitlab.mr_json.source_branch
157
+ end
158
+
159
+ # @!group MR Commit Metadata
160
+ # The base commit to which the MR is going to be merged as a parent
161
+ # @return [String]
162
+ #
163
+ def base_commit
164
+ @gitlab.mr_json.diff_refs.base_sha
165
+ end
166
+
167
+ # @!group MR Commit Metadata
168
+ # The head commit to which the MR is requesting to be merged from
169
+ # @return [String]
170
+ #
171
+ def head_commit
172
+ @gitlab.mr_json.diff_refs.head_sha
173
+ end
174
+
175
+ # @!group GitLab Misc
176
+ # The hash that represents the MR's JSON. See documentation for the
177
+ # structure [here](http://docs.gitlab.com/ce/api/merge_requests.html#get-single-mr)
178
+ # @return [Hash]
179
+ #
180
+ def mr_json
181
+ @gitlab.mr_json.to_hash
182
+ end
183
+
184
+ # @!group GitLab Misc
185
+ # Provides access to the GitLab API client used inside Danger. Making
186
+ # it easy to use the GitLab API inside a Dangerfile. See the gitlab
187
+ # gem's [documentation](http://www.rubydoc.info/gems/gitlab/Gitlab/Client)
188
+ # for accessible methods.
189
+ # @return [GitLab::Client]
190
+ #
191
+ def api
192
+ @gitlab.client
193
+ end
194
+
195
+ # @!group GitLab Misc
196
+ # Returns the web_url of the source project.
197
+ # @return [String]
198
+ #
199
+ def repository_web_url
200
+ @repository_web_url ||= begin
201
+ project = api.project(mr_json["source_project_id"])
202
+ project.web_url
203
+ end
204
+ end
205
+
206
+ # @!group GitLab Misc
207
+ # Returns a list of HTML anchors for a file, or files in the head repository. An example would be:
208
+ # `<a href='https://gitlab.com/artsy/eigen/blob/561827e46167077b5e53515b4b7349b8ae04610b/file.txt'>file.txt</a>`. It returns a string of multiple anchors if passed an array.
209
+ # @param [String or Array<String>] paths
210
+ # A list of strings to convert to gitlab anchors
211
+ # @param [Bool] full_path
212
+ # Shows the full path as the link's text, defaults to `true`.
213
+ #
214
+ # @return [String]
215
+ #
216
+ def html_link(paths, full_path: true)
217
+ paths = [paths] unless paths.kind_of?(Array)
218
+ commit = head_commit
219
+
220
+ paths = paths.map do |path|
221
+ url_path = path.start_with?("/") ? path : "/#{path}"
222
+ text = full_path ? path : File.basename(path)
223
+ create_link("#{repository_web_url}/blob/#{commit}#{url_path}", text)
224
+ end
225
+
226
+ return paths.first if paths.count < 2
227
+ paths.first(paths.count - 1).join(", ") + " & " + paths.last
228
+ end
229
+
230
+ %i(title body author labels json diff).each do |suffix|
231
+ alias_method "pr_#{suffix}".to_sym, "mr_#{suffix}".to_sym
232
+ end
233
+
234
+ private
235
+
236
+ def create_link(href, text)
237
+ "<a href='#{href}'>#{text}</a>"
238
+ end
239
+ end
240
+ end