diff --git a/cpp_coveralls/__init__.py b/cpp_coveralls/__init__.py index 5996398..6e744dd 100644 --- a/cpp_coveralls/__init__.py +++ b/cpp_coveralls/__init__.py @@ -2,7 +2,7 @@ from __future__ import print_function __author__ = 'Lei Xu ' -__version__ = '0.4.2' +__version__ = '0.4.3' __classifiers__ = [ 'Development Status :: 3 - Alpha', @@ -73,9 +73,9 @@ def run(): # use environment COVERALLS_REPO_TOKEN as a fallback args.repo_token = os.environ.get('COVERALLS_REPO_TOKEN') - # try get service name from yaml first - args.service_name = yml.get('service_name', '') + if not args.service_name: + args.service_name = yml.get('service_name', '') if not args.service_name: # use environment COVERALLS_SERVICE_NAME as a fallback args.service_name = os.environ.get('COVERALLS_SERVICE_NAME') @@ -94,21 +94,29 @@ def run(): args.include.extend(yml.get('include', [])) args.exclude_lines_pattern.extend(yml.get('exclude_lines_pattern', [])) - args.service_job_id = os.environ.get('TRAVIS_JOB_ID', '') + if args.service_job_id is None: + args.service_job_id = os.environ.get('TRAVIS_JOB_ID', '') + + if not args.parallel: + args.parallel = os.getenv('COVERALLS_PARALLEL', False) if args.repo_token == '' and args.service_job_id == '': raise ValueError("\nno coveralls.io token specified and no travis job id found\n" "see --help for examples on how to specify a token\n") - if not args.no_gcov: - coverage.run_gcov(args) - cov_report = coverage.collect(args) - if args.verbose: - print(cov_report) - if args.dryrun: - return 0 - if args.dump: - args.dump.write(json.dumps(cov_report)) - return 0 - - return report.post_report(cov_report, args) + if args.action == 'report': + if not args.no_gcov: + coverage.run_gcov(args) + cov_report = coverage.collect(args) + if args.verbose: + print(cov_report) + if args.dryrun: + return 0 + if args.dump: + args.dump.write(json.dumps(cov_report)) + return 0 + return report.post_report(cov_report, args) + elif args.action == 'finish-report': + return report.finish_report(args) + else: + raise ValueError("Not supported action") diff --git a/cpp_coveralls/coverage.py b/cpp_coveralls/coverage.py index 4f2c6e3..e6d5a7c 100644 --- a/cpp_coveralls/coverage.py +++ b/cpp_coveralls/coverage.py @@ -86,6 +86,11 @@ def create_args(params): help='skip ssl certificate verification when ' 'communicating with the coveralls server', action='store_true', default=False) + parser.add_argument('--service-name', type=str, default=None, help="The CI service or other environment in which the test suite was run. This can be anything, but certain services have special features (travis-ci, travis-pro, or coveralls-ruby).") + parser.add_argument('--service-job-id', type=str, default=None, help="A unique identifier of the job on the service specified by service_name.") + parser.add_argument('--service-build-number', type=str, default=None, help="The build number. Will default to chronological numbering from builds on repo.") + parser.add_argument('--parallel', action='store_true', help="Send a few reports and merge") + parser.add_argument('action', type=str, default='report', nargs='?', choices=['report', 'finish-report'], help="If the --parallel reports were used, the reports has to be finalized with a 'finish-report' action.") return parser.parse_args(params) @@ -225,16 +230,18 @@ def run_gcov(args): gcov_files.append(files) if re.search(r".*\.c.*", basename): path = os.path.abspath(os.path.join(root, basename + '.o')) - subprocess.call( - 'cd "%s" && %s %s%s "%s"' % ( - gcov_root, args.gcov, args.gcov_options, local_gcov_options, path), - shell=True) + cmd = 'cd "%s" && %s %s%s "%s"' % (gcov_root, args.gcov, args.gcov_options, local_gcov_options, path) + if args.verbose: + print(cmd) + + subprocess.call(cmd, shell=True) else: path = os.path.abspath(os.path.join(root, basename)) - subprocess.call( - 'cd "%s" && %s %s%s "%s"' % ( - gcov_root, args.gcov, args.gcov_options, local_gcov_options, filepath), - shell=True) + cmd = 'cd "%s" && %s %s%s "%s"' % (gcov_root, args.gcov, args.gcov_options, local_gcov_options, filepath) + if args.verbose: + print(cmd) + + subprocess.call(cmd, shell=True) # If gcov was run in the build root move the resulting gcov # file to the same directory as the .o file. if custom_gcov_root: @@ -276,23 +283,32 @@ def parse_gcov_file(args, fobj, filename): sys.stderr.write("Warning: %s:%d: LCOV_EXCL_STOP is the " "correct keyword\n" % (filename, line_num)) ignoring = False - if cov_num == '-': + + line_num -= 1 + while len(coverage) <= line_num: coverage.append(None) + + if cov_num == '-': + if coverage[line_num] is None: + coverage[line_num] = None elif cov_num == '#####': # Avoid false positives. if ( ignoring or any([re.search(pattern, text) for pattern in args.exclude_lines_pattern]) ): - coverage.append(None) + if coverage[line_num] is None: + coverage[line_num] = None else: - coverage.append(0) + if coverage[line_num] is None: + coverage[line_num] = 0 elif cov_num == '=====': # This is indicitive of a gcov output parse # error. - coverage.append(0) + if coverage[line_num] is None: + coverage[line_num] = 0 else: - coverage.append(int(cov_num.rstrip('*'))) + coverage[line_num] = int(cov_num.rstrip('*')) return coverage @@ -382,8 +398,11 @@ def collect(args): report['service_name'] = args.service_name report['service_job_id'] = args.service_job_id - if os.getenv('COVERALLS_PARALLEL', False): - report['parallel'] = 'true' + if args.service_build_number: + report['service_number'] = args.service_build_number + + if args.parallel: + report['parallel'] = args.parallel args.exclude_lines_pattern.extend([ r'\bLCOV_EXCL_LINE\b', diff --git a/cpp_coveralls/report.py b/cpp_coveralls/report.py index 4ffb669..e75f36e 100644 --- a/cpp_coveralls/report.py +++ b/cpp_coveralls/report.py @@ -5,11 +5,12 @@ import json import os -URL = os.getenv('COVERALLS_ENDPOINT', 'https://coveralls.io') + "/api/v1/jobs" +ENDPOINT = os.getenv('COVERALLS_ENDPOINT', 'https://coveralls.io') def post_report(coverage, args): """Post coverage report to coveralls.io.""" - response = requests.post(URL, files={'json_file': json.dumps(coverage)}, + api_endpoint = f'{ENDPOINT}/api/v1/jobs' + response = requests.post(api_endpoint, files={'json_file': json.dumps(coverage)}, verify=(not args.skip_ssl_verify)) try: result = response.json() @@ -22,3 +23,15 @@ def post_report(coverage, args): if 'error' in result: return result['error'] return 0 + +def finish_report(args): + """Finish a parallel reporting: https://docs.coveralls.io/parallel-build-webhook""" + api_endpoint = f'{ENDPOINT}/webhook?repo_token={args.repo_token}' + data = {'payload[build_num]': args.service_build_number, 'payload[status]':'done'} + response = requests.post(api_endpoint, data=data, + verify=(not args.skip_ssl_verify)) + + if response.status_code != 200: + return response.status_code, response.reason, data + else: + return 0 \ No newline at end of file