1 | var fs = require('fs')
|
2 | var path = require('path')
|
3 | var request = require('teeny-request').teenyRequest
|
4 | var urlgrey = require('urlgrey')
|
5 | var jsYaml = require('js-yaml')
|
6 | var walk = require('ignore-walk')
|
7 | var execSync = require('child_process').execSync
|
8 |
|
9 | var detectProvider = require('./detect')
|
10 |
|
11 | var version = 'v' + require('../package.json').version
|
12 |
|
13 | var patterns,
|
14 | more_patterns = ''
|
15 |
|
16 | var isWindows =
|
17 | process.platform.match(/win32/) || process.platform.match(/win64/)
|
18 |
|
19 | if (!isWindows) {
|
20 | patterns =
|
21 | "-type f \\( -name '*coverage.*' " +
|
22 | "-or -name 'nosetests.xml' " +
|
23 | "-or -name 'jacoco*.xml' " +
|
24 | "-or -name 'clover.xml' " +
|
25 | "-or -name 'report.xml' " +
|
26 | "-or -name 'cobertura.xml' " +
|
27 | "-or -name 'luacov.report.out' " +
|
28 | "-or -name 'lcov.info' " +
|
29 | "-or -name '*.lcov' " +
|
30 | "-or -name 'gcov.info' " +
|
31 | "-or -name '*.gcov' " +
|
32 | "-or -name '*.lst' \\) " +
|
33 | "-not -name '*.sh' " +
|
34 | "-not -name '*.data' " +
|
35 | "-not -name '*.py' " +
|
36 | "-not -name '*.class' " +
|
37 | "-not -name '*.xcconfig' " +
|
38 | "-not -name 'Coverage.profdata' " +
|
39 | "-not -name 'phpunit-code-coverage.xml' " +
|
40 | "-not -name 'coverage.serialized' " +
|
41 | "-not -name '*.pyc' " +
|
42 | "-not -name '*.cfg' " +
|
43 | "-not -name '*.egg' " +
|
44 | "-not -name '*.whl' " +
|
45 | "-not -name '*.html' " +
|
46 | "-not -name '*.js' " +
|
47 | "-not -name '*.cpp' " +
|
48 | "-not -name 'coverage.jade' " +
|
49 | "-not -name 'include.lst' " +
|
50 | "-not -name 'inputFiles.lst' " +
|
51 | "-not -name 'createdFiles.lst' " +
|
52 | "-not -name 'coverage.html' " +
|
53 | "-not -name 'scoverage.measurements.*' " +
|
54 | "-not -name 'test_*_coverage.txt' " +
|
55 | "-not -path '*/vendor/*' " +
|
56 | "-not -path '*/htmlcov/*' " +
|
57 | "-not -path '*/home/cainus/*' " +
|
58 | "-not -path '*/virtualenv/*' " +
|
59 | "-not -path '*/js/generated/coverage/*' " +
|
60 | "-not -path '*/.virtualenv/*' " +
|
61 | "-not -path '*/virtualenvs/*' " +
|
62 | "-not -path '*/.virtualenvs/*' " +
|
63 | "-not -path '*/.env/*' " +
|
64 | "-not -path '*/.envs/*' " +
|
65 | "-not -path '*/env/*' " +
|
66 | "-not -path '*/envs/*' " +
|
67 | "-not -path '*/.venv/*' " +
|
68 | "-not -path '*/.venvs/*' " +
|
69 | "-not -path '*/venv/*' " +
|
70 | "-not -path '*/venvs/*' " +
|
71 | "-not -path '*/.git/*' " +
|
72 | "-not -path '*/.hg/*' " +
|
73 | "-not -path '*/.tox/*' " +
|
74 | "-not -path '*/__pycache__/*' " +
|
75 | "-not -path '*/.egg-info*' " +
|
76 | "-not -path '*/$bower_components/*' " +
|
77 | "-not -path '*/node_modules/*' " +
|
78 | "-not -path '*/conftest_*.c.gcov'"
|
79 | } else {
|
80 | patterns =
|
81 | '/a-d /b /s *coverage.* ' +
|
82 | '/s nosetests.xml ' +
|
83 | '/s jacoco*.xml ' +
|
84 | '/s clover.xml ' +
|
85 | '/s report.xml ' +
|
86 | '/s cobertura.xml ' +
|
87 | '/s luacov.report.out ' +
|
88 | '/s lcov.info ' +
|
89 | '/s *.lcov ' +
|
90 | '/s gcov.info ' +
|
91 | '/s *.gcov ' +
|
92 | '/s *.lst' +
|
93 | '| findstr /i /v \\.sh$ ' +
|
94 | '| findstr /i /v \\.data$ ' +
|
95 | '| findstr /i /v \\.py$ ' +
|
96 | '| findstr /i /v \\.class$ ' +
|
97 | '| findstr /i /v \\.xcconfig$ ' +
|
98 | '| findstr /i /v Coverage\\.profdata$ ' +
|
99 | '| findstr /i /v phpunit-code-coverage\\.xml$ ' +
|
100 | '| findstr /i /v coverage\\.serialized$ ' +
|
101 | '| findstr /i /v \\.pyc$ ' +
|
102 | '| findstr /i /v \\.cfg$ ' +
|
103 | '| findstr /i /v \\.egg$ ' +
|
104 | '| findstr /i /v \\.whl$ ' +
|
105 | '| findstr /i /v \\.html$ ' +
|
106 | '| findstr /i /v \\.js$ ' +
|
107 | '| findstr /i /v \\.cpp$ ' +
|
108 | '| findstr /i /v coverage\\.jade$ ' +
|
109 | '| findstr /i /v include\\.lst$ ' +
|
110 | '| findstr /i /v inputFiles\\.lst$ ' +
|
111 | '| findstr /i /v createdFiles\\.lst$ ' +
|
112 | '| findstr /i /v coverage\\.html$ ' +
|
113 | '| findstr /i /v scoverage\\.measurements\\..* ' +
|
114 | '| findstr /i /v test_.*_coverage\\.txt ' +
|
115 | '| findstr /i /v \\vendor\\ ' +
|
116 | '| findstr /i /v \\htmlcov\\ ' +
|
117 | '| findstr /i /v \\home\\cainus\\ ' +
|
118 | '| findstr /i /v \\js\\generated\\coverage\\ ' +
|
119 | '| findstr /i /v \\virtualenv\\ ' +
|
120 | '| findstr /i /v \\virtualenvs\\ ' +
|
121 | '| findstr /i /v \\\\.virtualenv\\ ' +
|
122 | '| findstr /i /v \\\\.virtualenvs\\ ' +
|
123 | '| findstr /i /v \\\\.env\\ ' +
|
124 | '| findstr /i /v \\\\.envs\\ ' +
|
125 | '| findstr /i /v \\env\\ ' +
|
126 | '| findstr /i /v \\envs\\ ' +
|
127 | '| findstr /i /v \\\\.venv\\ ' +
|
128 | '| findstr /i /v \\\\.venvs\\ ' +
|
129 | '| findstr /i /v \\venv\\ ' +
|
130 | '| findstr /i /v \\venvs\\ ' +
|
131 | '| findstr /i /v \\\\.git\\ ' +
|
132 | '| findstr /i /v \\\\.hg\\ ' +
|
133 | '| findstr /i /v \\\\.tox\\ ' +
|
134 | '| findstr /i /v \\__pycache__\\ ' +
|
135 | '| findstr /i /v \\\\.egg-info* ' +
|
136 | '| findstr /i /v \\\\$bower_components\\ ' +
|
137 | '| findstr /i /v \\node_modules\\ ' +
|
138 | '| findstr /i /v \\conftest_.*\\.c\\.gcov '
|
139 | }
|
140 |
|
141 | var sendToCodecovV2 = function(
|
142 | codecov_endpoint,
|
143 | query,
|
144 | upload_body,
|
145 | on_success,
|
146 | on_failure
|
147 | ) {
|
148 |
|
149 | request(
|
150 | {
|
151 | uri: urlgrey(codecov_endpoint + '/upload/v2')
|
152 | .query(query)
|
153 | .toString(),
|
154 | method: 'POST',
|
155 | body: upload_body,
|
156 | headers: {
|
157 | 'Content-Type': 'text/plain',
|
158 | Accept: 'text/plain',
|
159 | },
|
160 | },
|
161 | function(err, response) {
|
162 | if (err || response.statusCode !== 200) {
|
163 | console.log(' ' + (err || response.body))
|
164 | return response
|
165 | ? on_failure(response.statusCode, response.body)
|
166 | : on_failure(err.code, err.message)
|
167 | } else {
|
168 | console.log(' Success!')
|
169 | console.log(' View report at: ' + response.body)
|
170 | return on_success(response.body)
|
171 | }
|
172 | }
|
173 | )
|
174 | }
|
175 |
|
176 | var sendToCodecovV3 = function(
|
177 | codecov_endpoint,
|
178 | query,
|
179 | upload_body,
|
180 | on_success,
|
181 | on_failure
|
182 | ) {
|
183 |
|
184 | request(
|
185 | {
|
186 | uri: urlgrey(codecov_endpoint + '/upload/v4')
|
187 | .query(query)
|
188 | .toString(),
|
189 | method: 'POST',
|
190 | body: '',
|
191 | headers: {
|
192 | 'Content-Type': 'text/plain',
|
193 | Accept: 'text/plain',
|
194 | },
|
195 | },
|
196 | function(err, response, result) {
|
197 | if (err) {
|
198 | sendToCodecovV2(
|
199 | codecov_endpoint,
|
200 | query,
|
201 | upload_body,
|
202 | on_success,
|
203 | on_failure
|
204 | )
|
205 | } else {
|
206 | var codecov_report_url = result.split('\n')[0]
|
207 | request(
|
208 | {
|
209 | uri: result.split('\n')[1],
|
210 | method: 'PUT',
|
211 | body: upload_body,
|
212 | headers: {
|
213 | 'Content-Type': 'text/plain',
|
214 | },
|
215 | },
|
216 | function(err) {
|
217 | if (err) {
|
218 | sendToCodecovV2(
|
219 | codecov_endpoint,
|
220 | query,
|
221 | upload_body,
|
222 | on_success,
|
223 | on_failure
|
224 | )
|
225 | } else {
|
226 | console.log(' Success!')
|
227 | console.log(' View report at: ' + codecov_report_url)
|
228 | on_success(codecov_report_url)
|
229 | }
|
230 | }
|
231 | )
|
232 | }
|
233 | }
|
234 | )
|
235 | }
|
236 |
|
237 | var upload = function(args, on_success, on_failure) {
|
238 |
|
239 | var codecov_endpoint =
|
240 | args.options.url ||
|
241 | process.env.codecov_url ||
|
242 | process.env.CODECOV_URL ||
|
243 | 'https://codecov.io'
|
244 | var query = {}
|
245 | var debug = []
|
246 | var yamlFile =
|
247 | args.options.yml ||
|
248 | process.env.codecov_yml ||
|
249 | process.env.CODECOV_YML ||
|
250 | 'codecov.yml'
|
251 |
|
252 | console.log(
|
253 | '' +
|
254 | ' _____ _ \n' +
|
255 | ' / ____| | | \n' +
|
256 | '| | ___ __| | ___ ___ _____ __ \n' +
|
257 | '| | / _ \\ / _` |/ _ \\/ __/ _ \\ \\ / / \n' +
|
258 | '| |___| (_) | (_| | __/ (_| (_) \\ V / \n' +
|
259 | ' \\_____\\___/ \\__,_|\\___|\\___\\___/ \\_/ \n' +
|
260 | ' ' +
|
261 | version
|
262 | )
|
263 |
|
264 | if ((args.options.disable || '').split(',').indexOf('detect') === -1) {
|
265 | console.log('==> Detecting CI Provider')
|
266 | query = detectProvider()
|
267 | } else {
|
268 | debug.push('disabled detect')
|
269 | }
|
270 |
|
271 | query.yaml = [yamlFile, '.codecov.yml'].reduce(function(result, file) {
|
272 | return (
|
273 | result ||
|
274 | (fs.existsSync(path.resolve(process.cwd(), file))
|
275 | ? path.resolve(process.cwd(), file)
|
276 | : undefined)
|
277 | )
|
278 | }, undefined)
|
279 |
|
280 | if (args.options.build) {
|
281 | query.build = args.options.build
|
282 | }
|
283 |
|
284 | if (args.options.commit) {
|
285 | query.commit = args.options.commit
|
286 | }
|
287 |
|
288 | if (args.options.branch) {
|
289 | query.branch = args.options.branch
|
290 | }
|
291 |
|
292 | if (args.options.slug) {
|
293 | query.slug = args.options.slug
|
294 | }
|
295 |
|
296 | var flags =
|
297 | args.options.flags || process.env.codecov_flags || process.env.CODECOV_FLAGS
|
298 | if (flags) {
|
299 | query.flags = flags
|
300 | }
|
301 |
|
302 | var yamlToken
|
303 | try {
|
304 | var loadedYamlFile = jsYaml.safeLoad(fs.readFileSync(query.yaml, 'utf8'))
|
305 | yamlToken =
|
306 | loadedYamlFile && loadedYamlFile.codecov && loadedYamlFile.codecov.token
|
307 | } catch (e) {
|
308 |
|
309 | }
|
310 | var token =
|
311 | args.options.token ||
|
312 | yamlToken ||
|
313 | process.env.codecov_token ||
|
314 | process.env.CODECOV_TOKEN
|
315 | if (token) {
|
316 | query.token = token
|
317 | }
|
318 |
|
319 | query.package = 'node-' + version
|
320 |
|
321 | console.log('==> Configuration: ')
|
322 | console.log(' Endpoint: ' + codecov_endpoint)
|
323 |
|
324 | console.log({
|
325 | commit: query.commit,
|
326 | branch: query.branch,
|
327 | package: query.package,
|
328 | })
|
329 |
|
330 | var upload = ''
|
331 |
|
332 |
|
333 | var env_found = false
|
334 | if (args.options.env || process.env.CODECOV_ENV || process.env.codecov_env) {
|
335 | var env = (
|
336 | args.options.env +
|
337 | ',' +
|
338 | (process.env.CODECOV_ENV || '') +
|
339 | ',' +
|
340 | (process.env.codecov_env || '')
|
341 | ).split(',')
|
342 | for (var i = env.length - 1; i >= 0; i--) {
|
343 | if (env[i]) {
|
344 | upload += env[i] + '=' + (process.env[env[i]] || '').toString() + '\n'
|
345 | env_found = true
|
346 | }
|
347 | }
|
348 | if (env_found) {
|
349 | upload += '<<<<<< ENV\n'
|
350 | }
|
351 | }
|
352 |
|
353 |
|
354 | var root = path.resolve(args.options.root || query.root || '.')
|
355 | console.log('==> Building file structure')
|
356 | try {
|
357 | upload +=
|
358 | execSync('git ls-files || hg locate', { cwd: root })
|
359 | .toString()
|
360 | .trim() + '\n<<<<<< network\n'
|
361 | } catch (err) {
|
362 |
|
363 | upload +=
|
364 | walk
|
365 | .sync({ path: root, ignoreFiles: ['.gitignore', '.hgignore'] })
|
366 | .join('\n')
|
367 | .trim() + '\n<<<<<< network\n'
|
368 | }
|
369 |
|
370 | if ((args.options.disable || '').split(',').indexOf('gcov') === -1) {
|
371 | try {
|
372 | console.log('==> Generating gcov reports (skip via --disable=gcov)')
|
373 | var gcg = args.options['gcov-glob'] || ''
|
374 | if (gcg) {
|
375 | if (!isWindows) {
|
376 | gcg = gcg
|
377 | .split(' ')
|
378 | .map(function(p) {
|
379 | return "-not -path '" + p + "'"
|
380 | })
|
381 | .join(' ')
|
382 | } else {
|
383 | gcg = gcg
|
384 | .split(' ')
|
385 | .map(function(p) {
|
386 | return '^| findstr /i /v ' + p
|
387 | })
|
388 | .join(' ')
|
389 | }
|
390 | }
|
391 | var gcov
|
392 | if (!isWindows) {
|
393 | gcov =
|
394 | 'find ' +
|
395 | (sanitizeVar(args.options['gcov-root']) || root) +
|
396 | " -type f -name '*.gcno' " +
|
397 | gcg +
|
398 | ' -exec ' +
|
399 | (sanitizeVar(args.options['gcov-exec']) || 'gcov') +
|
400 | ' ' +
|
401 | (sanitizeVar(args.options['gcov-args']) || '') +
|
402 | ' {} +'
|
403 | } else {
|
404 |
|
405 |
|
406 | gcov =
|
407 | 'for /f "delims=" %g in (\'dir /a-d /b /s *.gcno ' +
|
408 | gcg +
|
409 | "') do " +
|
410 | (sanitizeVar(args.options['gcov-exec']) || 'gcov') +
|
411 | ' ' +
|
412 | (sanitizeVar(args.options['gcov-args']) || '') +
|
413 | ' %g'
|
414 | }
|
415 | debug.push(gcov)
|
416 | console.log(' $ ' + gcov)
|
417 | execSync(gcov)
|
418 | } catch (e) {
|
419 | console.log(' Failed to run gcov command.')
|
420 | }
|
421 | } else {
|
422 | debug.push('disabled gcov')
|
423 | }
|
424 |
|
425 |
|
426 | var bowerrc
|
427 | if (!isWindows) {
|
428 | bowerrc = execSync('test -f .bowerrc && cat .bowerrc || echo ""', {
|
429 | cwd: root,
|
430 | })
|
431 | .toString()
|
432 | .trim()
|
433 | } else {
|
434 | bowerrc = execSync('if exist .bowerrc type .bowerrc', { cwd: root })
|
435 | .toString()
|
436 | .trim()
|
437 | }
|
438 | if (bowerrc) {
|
439 | bowerrc = JSON.parse(bowerrc).directory
|
440 | if (bowerrc) {
|
441 | if (!isWindows) {
|
442 | more_patterns =
|
443 | " -not -path '*/" + bowerrc.toString().replace(/\/$/, '') + "/*'"
|
444 | } else {
|
445 | more_patterns =
|
446 | '| findstr /i /v \\' + bowerrc.toString().replace(/\/$/, '') + '\\'
|
447 | }
|
448 | }
|
449 | }
|
450 |
|
451 | var files = [],
|
452 | file = null
|
453 | if (args.options.pipe) {
|
454 |
|
455 | upload += '# path=piped\n' + args.options.pipe.join('') + '\n<<<<<< EOF\n'
|
456 | console.log('==> Reading report from stdin')
|
457 | } else if (args.options.file) {
|
458 |
|
459 | file = args.options.file
|
460 | console.log('==> Targeting specific file')
|
461 | try {
|
462 | upload +=
|
463 | '# path=' +
|
464 | file +
|
465 | '\n' +
|
466 | fs.readFileSync(file, 'utf8').toString() +
|
467 | '\n<<<<<< EOF\n'
|
468 | console.log(' + ' + file)
|
469 | files.push(file)
|
470 | } catch (e) {
|
471 | debug.push('failed: ' + file.split('/').pop())
|
472 | console.log(' X Failed to read file at ' + file)
|
473 | }
|
474 | } else if ((args.options.disable || '').split(',').indexOf('search') === -1) {
|
475 | console.log('==> Scanning for reports')
|
476 | var _files
|
477 | if (!isWindows) {
|
478 | _files = execSync('find ' + root + ' ' + patterns + more_patterns)
|
479 | .toString()
|
480 | .trim()
|
481 | .split('\n')
|
482 | } else {
|
483 |
|
484 |
|
485 | _files = execSync('dir ' + patterns + more_patterns)
|
486 | .toString()
|
487 | .trim()
|
488 | .split('\r\n')
|
489 | }
|
490 | if (_files) {
|
491 | for (var i2 = _files.length - 1; i2 >= 0; i2--) {
|
492 | file = _files[i2]
|
493 | try {
|
494 | upload +=
|
495 | '# path=' +
|
496 | file +
|
497 | '\n' +
|
498 | fs.readFileSync(file, 'utf8').toString() +
|
499 | '\n<<<<<< EOF\n'
|
500 | console.log(' + ' + file)
|
501 | files.push(file)
|
502 | } catch (e) {
|
503 | debug.push('failed: ' + file.split('/').pop())
|
504 | console.log(' X Failed to read file at ' + file)
|
505 | }
|
506 | }
|
507 | }
|
508 | } else {
|
509 | debug.push('disabled search')
|
510 | }
|
511 |
|
512 | if (files) {
|
513 |
|
514 | if (args.options.dump) {
|
515 | console.log('-------- DEBUG START --------')
|
516 | console.log(upload)
|
517 | console.log('-------- DEBUG END --------')
|
518 | } else {
|
519 | console.log('==> Uploading reports')
|
520 | var _upload
|
521 | if ((args.options.disable || '').split(',').indexOf('s3') === -1) {
|
522 | _upload = sendToCodecovV3
|
523 | } else {
|
524 | _upload = sendToCodecovV2
|
525 | }
|
526 | _upload(
|
527 | codecov_endpoint,
|
528 | query,
|
529 | upload,
|
530 | function() {
|
531 |
|
532 | if (args.options.clear) {
|
533 | for (var i = files.length - 1; i >= 0; i--) {
|
534 | try {
|
535 | fs.unlinkSync(files[i])
|
536 | } catch (e) {}
|
537 | }
|
538 | }
|
539 | if (on_success) {
|
540 | on_success.apply(this, arguments)
|
541 | }
|
542 | },
|
543 | on_failure || function() {}
|
544 | )
|
545 | }
|
546 | }
|
547 |
|
548 | return {
|
549 | body: upload,
|
550 | files: files,
|
551 | query: query,
|
552 | debug: debug,
|
553 | url: codecov_endpoint,
|
554 | }
|
555 | }
|
556 |
|
557 | function sanitizeVar(arg) {
|
558 | return arg.replace(/&/g, '')
|
559 | }
|
560 |
|
561 | module.exports = {
|
562 | sanitizeVar: sanitizeVar,
|
563 | upload: upload,
|
564 | version: version,
|
565 | sendToCodecovV2: sendToCodecovV2,
|
566 | sendToCodecovV3: sendToCodecovV3,
|
567 | }
|