codecov 0.3.0 → 0.4.0

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