codecov 0.3.0 → 0.4.0

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 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: