danger 8.0.3

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 +159 -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,42 @@
1
+ require "danger/plugin_support/plugin"
2
+
3
+ # Danger
4
+ module Danger
5
+ # Handles interacting with local only plugin inside a Dangerfile.
6
+ # It is support pluggin for dry_run command and does not expose any methods.
7
+ # But you can still use other plugins like git
8
+ #
9
+ # @example Check that added lines contains agreed form of words
10
+ #
11
+ # git.diff.each do |chunk|
12
+ # chunk.patch.lines.grep(/^\+/).each do |added_line|
13
+ # if added_line.gsub!(/(?<cancel>cancel)(?<rest>[^l[[:space:]][[:punct:]]]+)/i, '>>\k<cancel>-l-\k<rest><<')
14
+ # fail "Single 'L' for cancellation-alike words in '#{added_line}'"
15
+ # end
16
+ # end
17
+ # end
18
+ #
19
+ # @see danger/danger
20
+ # @tags core, local_only
21
+ #
22
+ class DangerfileLocalOnlyPlugin < Plugin
23
+ # So that this init can fail.
24
+ def self.new(dangerfile)
25
+ return nil if dangerfile.env.request_source.class != Danger::RequestSources::LocalOnly
26
+ super
27
+ end
28
+
29
+ def initialize(dangerfile)
30
+ super(dangerfile)
31
+
32
+ @local_repo = dangerfile.env.request_source
33
+ end
34
+
35
+ # The instance name used in the Dangerfile
36
+ # @return [String]
37
+ #
38
+ def self.instance_name
39
+ "local_repo"
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,218 @@
1
+ require "danger/danger_core/messages/violation"
2
+ require "danger/danger_core/messages/markdown"
3
+ require "danger/plugin_support/plugin"
4
+
5
+ module Danger
6
+ # Provides the feedback mechanism for Danger. Danger can keep track of
7
+ # messages, warnings, failure and post arbitrary markdown into a comment.
8
+ #
9
+ # The message within which Danger communicates back is amended on each run in a session.
10
+ #
11
+ # Each of `message`, `warn` and `fail` have a `sticky` flag, `false` by default, which
12
+ # when `true` means that the message will be crossed out instead of being removed.
13
+ # If it's not called again on subsequent runs.
14
+ #
15
+ # Each of `message`, `warn`, `fail` and `markdown` support multiple passed arguments
16
+ # @example
17
+ #
18
+ # message 'Hello', 'World', file: "Dangerfile", line: 1
19
+ # warn ['This', 'is', 'warning'], file: "Dangerfile", line: 1
20
+ # failure 'Ooops', 'bad bad error', sticky: false
21
+ # markdown '# And', '# Even', '# Markdown', file: "Dangerfile", line: 1
22
+ #
23
+ # By default, using `failure` would fail the corresponding build. Either via an API call, or
24
+ # via the return value for the danger command. Older code examples use `fail` which is an alias
25
+ # of `failure`, but the default Rubocop settings would have an issue with it.
26
+ #
27
+ # You can optionally add `file` and `line` to provide inline feedback on a PR in GitHub, note that
28
+ # only feedback inside the PR's diff will show up inline. Others will appear inside the main comment.
29
+ #
30
+ # It is possible to have Danger ignore specific warnings or errors by writing `Danger: Ignore "[warning/error text]"`.
31
+ #
32
+ # Sidenote: Messaging is the only plugin which adds functions to the root of the Dangerfile.
33
+ #
34
+ # @example Failing a build
35
+ #
36
+ # failure "This build didn't pass tests"
37
+ # failure "Ooops!", "Something bad happened"
38
+ # failure ["This is example", "with array"]
39
+ #
40
+ # @example Failing a build, and note that on subsequent runs
41
+ #
42
+ # failure("This build didn't pass tests", sticky: true)
43
+ #
44
+ # @example Passing a warning
45
+ #
46
+ # warn "This build didn't pass linting"
47
+ # warn "Hm...", "This is not really good"
48
+ # warn ["Multiple warnings", "via array"]
49
+ #
50
+ # @example Displaying a markdown table
51
+ #
52
+ # message = "### Proselint found issues\n\n"
53
+ # message << "Line | Message | Severity |\n"
54
+ # message << "| --- | ----- | ----- |\n"
55
+ # message << "20 | No documentation | Error \n"
56
+ # markdown message
57
+ #
58
+ # markdown "### First issue", "### Second issue"
59
+ # markdown ["### First issue", "### Second issue"]
60
+ #
61
+ # @example Adding an inline warning to a file
62
+ #
63
+ # warn("You shouldn't use puts in your Dangerfile", file: "Dangerfile", line: 10)
64
+ #
65
+ #
66
+ # @see danger/danger
67
+ # @tags core, messaging
68
+ #
69
+
70
+ class DangerfileMessagingPlugin < Plugin
71
+ def initialize(dangerfile)
72
+ super(dangerfile)
73
+
74
+ @warnings = []
75
+ @errors = []
76
+ @messages = []
77
+ @markdowns = []
78
+ end
79
+
80
+ # The instance name used in the Dangerfile
81
+ # @return [String]
82
+ #
83
+ def self.instance_name
84
+ "messaging"
85
+ end
86
+
87
+ # @!group Core
88
+ # Print markdown to below the table
89
+ #
90
+ # @param [String, Array<String>] message
91
+ # The markdown based message to be printed below the table
92
+ # @param [String] file
93
+ # Optional. Path to the file that the message is for.
94
+ # @param [String] line
95
+ # Optional. The line in the file to present the message in.
96
+ # @return [void]
97
+ #
98
+ def markdown(*markdowns, **options)
99
+ file = options.fetch(:file, nil)
100
+ line = options.fetch(:line, nil)
101
+
102
+ markdowns.flatten.each do |markdown|
103
+ @markdowns << Markdown.new(markdown, file, line)
104
+ end
105
+ end
106
+
107
+ # @!group Core
108
+ # Print out a generate message on the PR
109
+ #
110
+ # @param [String, Array<String>] message
111
+ # The message to present to the user
112
+ # @param [Boolean] sticky
113
+ # Whether the message should be kept after it was fixed,
114
+ # defaults to `false`.
115
+ # @param [String] file
116
+ # Optional. Path to the file that the message is for.
117
+ # @param [String] line
118
+ # Optional. The line in the file to present the message in.
119
+ # @return [void]
120
+ #
121
+ def message(*messages, **options)
122
+ sticky = options.fetch(:sticky, false)
123
+ file = options.fetch(:file, nil)
124
+ line = options.fetch(:line, nil)
125
+
126
+ messages.flatten.each do |message|
127
+ @messages << Violation.new(message, sticky, file, line, type: :message) if message
128
+ end
129
+ end
130
+
131
+ # @!group Core
132
+ # Specifies a problem, but not critical
133
+ #
134
+ # @param [String, Array<String>] message
135
+ # The message to present to the user
136
+ # @param [Boolean] sticky
137
+ # Whether the message should be kept after it was fixed,
138
+ # defaults to `false`.
139
+ # @param [String] file
140
+ # Optional. Path to the file that the message is for.
141
+ # @param [String] line
142
+ # Optional. The line in the file to present the message in.
143
+ # @return [void]
144
+ #
145
+ def warn(*warnings, **options)
146
+ sticky = options.fetch(:sticky, false)
147
+ file = options.fetch(:file, nil)
148
+ line = options.fetch(:line, nil)
149
+
150
+ warnings.flatten.each do |warning|
151
+ next if should_ignore_violation(warning)
152
+ @warnings << Violation.new(warning, sticky, file, line, type: :warning) if warning
153
+ end
154
+ end
155
+
156
+ # @!group Core
157
+ # Declares a CI blocking error
158
+ #
159
+ # @param [String, Array<String>] message
160
+ # The message to present to the user
161
+ # @param [Boolean] sticky
162
+ # Whether the message should be kept after it was fixed,
163
+ # defaults to `false`.
164
+ # @param [String] file
165
+ # Optional. Path to the file that the message is for.
166
+ # @param [String] line
167
+ # Optional. The line in the file to present the message in.
168
+ # @return [void]
169
+ #
170
+ def fail(*failures, **options)
171
+ sticky = options.fetch(:sticky, false)
172
+ file = options.fetch(:file, nil)
173
+ line = options.fetch(:line, nil)
174
+
175
+ failures.flatten.each do |failure|
176
+ next if should_ignore_violation(failure)
177
+ @errors << Violation.new(failure, sticky, file, line, type: :error) if failure
178
+ end
179
+ end
180
+
181
+ alias_method :failure, :fail
182
+
183
+ # @!group Reporting
184
+ # A list of all messages passed to Danger, including
185
+ # the markdowns.
186
+ #
187
+ # @visibility hidden
188
+ # @return [Hash]
189
+ def status_report
190
+ {
191
+ errors: @errors.map(&:message).clone.freeze,
192
+ warnings: @warnings.map(&:message).clone.freeze,
193
+ messages: @messages.map(&:message).clone.freeze,
194
+ markdowns: @markdowns.clone.freeze
195
+ }
196
+ end
197
+
198
+ # @!group Reporting
199
+ # A list of all violations passed to Danger, we don't
200
+ # anticipate users of Danger needing to use this.
201
+ #
202
+ # @visibility hidden
203
+ # @return [Hash]
204
+ def violation_report
205
+ {
206
+ errors: @errors.clone.freeze,
207
+ warnings: @warnings.clone.freeze,
208
+ messages: @messages.clone.freeze
209
+ }
210
+ end
211
+
212
+ private
213
+
214
+ def should_ignore_violation(message)
215
+ env.request_source.ignored_violations.include? message
216
+ end
217
+ end
218
+ end
@@ -0,0 +1,191 @@
1
+ # coding: utf-8
2
+
3
+ require "danger/plugin_support/plugin"
4
+
5
+ module Danger
6
+ # Handles interacting with VSTS inside a Dangerfile. Provides a few functions which wrap `pr_json` and also
7
+ # through a few standard functions to simplify your code.
8
+ #
9
+ # @example Warn when a PR is classed as work in progress
10
+ #
11
+ # warn "PR is classed as Work in Progress" if vsts.pr_title.include? "[WIP]"
12
+ #
13
+ # @example Declare a PR to be simple to avoid specific Danger rules
14
+ #
15
+ # declared_trivial = (vsts.pr_title + vsts.pr_body).include?("#trivial")
16
+ #
17
+ # @example Ensure there is a summary for a PR
18
+ #
19
+ # failure "Please provide a summary in the Pull Request description" if vsts.pr_body.length < 5
20
+ #
21
+ # @example Only accept PRs to the develop branch
22
+ #
23
+ # failure "Please re-submit this PR to develop, we may have already fixed your issue." if vsts.branch_for_base != "develop"
24
+ #
25
+ # @example Highlight when a celebrity makes a pull request
26
+ #
27
+ # message "Welcome, Danger." if vsts.pr_author == "dangermcshane"
28
+ #
29
+ # @example Ensure that all PRs have an assignee
30
+ #
31
+ # warn "This PR does not have any assignees yet." unless vsts.pr_json["reviewers"].length == 0
32
+ #
33
+ # @example Send a message with links to a collection of specific files
34
+ #
35
+ # if git.modified_files.include? "config/*.js"
36
+ # config_files = git.modified_files.select { |path| path.include? "config/" }
37
+ # message "This PR changes #{ vsts.markdown_link(config_files) }"
38
+ # end
39
+ #
40
+ # @example Highlight with a clickable link if a Package.json is changed
41
+ #
42
+ # warn "#{vsts.markdown_link("Package.json")} was edited." if git.modified_files.include? "Package.json"
43
+ #
44
+ # @example Note an issue with a particular line on a file using the #L[num] syntax, e.g. `#L23`
45
+ #
46
+ # linter_json = `my_linter lint "file"`
47
+ # results = JSON.parse linter_json
48
+ # unless results.empty?
49
+ # file, line, warning = result.first
50
+ # warn "#{vsts.markdown_link("#{file}#L#{line}")} has linter issue: #{warning}."
51
+ # end
52
+ #
53
+ #
54
+ # @see danger/danger
55
+ # @tags core, vsts
56
+ #
57
+ class DangerfileVSTSPlugin < Plugin
58
+ # So that this init can fail.
59
+ def self.new(dangerfile)
60
+ return nil if dangerfile.env.request_source.class != Danger::RequestSources::VSTS
61
+ super
62
+ end
63
+
64
+ # The instance name used in the Dangerfile
65
+ # @return [String]
66
+ #
67
+ def self.instance_name
68
+ "vsts"
69
+ end
70
+
71
+ def initialize(dangerfile)
72
+ super(dangerfile)
73
+ @source = dangerfile.env.request_source
74
+ end
75
+
76
+ # @!group VSTS Misc
77
+ # The hash that represents the PR's JSON. For an example of what this looks like
78
+ # see the [Danger Fixture'd one](https://raw.githubusercontent.com/danger/danger/master/spec/fixtures/vsts_api/pr_response.json).
79
+ # @return [Hash]
80
+ def pr_json
81
+ @source.pr_json
82
+ end
83
+
84
+ # @!group PR Metadata
85
+ # The title of the Pull Request.
86
+ # @return [String]
87
+ #
88
+ def pr_title
89
+ @source.pr_json[:title].to_s
90
+ end
91
+
92
+ # @!group PR Metadata
93
+ # The body text of the Pull Request.
94
+ # @return [String]
95
+ #
96
+ def pr_description
97
+ @source.pr_json[:description].to_s
98
+ end
99
+ alias pr_body pr_description
100
+
101
+ # @!group PR Metadata
102
+ # The username of the author of the Pull Request.
103
+ # @return [String]
104
+ #
105
+ def pr_author
106
+ @source.pr_json[:createdBy][:displayName].to_s
107
+ end
108
+
109
+ # @!group PR Commit Metadata
110
+ # The branch to which the PR is going to be merged into.
111
+ # @return [String]
112
+ #
113
+ def branch_for_base
114
+ branch_name(:targetRefName)
115
+ end
116
+
117
+ # @!group PR Commit Metadata
118
+ # A href that represents the current PR
119
+ # @return [String]
120
+ #
121
+ def pr_link
122
+ repo_path = @source.pr_json[:repository][:remoteUrl].to_s
123
+ pull_request_id = @source.pr_json[:pullRequestId].to_s
124
+
125
+ "#{repo_path}/pullRequest/#{pull_request_id}"
126
+ end
127
+
128
+ # @!group PR Commit Metadata
129
+ # The branch to which the PR is going to be merged from.
130
+ # @return [String]
131
+ #
132
+ def branch_for_head
133
+ branch_name(:sourceRefName)
134
+ end
135
+
136
+ # @!group PR Commit Metadata
137
+ # The base commit to which the PR is going to be merged as a parent.
138
+ # @return [String]
139
+ #
140
+ def base_commit
141
+ @source.pr_json[:lastMergeTargetCommit][:commitId].to_s
142
+ end
143
+
144
+ # @!group PR Commit Metadata
145
+ # The head commit to which the PR is requesting to be merged from.
146
+ # @return [String]
147
+ #
148
+ def head_commit
149
+ @source.pr_json[:lastMergeSourceCommit][:commitId].to_s
150
+ end
151
+
152
+ # @!group VSTS Misc
153
+ # Returns a list of Markdown links for a file, or files in the head repository.
154
+ # It returns a string of multiple links if passed an array.
155
+ # @param [String or Array<String>] paths
156
+ # A list of strings to convert to Markdown links
157
+ # @param [Bool] full_path
158
+ # Shows the full path as the link's text, defaults to `true`.
159
+ #
160
+ # @return [String]
161
+ #
162
+ def markdown_link(paths, full_path: true)
163
+ paths = [paths] unless paths.kind_of?(Array)
164
+ commit = head_commit
165
+ repo = pr_json[:repository][:remoteUrl].to_s
166
+
167
+ paths = paths.map do |path|
168
+ path, line = path.split("#L")
169
+ url_path = path.start_with?("/") ? path : "/#{path}"
170
+ text = full_path ? path : File.basename(path)
171
+ url_path.gsub!(" ", "%20")
172
+ line_ref = line ? "&line=#{line}" : ""
173
+ create_markdown_link("#{repo}/commit/#{commit}?path=#{url_path}&_a=contents#{line_ref}", text)
174
+ end
175
+
176
+ return paths.first if paths.count < 2
177
+ paths.first(paths.count - 1).join(", ") + " & " + paths.last
178
+ end
179
+
180
+ private
181
+
182
+ def create_markdown_link(href, text)
183
+ "[#{text}](#{href})"
184
+ end
185
+
186
+ def branch_name(key)
187
+ repo_matches = @source.pr_json[key].to_s.match(%r{refs\/heads\/(.*)})
188
+ repo_matches[1] unless repo_matches.nil?
189
+ end
190
+ end
191
+ end