codecov 0.1.21
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/lib/codecov.rb +620 -0
- data/test/test_codecov.rb +675 -0
- metadata +158 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 4e9408049c526c9e3733a4b4431fb8f5f479db5a6d70ac769655c178690b878c
|
4
|
+
data.tar.gz: 7f73f2059b9b0f1e8f5549e955f8482532ef34c8505114f068ba689042d6ada9
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0332e23b36a550b36f86dab1d9915c616dca2a119735616486ac4829ad88ad05c46721de6bfea2c400b3f735b26167587acda24db84397842bf9328616fe2997
|
7
|
+
data.tar.gz: e9d9b9ecd83b6c4037d6ca15964184fbe9745e93f33f0b7a17aa0e4d032d13353ffb1893288240c9c004b353e97623f2d88e4e5b3371b507050a8f9273eb5a68
|
data/lib/codecov.rb
ADDED
@@ -0,0 +1,620 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'uri'
|
4
|
+
require 'json'
|
5
|
+
require 'net/http'
|
6
|
+
require 'simplecov'
|
7
|
+
require 'zlib'
|
8
|
+
|
9
|
+
class SimpleCov::Formatter::Codecov
|
10
|
+
VERSION = '0.1.21'
|
11
|
+
|
12
|
+
### CIs
|
13
|
+
RECOGNIZED_CIS = [
|
14
|
+
APPVEYOR = 'Appveyor CI',
|
15
|
+
AZUREPIPELINES = 'Azure Pipelines',
|
16
|
+
BITBUCKET = 'Bitbucket',
|
17
|
+
BITRISE = 'Bitrise CI',
|
18
|
+
BUILDKITE = 'Buildkite CI',
|
19
|
+
CIRCLE = 'Circle CI',
|
20
|
+
CODEBUILD = 'Codebuild CI',
|
21
|
+
CODESHIP = 'Codeship CI',
|
22
|
+
DRONEIO = 'Drone CI',
|
23
|
+
GITHUB = 'GitHub Actions',
|
24
|
+
GITLAB = 'GitLab CI',
|
25
|
+
HEROKU = 'Heroku CI',
|
26
|
+
JENKINS = 'Jenkins CI',
|
27
|
+
SEMAPHORE = 'Semaphore CI',
|
28
|
+
SHIPPABLE = 'Shippable',
|
29
|
+
SOLANO = 'Solano CI',
|
30
|
+
TEAMCITY = 'TeamCity CI',
|
31
|
+
TRAVIS = 'Travis CI',
|
32
|
+
WERCKER = 'Wercker CI'
|
33
|
+
].freeze
|
34
|
+
|
35
|
+
def display_header
|
36
|
+
puts [
|
37
|
+
'',
|
38
|
+
' _____ _',
|
39
|
+
' / ____| | |',
|
40
|
+
'| | ___ __| | ___ ___ _____ __',
|
41
|
+
'| | / _ \ / _\`|/ _ \/ __/ _ \ \ / /',
|
42
|
+
'| |___| (_) | (_| | __/ (_| (_) \ V /',
|
43
|
+
' \_____\___/ \__,_|\___|\___\___/ \_/',
|
44
|
+
" Ruby-#{VERSION}",
|
45
|
+
''
|
46
|
+
].join("\n")
|
47
|
+
end
|
48
|
+
|
49
|
+
def detect_ci
|
50
|
+
ci = if (ENV['CI'] == 'True') && (ENV['APPVEYOR'] == 'True')
|
51
|
+
APPVEYOR
|
52
|
+
elsif !ENV['TF_BUILD'].nil?
|
53
|
+
AZUREPIPELINES
|
54
|
+
elsif (ENV['CI'] == 'true') && !ENV['BITBUCKET_BRANCH'].nil?
|
55
|
+
BITBUCKET
|
56
|
+
elsif (ENV['CI'] == 'true') && (ENV['BITRISE_IO'] == 'true')
|
57
|
+
BITRISE
|
58
|
+
elsif (ENV['CI'] == 'true') && (ENV['BUILDKITE'] == 'true')
|
59
|
+
BUILDKITE
|
60
|
+
elsif (ENV['CI'] == 'true') && (ENV['CIRCLECI'] == 'true')
|
61
|
+
CIRCLE
|
62
|
+
elsif ENV['CODEBUILD_CI'] == 'true'
|
63
|
+
CODEBUILD
|
64
|
+
elsif (ENV['CI'] == 'true') && (ENV['CI_NAME'] == 'codeship')
|
65
|
+
CODESHIP
|
66
|
+
elsif ((ENV['CI'] == 'true') || (ENV['CI'] == 'drone')) && (ENV['DRONE'] == 'true')
|
67
|
+
DRONEIO
|
68
|
+
elsif (ENV['CI'] == 'true') && (ENV['GITHUB_ACTIONS'] == 'true')
|
69
|
+
GITHUB
|
70
|
+
elsif !ENV['GITLAB_CI'].nil?
|
71
|
+
GITLAB
|
72
|
+
elsif ENV['HEROKU_TEST_RUN_ID']
|
73
|
+
HEROKU
|
74
|
+
elsif !ENV['JENKINS_URL'].nil?
|
75
|
+
JENKINS
|
76
|
+
elsif (ENV['CI'] == 'true') && (ENV['SEMAPHORE'] == 'true')
|
77
|
+
SEMAPHORE
|
78
|
+
elsif (ENV['CI'] == 'true') && (ENV['SHIPPABLE'] == 'true')
|
79
|
+
SHIPPABLE
|
80
|
+
elsif ENV['TDDIUM'] == 'true'
|
81
|
+
SOLANO
|
82
|
+
elsif ENV['CI_SERVER_NAME'] == 'TeamCity'
|
83
|
+
TEAMCITY
|
84
|
+
elsif (ENV['CI'] == 'true') && (ENV['TRAVIS'] == 'true')
|
85
|
+
TRAVIS
|
86
|
+
elsif (ENV['CI'] == 'true') && !ENV['WERCKER_GIT_BRANCH'].nil?
|
87
|
+
WERCKER
|
88
|
+
end
|
89
|
+
|
90
|
+
if !RECOGNIZED_CIS.include?(ci)
|
91
|
+
puts ['x>'.red, 'No CI provider detected.'].join(' ')
|
92
|
+
else
|
93
|
+
puts "==> #{ci} detected"
|
94
|
+
end
|
95
|
+
|
96
|
+
ci
|
97
|
+
end
|
98
|
+
|
99
|
+
def build_params(ci)
|
100
|
+
params = {
|
101
|
+
'token' => ENV['CODECOV_TOKEN'],
|
102
|
+
'flags' => ENV['CODECOV_FLAG'] || ENV['CODECOV_FLAGS'],
|
103
|
+
'package' => "ruby-#{VERSION}"
|
104
|
+
}
|
105
|
+
|
106
|
+
case ci
|
107
|
+
when APPVEYOR
|
108
|
+
# http://www.appveyor.com/docs/environment-variables
|
109
|
+
params[:service] = 'appveyor'
|
110
|
+
params[:branch] = ENV['APPVEYOR_REPO_BRANCH']
|
111
|
+
params[:build] = ENV['APPVEYOR_JOB_ID']
|
112
|
+
params[:pr] = ENV['APPVEYOR_PULL_REQUEST_NUMBER']
|
113
|
+
params[:job] = ENV['APPVEYOR_ACCOUNT_NAME'] + '/' + ENV['APPVEYOR_PROJECT_SLUG'] + '/' + ENV['APPVEYOR_BUILD_VERSION']
|
114
|
+
params[:slug] = ENV['APPVEYOR_REPO_NAME']
|
115
|
+
params[:commit] = ENV['APPVEYOR_REPO_COMMIT']
|
116
|
+
when AZUREPIPELINES
|
117
|
+
params[:service] = 'azure_pipelines'
|
118
|
+
params[:branch] = ENV['BUILD_SOURCEBRANCH']
|
119
|
+
params[:pull_request] = ENV['SYSTEM_PULLREQUEST_PULLREQUESTNUMBER']
|
120
|
+
params[:job] = ENV['SYSTEM_JOBID']
|
121
|
+
params[:build] = ENV['BUILD_BUILDID']
|
122
|
+
params[:build_url] = "#{ENV['SYSTEM_TEAMFOUNDATIONSERVERURI']}/#{ENV['SYSTEM_TEAMPROJECT']}/_build/results?buildId=#{ENV['BUILD_BUILDID']}"
|
123
|
+
params[:commit] = ENV['BUILD_SOURCEVERSION']
|
124
|
+
params[:slug] = ENV['BUILD_REPOSITORY_ID']
|
125
|
+
when BITBUCKET
|
126
|
+
# https://confluence.atlassian.com/bitbucket/variables-in-pipelines-794502608.html
|
127
|
+
params[:service] = 'bitbucket'
|
128
|
+
params[:branch] = ENV['BITBUCKET_BRANCH']
|
129
|
+
# BITBUCKET_COMMIT does not always provide full commit sha due to a bug https://jira.atlassian.com/browse/BCLOUD-19393#
|
130
|
+
params[:commit] = (ENV['BITBUCKET_COMMIT'].length < 40 ? nil : ENV['BITBUCKET_COMMIT'])
|
131
|
+
params[:build] = ENV['BITBUCKET_BUILD_NUMBER']
|
132
|
+
when BITRISE
|
133
|
+
# http://devcenter.bitrise.io/faq/available-environment-variables/
|
134
|
+
params[:service] = 'bitrise'
|
135
|
+
params[:branch] = ENV['BITRISE_GIT_BRANCH']
|
136
|
+
params[:pr] = ENV['BITRISE_PULL_REQUEST']
|
137
|
+
params[:build] = ENV['BITRISE_BUILD_NUMBER']
|
138
|
+
params[:build_url] = ENV['BITRISE_BUILD_URL']
|
139
|
+
params[:commit] = ENV['BITRISE_GIT_COMMIT']
|
140
|
+
params[:slug] = ENV['BITRISEIO_GIT_REPOSITORY_OWNER'] + '/' + ENV['BITRISEIO_GIT_REPOSITORY_SLUG']
|
141
|
+
when BUILDKITE
|
142
|
+
# https://buildkite.com/docs/guides/environment-variables
|
143
|
+
params[:service] = 'buildkite'
|
144
|
+
params[:branch] = ENV['BUILDKITE_BRANCH']
|
145
|
+
params[:build] = ENV['BUILDKITE_BUILD_NUMBER']
|
146
|
+
params[:job] = ENV['BUILDKITE_JOB_ID']
|
147
|
+
params[:build_url] = ENV['BUILDKITE_BUILD_URL']
|
148
|
+
params[:slug] = ENV['BUILDKITE_PROJECT_SLUG']
|
149
|
+
params[:commit] = ENV['BUILDKITE_COMMIT']
|
150
|
+
when CIRCLE
|
151
|
+
# https://circleci.com/docs/environment-variables
|
152
|
+
params[:service] = 'circleci'
|
153
|
+
params[:build] = ENV['CIRCLE_BUILD_NUM']
|
154
|
+
params[:job] = ENV['CIRCLE_NODE_INDEX']
|
155
|
+
params[:slug] = if !ENV['CIRCLE_PROJECT_REPONAME'].nil?
|
156
|
+
ENV['CIRCLE_PROJECT_USERNAME'] + '/' + ENV['CIRCLE_PROJECT_REPONAME']
|
157
|
+
else
|
158
|
+
ENV['CIRCLE_REPOSITORY_URL'].gsub(/^.*:/, '').gsub(/\.git$/, '')
|
159
|
+
end
|
160
|
+
params[:pr] = ENV['CIRCLE_PR_NUMBER']
|
161
|
+
params[:branch] = ENV['CIRCLE_BRANCH']
|
162
|
+
params[:commit] = ENV['CIRCLE_SHA1']
|
163
|
+
when CODEBUILD
|
164
|
+
# https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-env-vars.html
|
165
|
+
params[:service] = 'codebuild'
|
166
|
+
params[:branch] = ENV['CODEBUILD_WEBHOOK_HEAD_REF'].split('/')[2]
|
167
|
+
params[:build] = ENV['CODEBUILD_BUILD_ID']
|
168
|
+
params[:commit] = ENV['CODEBUILD_RESOLVED_SOURCE_VERSION']
|
169
|
+
params[:job] = ENV['CODEBUILD_BUILD_ID']
|
170
|
+
params[:slug] = ENV['CODEBUILD_SOURCE_REPO_URL'].match(/.*github.com\/(?<slug>.*).git/)['slug']
|
171
|
+
params[:pr] = ENV['CODEBUILD_SOURCE_VERSION'].match(/pr\/(?<pr>.*)/)['pr'] if ENV['CODEBUILD_SOURCE_VERSION']
|
172
|
+
when CODESHIP
|
173
|
+
# https://www.codeship.io/documentation/continuous-integration/set-environment-variables/
|
174
|
+
params[:service] = 'codeship'
|
175
|
+
params[:branch] = ENV['CI_BRANCH']
|
176
|
+
params[:commit] = ENV['CI_COMMIT_ID']
|
177
|
+
params[:build] = ENV['CI_BUILD_NUMBER']
|
178
|
+
params[:build_url] = ENV['CI_BUILD_URL']
|
179
|
+
when DRONEIO
|
180
|
+
# https://semaphoreapp.com/docs/available-environment-variables.html
|
181
|
+
params[:service] = 'drone.io'
|
182
|
+
params[:branch] = ENV['DRONE_BRANCH']
|
183
|
+
params[:commit] = ENV['DRONE_COMMIT_SHA']
|
184
|
+
params[:job] = ENV['DRONE_JOB_NUMBER']
|
185
|
+
params[:build] = ENV['DRONE_BUILD_NUMBER']
|
186
|
+
params[:build_url] = ENV['DRONE_BUILD_LINK'] || ENV['DRONE_BUILD_URL'] || ENV['CI_BUILD_URL']
|
187
|
+
params[:pr] = ENV['DRONE_PULL_REQUEST']
|
188
|
+
params[:tag] = ENV['DRONE_TAG']
|
189
|
+
when GITHUB
|
190
|
+
# https://help.github.com/en/actions/configuring-and-managing-workflows/using-environment-variables#default-environment-variables
|
191
|
+
params[:service] = 'github-actions'
|
192
|
+
params[:branch] = ENV['GITHUB_HEAD_REF'] || ENV['GITHUB_REF'].sub('refs/head/', '')
|
193
|
+
# PR refs are in the format: refs/pull/7/merge for pull_request events
|
194
|
+
params[:pr] = ENV['GITHUB_REF'].split('/')[2] unless ENV['GITHUB_HEAD_REF'].nil? || ENV['GITHUB_HEAD_REF'].empty?
|
195
|
+
params[:slug] = ENV['GITHUB_REPOSITORY']
|
196
|
+
params[:build] = ENV['GITHUB_RUN_ID']
|
197
|
+
params[:commit] = ENV['GITHUB_SHA']
|
198
|
+
when GITLAB
|
199
|
+
# http://doc.gitlab.com/ci/examples/README.html#environmental-variables
|
200
|
+
# https://gitlab.com/gitlab-org/gitlab-ci-runner/blob/master/lib/build.rb#L96
|
201
|
+
# GitLab Runner v9 renamed some environment variables, so we check both old and new variable names.
|
202
|
+
params[:service] = 'gitlab'
|
203
|
+
params[:branch] = ENV['CI_BUILD_REF_NAME'] || ENV['CI_COMMIT_REF_NAME']
|
204
|
+
params[:build] = ENV['CI_BUILD_ID'] || ENV['CI_JOB_ID']
|
205
|
+
slug = ENV['CI_BUILD_REPO'] || ENV['CI_REPOSITORY_URL']
|
206
|
+
params[:slug] = slug.split('/', 4)[-1].sub('.git', '') if slug
|
207
|
+
params[:commit] = ENV['CI_BUILD_REF'] || ENV['CI_COMMIT_SHA']
|
208
|
+
when HEROKU
|
209
|
+
params[:service] = 'heroku'
|
210
|
+
params[:branch] = ENV['HEROKU_TEST_RUN_BRANCH']
|
211
|
+
params[:build] = ENV['HEROKU_TEST_RUN_ID']
|
212
|
+
params[:commit] = ENV['HEROKU_TEST_RUN_COMMIT_VERSION']
|
213
|
+
when JENKINS
|
214
|
+
# https://wiki.jenkins-ci.org/display/JENKINS/Building+a+software+project
|
215
|
+
# https://wiki.jenkins-ci.org/display/JENKINS/GitHub+pull+request+builder+plugin#GitHubpullrequestbuilderplugin-EnvironmentVariables
|
216
|
+
params[:service] = 'jenkins'
|
217
|
+
params[:branch] = ENV['ghprbSourceBranch'] || ENV['GIT_BRANCH']
|
218
|
+
params[:commit] = ENV['ghprbActualCommit'] || ENV['GIT_COMMIT']
|
219
|
+
params[:pr] = ENV['ghprbPullId']
|
220
|
+
params[:build] = ENV['BUILD_NUMBER']
|
221
|
+
params[:root] = ENV['WORKSPACE']
|
222
|
+
params[:build_url] = ENV['BUILD_URL']
|
223
|
+
when SEMAPHORE
|
224
|
+
# https://semaphoreapp.com/docs/available-environment-variables.html
|
225
|
+
params[:service] = 'semaphore'
|
226
|
+
params[:branch] = ENV['BRANCH_NAME']
|
227
|
+
params[:commit] = ENV['REVISION']
|
228
|
+
params[:build] = ENV['SEMAPHORE_BUILD_NUMBER']
|
229
|
+
params[:job] = ENV['SEMAPHORE_CURRENT_THREAD']
|
230
|
+
params[:slug] = ENV['SEMAPHORE_REPO_SLUG']
|
231
|
+
when SHIPPABLE
|
232
|
+
# http://docs.shippable.com/en/latest/config.html#common-environment-variables
|
233
|
+
params[:service] = 'shippable'
|
234
|
+
params[:branch] = ENV['BRANCH']
|
235
|
+
params[:build] = ENV['BUILD_NUMBER']
|
236
|
+
params[:build_url] = ENV['BUILD_URL']
|
237
|
+
params[:pull_request] = ENV['PULL_REQUEST']
|
238
|
+
params[:slug] = ENV['REPO_NAME']
|
239
|
+
params[:commit] = ENV['COMMIT']
|
240
|
+
when SOLANO
|
241
|
+
# http://docs.solanolabs.com/Setup/tddium-set-environment-variables/
|
242
|
+
params[:service] = 'solano'
|
243
|
+
params[:branch] = ENV['TDDIUM_CURRENT_BRANCH']
|
244
|
+
params[:commit] = ENV['TDDIUM_CURRENT_COMMIT']
|
245
|
+
params[:build] = ENV['TDDIUM_TID']
|
246
|
+
params[:pr] = ENV['TDDIUM_PR_ID']
|
247
|
+
when TEAMCITY
|
248
|
+
# https://confluence.jetbrains.com/display/TCD8/Predefined+Build+Parameters
|
249
|
+
# Teamcity does not automatically make build parameters available as environment variables.
|
250
|
+
# Add the following environment parameters to the build configuration
|
251
|
+
# env.TEAMCITY_BUILD_BRANCH = %teamcity.build.branch%
|
252
|
+
# env.TEAMCITY_BUILD_ID = %teamcity.build.id%
|
253
|
+
# env.TEAMCITY_BUILD_URL = %teamcity.serverUrl%/viewLog.html?buildId=%teamcity.build.id%
|
254
|
+
# env.TEAMCITY_BUILD_COMMIT = %system.build.vcs.number%
|
255
|
+
# env.TEAMCITY_BUILD_REPOSITORY = %vcsroot.<YOUR TEAMCITY VCS NAME>.url%
|
256
|
+
params[:service] = 'teamcity'
|
257
|
+
params[:branch] = ENV['TEAMCITY_BUILD_BRANCH']
|
258
|
+
params[:build] = ENV['TEAMCITY_BUILD_ID']
|
259
|
+
params[:build_url] = ENV['TEAMCITY_BUILD_URL']
|
260
|
+
params[:commit] = ENV['TEAMCITY_BUILD_COMMIT']
|
261
|
+
params[:slug] = ENV['TEAMCITY_BUILD_REPOSITORY'].split('/', 4)[-1].sub('.git', '')
|
262
|
+
when TRAVIS
|
263
|
+
# http://docs.travis-ci.com/user/ci-environment/#Environment-variables
|
264
|
+
params[:service] = 'travis'
|
265
|
+
params[:branch] = ENV['TRAVIS_BRANCH']
|
266
|
+
params[:pull_request] = ENV['TRAVIS_PULL_REQUEST']
|
267
|
+
params[:job] = ENV['TRAVIS_JOB_ID']
|
268
|
+
params[:slug] = ENV['TRAVIS_REPO_SLUG']
|
269
|
+
params[:build] = ENV['TRAVIS_JOB_NUMBER']
|
270
|
+
params[:commit] = ENV['TRAVIS_COMMIT']
|
271
|
+
params[:env] = ENV['TRAVIS_RUBY_VERSION']
|
272
|
+
when WERCKER
|
273
|
+
# http://devcenter.wercker.com/articles/steps/variables.html
|
274
|
+
params[:service] = 'wercker'
|
275
|
+
params[:branch] = ENV['WERCKER_GIT_BRANCH']
|
276
|
+
params[:build] = ENV['WERCKER_MAIN_PIPELINE_STARTED']
|
277
|
+
params[:slug] = ENV['WERCKER_GIT_OWNER'] + '/' + ENV['WERCKER_GIT_REPOSITORY']
|
278
|
+
params[:commit] = ENV['WERCKER_GIT_COMMIT']
|
279
|
+
end
|
280
|
+
|
281
|
+
if params[:branch].nil?
|
282
|
+
# find branch, commit, repo from git command
|
283
|
+
branch = `git rev-parse --abbrev-ref HEAD`.strip
|
284
|
+
params[:branch] = branch != 'HEAD' ? branch : 'master'
|
285
|
+
end
|
286
|
+
|
287
|
+
if !ENV['VCS_COMMIT_ID'].nil?
|
288
|
+
params[:commit] = ENV['VCS_COMMIT_ID']
|
289
|
+
|
290
|
+
elsif params[:commit].nil?
|
291
|
+
params[:commit] = `git rev-parse HEAD`.strip
|
292
|
+
end
|
293
|
+
|
294
|
+
slug = ENV['CODECOV_SLUG']
|
295
|
+
params[:slug] = slug unless slug.nil?
|
296
|
+
|
297
|
+
params[:pr] = params[:pr].sub('#', '') unless params[:pr].nil?
|
298
|
+
|
299
|
+
params
|
300
|
+
end
|
301
|
+
|
302
|
+
def retry_request(req, https)
|
303
|
+
retries = 3
|
304
|
+
begin
|
305
|
+
response = https.request(req)
|
306
|
+
rescue Timeout::Error, SocketError => e
|
307
|
+
retries -= 1
|
308
|
+
|
309
|
+
if retries.zero?
|
310
|
+
puts 'Timeout or connection error uploading coverage reports to Codecov. Out of retries.'
|
311
|
+
puts e
|
312
|
+
return response
|
313
|
+
end
|
314
|
+
|
315
|
+
puts 'Timeout or connection error uploading coverage reports to Codecov. Retrying...'
|
316
|
+
puts e
|
317
|
+
retry
|
318
|
+
rescue StandardError => e
|
319
|
+
puts 'Error uploading coverage reports to Codecov. Sorry'
|
320
|
+
puts e.class.name
|
321
|
+
puts e
|
322
|
+
return response
|
323
|
+
end
|
324
|
+
|
325
|
+
response
|
326
|
+
end
|
327
|
+
|
328
|
+
def create_report(report)
|
329
|
+
result = {
|
330
|
+
'meta' => {
|
331
|
+
'version' => 'codecov-ruby/v' + VERSION
|
332
|
+
}
|
333
|
+
}
|
334
|
+
result.update(result_to_codecov(report))
|
335
|
+
result
|
336
|
+
end
|
337
|
+
|
338
|
+
def gzip_report(report)
|
339
|
+
puts ['==>'.green, 'Gzipping contents'].join(' ')
|
340
|
+
|
341
|
+
io = StringIO.new
|
342
|
+
gzip = Zlib::GzipWriter.new(io)
|
343
|
+
gzip << report
|
344
|
+
gzip.close
|
345
|
+
|
346
|
+
io.string
|
347
|
+
end
|
348
|
+
|
349
|
+
def upload_to_codecov(ci, report)
|
350
|
+
url = ENV['CODECOV_URL'] || 'https://codecov.io'
|
351
|
+
is_enterprise = url != 'https://codecov.io'
|
352
|
+
|
353
|
+
params = build_params(ci)
|
354
|
+
params_secret_token = params.clone
|
355
|
+
params_secret_token['token'] = 'secret'
|
356
|
+
|
357
|
+
query = URI.encode_www_form(params)
|
358
|
+
query_without_token = URI.encode_www_form(params_secret_token)
|
359
|
+
|
360
|
+
gzipped_report = gzip_report(report['codecov'])
|
361
|
+
|
362
|
+
report['params'] = params
|
363
|
+
report['query'] = query
|
364
|
+
|
365
|
+
puts ['==>'.green, 'Uploading reports'].join(' ')
|
366
|
+
puts " url: #{url}"
|
367
|
+
puts " query: #{query_without_token}"
|
368
|
+
|
369
|
+
response = false
|
370
|
+
unless is_enterprise
|
371
|
+
response = upload_to_v4(url, gzipped_report, query, query_without_token)
|
372
|
+
return false if response == false
|
373
|
+
end
|
374
|
+
|
375
|
+
response || upload_to_v2(url, gzipped_report, query, query_without_token)
|
376
|
+
end
|
377
|
+
|
378
|
+
def upload_to_v4(url, report, query, query_without_token)
|
379
|
+
uri = URI.parse(url.chomp('/') + '/upload/v4')
|
380
|
+
https = Net::HTTP.new(uri.host, uri.port)
|
381
|
+
https.use_ssl = !url.match(/^https/).nil?
|
382
|
+
|
383
|
+
puts ['-> '.green, 'Pinging Codecov'].join(' ')
|
384
|
+
puts "#{url}#{uri.path}?#{query_without_token}"
|
385
|
+
|
386
|
+
req = Net::HTTP::Post.new(
|
387
|
+
"#{uri.path}?#{query}",
|
388
|
+
{
|
389
|
+
'X-Reduced-Redundancy' => 'false',
|
390
|
+
'X-Content-Encoding' => 'application/x-gzip',
|
391
|
+
'Content-Type' => 'text/plain'
|
392
|
+
}
|
393
|
+
)
|
394
|
+
response = retry_request(req, https)
|
395
|
+
if !response&.code || response.code == '400'
|
396
|
+
puts response&.body&.red
|
397
|
+
return false
|
398
|
+
end
|
399
|
+
|
400
|
+
reports_url = response.body.lines[0]
|
401
|
+
s3target = response.body.lines[1]
|
402
|
+
puts ['-> '.green, 'Uploading to'].join(' ')
|
403
|
+
puts s3target
|
404
|
+
|
405
|
+
uri = URI(s3target)
|
406
|
+
https = Net::HTTP.new(uri.host, uri.port)
|
407
|
+
https.use_ssl = true
|
408
|
+
req = Net::HTTP::Put.new(
|
409
|
+
s3target,
|
410
|
+
{
|
411
|
+
'Content-Encoding' => 'gzip',
|
412
|
+
'Content-Type' => 'text/plain'
|
413
|
+
}
|
414
|
+
)
|
415
|
+
req.body = report
|
416
|
+
res = retry_request(req, https)
|
417
|
+
if res.body == ''
|
418
|
+
{
|
419
|
+
'uploaded' => true,
|
420
|
+
'url' => reports_url,
|
421
|
+
'meta' => {
|
422
|
+
'status' => res.code
|
423
|
+
},
|
424
|
+
'message' => 'Coverage reports upload successfully'
|
425
|
+
}.to_json
|
426
|
+
else
|
427
|
+
puts ['-> '.black, 'Could not upload reports via v4 API, defaulting to v2'].join(' ')
|
428
|
+
puts res.body.red
|
429
|
+
nil
|
430
|
+
end
|
431
|
+
end
|
432
|
+
|
433
|
+
def upload_to_v2(url, report, query, query_without_token)
|
434
|
+
uri = URI.parse(url.chomp('/') + '/upload/v2')
|
435
|
+
https = Net::HTTP.new(uri.host, uri.port)
|
436
|
+
https.use_ssl = !url.match(/^https/).nil?
|
437
|
+
|
438
|
+
puts ['-> '.green, 'Uploading to Codecov'].join(' ')
|
439
|
+
puts "#{url}#{uri.path}?#{query_without_token}"
|
440
|
+
|
441
|
+
req = Net::HTTP::Post.new(
|
442
|
+
"#{uri.path}?#{query}",
|
443
|
+
{
|
444
|
+
'Accept' => 'application/json',
|
445
|
+
'Content-Encoding' => 'gzip',
|
446
|
+
'Content-Type' => 'text/plain',
|
447
|
+
'X-Content-Encoding' => 'gzip'
|
448
|
+
}
|
449
|
+
)
|
450
|
+
req.body = report
|
451
|
+
res = retry_request(req, https)
|
452
|
+
res&.body
|
453
|
+
end
|
454
|
+
|
455
|
+
def handle_report_response(report)
|
456
|
+
if report['result']['uploaded']
|
457
|
+
puts " View reports at #{report['result']['url']}"
|
458
|
+
else
|
459
|
+
puts ' X> Failed to upload coverage reports'.red
|
460
|
+
end
|
461
|
+
end
|
462
|
+
|
463
|
+
def format(result, disable_net_blockers = true)
|
464
|
+
net_blockers(:off) if disable_net_blockers
|
465
|
+
|
466
|
+
display_header
|
467
|
+
ci = detect_ci
|
468
|
+
report = create_report(result)
|
469
|
+
response = upload_to_codecov(ci, report)
|
470
|
+
if response == false
|
471
|
+
report['result'] = { 'uploaded' => false }
|
472
|
+
return report
|
473
|
+
end
|
474
|
+
|
475
|
+
report['result'] = JSON.parse(response)
|
476
|
+
handle_report_response(report)
|
477
|
+
|
478
|
+
net_blockers(:on) if disable_net_blockers
|
479
|
+
report
|
480
|
+
end
|
481
|
+
|
482
|
+
private
|
483
|
+
|
484
|
+
# Format SimpleCov coverage data for the Codecov.io API.
|
485
|
+
#
|
486
|
+
# @param result [SimpleCov::Result] The coverage data to process.
|
487
|
+
# @return [Hash]
|
488
|
+
def result_to_codecov(result)
|
489
|
+
{
|
490
|
+
'codecov' => result_to_codecov_report(result),
|
491
|
+
'coverage' => result_to_codecov_coverage(result),
|
492
|
+
'messages' => result_to_codecov_messages(result)
|
493
|
+
}
|
494
|
+
end
|
495
|
+
|
496
|
+
def result_to_codecov_report(result)
|
497
|
+
report = file_network.join("\n").concat("\n")
|
498
|
+
report = report.concat({ 'coverage' => result_to_codecov_coverage(result) }.to_json)
|
499
|
+
report
|
500
|
+
end
|
501
|
+
|
502
|
+
def file_network
|
503
|
+
invalid_file_types = [
|
504
|
+
'woff', 'eot', 'otf', # fonts
|
505
|
+
'gif', 'png', 'jpg', 'jpeg', 'psd', # images
|
506
|
+
'ptt', 'pptx', 'numbers', 'pages', 'md', 'txt', 'xlsx', 'docx', 'doc', 'pdf', 'csv', # docs
|
507
|
+
'yml', 'yaml', '.gitignore'
|
508
|
+
].freeze
|
509
|
+
|
510
|
+
invalid_directories = [
|
511
|
+
'node_modules/'
|
512
|
+
]
|
513
|
+
|
514
|
+
puts ['==>'.green, 'Appending file network'].join(' ')
|
515
|
+
network = []
|
516
|
+
Dir['**/*'].keep_if do |file|
|
517
|
+
if File.file?(file) && !file.end_with?(*invalid_file_types) && !file.include?(*invalid_directories)
|
518
|
+
network.push(file)
|
519
|
+
end
|
520
|
+
end
|
521
|
+
network.push('<<<<<< network')
|
522
|
+
network
|
523
|
+
end
|
524
|
+
|
525
|
+
# Format SimpleCov coverage data for the Codecov.io coverage API.
|
526
|
+
#
|
527
|
+
# @param result [SimpleCov::Result] The coverage data to process.
|
528
|
+
# @return [Hash<String, Array>]
|
529
|
+
def result_to_codecov_coverage(result)
|
530
|
+
result.files.each_with_object({}) do |file, memo|
|
531
|
+
memo[shortened_filename(file)] = file_to_codecov(file)
|
532
|
+
end
|
533
|
+
end
|
534
|
+
|
535
|
+
# Format SimpleCov coverage data for the Codecov.io messages API.
|
536
|
+
#
|
537
|
+
# @param result [SimpleCov::Result] The coverage data to process.
|
538
|
+
# @return [Hash<String, Hash>]
|
539
|
+
def result_to_codecov_messages(result)
|
540
|
+
result.files.each_with_object({}) do |file, memo|
|
541
|
+
memo[shortened_filename(file)] = file.lines.each_with_object({}) do |line, lines_memo|
|
542
|
+
lines_memo[line.line_number.to_s] = 'skipped' if line.skipped?
|
543
|
+
end
|
544
|
+
end
|
545
|
+
end
|
546
|
+
|
547
|
+
# Format coverage data for a single file for the Codecov.io API.
|
548
|
+
#
|
549
|
+
# @param file [SimpleCov::SourceFile] The file to process.
|
550
|
+
# @return [Array<nil, Integer>]
|
551
|
+
def file_to_codecov(file)
|
552
|
+
# Initial nil is required to offset line numbers.
|
553
|
+
[nil] + file.lines.map do |line|
|
554
|
+
if line.skipped?
|
555
|
+
nil
|
556
|
+
else
|
557
|
+
line.coverage
|
558
|
+
end
|
559
|
+
end
|
560
|
+
end
|
561
|
+
|
562
|
+
# Get a filename relative to the project root. Based on
|
563
|
+
# https://github.com/colszowka/simplecov-html, copyright Christoph Olszowka.
|
564
|
+
#
|
565
|
+
# @param file [SimeplCov::SourceFile] The file to use.
|
566
|
+
# @return [String]
|
567
|
+
def shortened_filename(file)
|
568
|
+
file.filename.gsub(/^#{SimpleCov.root}/, '.').gsub(%r{^\./}, '')
|
569
|
+
end
|
570
|
+
|
571
|
+
# Toggle VCR and WebMock on or off
|
572
|
+
#
|
573
|
+
# @param switch Toggle switch for Net Blockers.
|
574
|
+
# @return [Boolean]
|
575
|
+
def net_blockers(switch)
|
576
|
+
throw 'Only :on or :off' unless %i[on off].include? switch
|
577
|
+
|
578
|
+
if defined?(VCR)
|
579
|
+
case switch
|
580
|
+
when :on
|
581
|
+
VCR.turn_on!
|
582
|
+
when :off
|
583
|
+
VCR.turn_off!(ignore_cassettes: true)
|
584
|
+
end
|
585
|
+
end
|
586
|
+
|
587
|
+
if defined?(WebMock)
|
588
|
+
# WebMock on by default
|
589
|
+
# VCR depends on WebMock 1.8.11; no method to check whether enabled.
|
590
|
+
case switch
|
591
|
+
when :on
|
592
|
+
WebMock.enable!
|
593
|
+
when :off
|
594
|
+
WebMock.disable!
|
595
|
+
end
|
596
|
+
end
|
597
|
+
|
598
|
+
true
|
599
|
+
end
|
600
|
+
end
|
601
|
+
|
602
|
+
# https://stackoverflow.com/a/11482430/5769383
|
603
|
+
class String
|
604
|
+
# colorization
|
605
|
+
def colorize(color_code)
|
606
|
+
"\e[#{color_code}m#{self}\e[0m"
|
607
|
+
end
|
608
|
+
|
609
|
+
def black
|
610
|
+
colorize(30)
|
611
|
+
end
|
612
|
+
|
613
|
+
def red
|
614
|
+
colorize(31)
|
615
|
+
end
|
616
|
+
|
617
|
+
def green
|
618
|
+
colorize(32)
|
619
|
+
end
|
620
|
+
end
|