def add_shell_result(report: Report, name: str, exit_code: int) -> CheckResult: logging.info(f'"{name}" exited with {exit_code}') z = CheckResult.SUCCESS if exit_code != 0: z = CheckResult.FAILURE report.add_step(name, z, '') return z
def ninja_check_all_report(report: Report) -> CheckResult: # TODO: merge running ninja check all and analysing results in one step? print('Full will be available in Artifacts "ninja-check-all.log"') r = subprocess.run( f'ninja check-all | tee {artifacts_dir}/ninja-check-all.log | ' f'grep -vE "^\\[.*] (Building|Linking)" | ' f'grep -vE "^(PASS|XFAIL|UNSUPPORTED):"', shell=True, cwd=build_dir) z = add_shell_result(report, 'ninja check all', r.returncode) # TODO: check if test-results are present. report.add_artifact(build_dir, 'test-results.xml', 'test results') test_results_report.run(os.path.join(build_dir, 'test-results.xml'), report) return z
def run(base_commit, ignore_config, step: Optional[Step], report: Optional[Report]): """Apply clang-format and return if no issues were found.""" if report is None: report = Report() # For debugging. if step is None: step = Step() # For debugging. r, patch = get_diff(base_commit) if not r: step.success = False return add_artifact = False patches = unidiff.PatchSet(patch) ignore_lines = [] if ignore_config is not None and os.path.exists(ignore_config): ignore_lines = open(ignore_config, 'r').readlines() ignore = pathspec.PathSpec.from_lines( pathspec.patterns.GitWildMatchPattern, ignore_lines) patched_file: unidiff.PatchedFile success = True for patched_file in patches: add_artifact = True if ignore.match_file(patched_file.source_file) or ignore.match_file( patched_file.target_file): logging.info(f'patch of {patched_file.patch_info} is ignored') continue hunk: unidiff.Hunk for hunk in patched_file: lines = [str(x) for x in hunk] success = False m = 10 # max number of lines to report. description = 'please reformat the code\n```\n' n = len(lines) cut = n > m + 1 if cut: lines = lines[:m] description += ''.join(lines) + '\n```' if cut: description += f'\n{n - m} diff lines are omitted. See full path.' report.add_lint({ 'name': 'clang-format', 'severity': 'autofix', 'code': 'clang-format', 'path': patched_file.source_file, 'line': hunk.source_start, 'char': 1, 'description': description, }) if add_artifact: patch_file = 'clang-format.patch' with open(patch_file, 'w') as f: f.write(patch) report.add_artifact(os.getcwd(), patch_file, 'clang-format') if not success: step.success = False step.messages.append( 'Please format your changes with clang-format by running `git-clang-format HEAD^` or applying patch.' ) logging.debug(f'report: {report}') logging.debug(f'step: {step}')
def run(working_dir: str, test_results: str, step: Optional[Step], report: Optional[Report]): if report is None: report = Report() # For debugging. if step is None: step = Step() path = os.path.join(working_dir, test_results) if not os.path.exists(path): logging.warning(f'{path} is not found') step.success = False step.messages.append(f'test report "{path}" is not found') return report.add_artifact(working_dir, test_results, 'test results') try: success = True root_node = etree.parse(path) for test_case in root_node.xpath('//testcase'): test_result = 'pass' if test_case.find('failure') is not None: test_result = 'fail' if test_case.find('skipped') is not None: test_result = 'skip' report.test_stats[test_result] += 1 if test_result == 'fail': success = False failure = test_case.find('failure') test_result = { 'name': test_case.attrib['name'], 'namespace': test_case.attrib['classname'], 'result': test_result, 'duration': float(test_case.attrib['time']), 'details': failure.text } report.unit.append(test_result) msg = f'{report.test_stats["pass"]} tests passed, {report.test_stats["fail"]} failed and ' \ f'{report.test_stats["skip"]} were skipped.\n' if not success: step.success = False for test_case in report.unit: if test_case['result'] == 'fail': msg += f'{test_case["namespace"]}/{test_case["name"]}\n' except Exception as e: logging.error(e) step.messages.append('Parsing of test results failed') step.success = False logging.debug(f'report: {report}') logging.debug(f'step: {step}')
def run(test_results, report: Optional[Report]): """Apply clang-format and return if no issues were found.""" if report is None: report = Report() # For debugging. if not os.path.exists(test_results): logging.warning(f'{test_results} not found') report.add_step('clang-format', CheckResult.UNKNOWN, 'test report is not found') return success = True root_node = etree.parse(test_results) for test_case in root_node.xpath('//testcase'): test_result = 'pass' if test_case.find('failure') is not None: test_result = 'fail' if test_case.find('skipped') is not None: test_result = 'skip' report.test_stats[test_result] += 1 if test_result == 'fail': success = False failure = test_case.find('failure') test_result = { 'name': test_case.attrib['name'], 'namespace': test_case.attrib['classname'], 'result': test_result, 'duration': float(test_case.attrib['time']), 'details': failure.text } report.unit.append(test_result) msg = f'{report.test_stats["pass"]} tests passed, {report.test_stats["fail"]} failed and' \ f'{report.test_stats["skip"]} were skipped.\n' if success: report.add_step('test results', CheckResult.SUCCESS, msg) else: for test_case in report.unit: if test_case['result'] == 'fail': msg += f'{test_case["namespace"]}/{test_case["name"]}\n' report.add_step('unit tests', CheckResult.FAILURE, msg)
def run(base_commit, ignore_config, step: Optional[Step], report: Optional[Report]): """Apply clang-format and return if no issues were found.""" if report is None: report = Report() # For debugging. if step is None: step = Step() # For debugging. r = subprocess.run(f'git diff -U0 --no-prefix {base_commit}', shell=True, capture_output=True) logging.debug(f'git diff {r}') diff = r.stdout.decode() if ignore_config is not None and os.path.exists(ignore_config): ignore = pathspec.PathSpec.from_lines( pathspec.patterns.GitWildMatchPattern, open(ignore_config, 'r').readlines()) diff = ignore_diff.remove_ignored(diff.splitlines(keepends=True), open(ignore_config, 'r')) logging.debug(f'filtered diff: {diff}') else: ignore = pathspec.PathSpec.from_lines( pathspec.patterns.GitWildMatchPattern, []) p = subprocess.Popen(['clang-tidy-diff', '-p0', '-quiet'], stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE) step.reproduce_commands.append( f'git diff -U0 --no-prefix {base_commit} | clang-tidy-diff -p0') a = ''.join(diff) logging.info(f'clang-tidy input: {a}') out = p.communicate(input=a.encode())[0].decode() logging.debug(f'clang-tidy-diff {p}: {out}') # Typical finding looks like: # [cwd/]clang/include/clang/AST/DeclCXX.h:3058:20: error: ... [clang-diagnostic-error] pattern = '^([^:]*):(\\d+):(\\d+): (.*): (.*)' add_artifact = False logging.debug("cwd", os.getcwd()) errors_count = 0 warn_count = 0 inline_comments = 0 for line in out.splitlines(keepends=False): line = line.strip() line = line.replace(os.getcwd() + os.sep, '') logging.debug(line) if len(line) == 0 or line == 'No relevant changes found.': continue add_artifact = True match = re.search(pattern, line) if match: file_name = match.group(1) line_pos = match.group(2) char_pos = match.group(3) severity = match.group(4) text = match.group(5) text += '\n[[{} | not useful]] '.format( 'https://github.com/google/llvm-premerge-checks/blob/master/docs/clang_tidy.md#warning-is-not-useful' ) if severity in ['warning', 'error']: if severity == 'warning': warn_count += 1 if severity == 'error': errors_count += 1 if ignore.match_file(file_name): print( '{} is ignored by pattern and no comment will be added' .format(file_name)) else: inline_comments += 1 report.add_lint({ 'name': 'clang-tidy', 'severity': 'warning', 'code': 'clang-tidy', 'path': file_name, 'line': int(line_pos), 'char': int(char_pos), 'description': '{}: {}'.format(severity, text), }) else: logging.debug('does not match pattern') if add_artifact: p = 'clang-tidy.txt' with open(p, 'w') as f: f.write(out) report.add_artifact(os.getcwd(), p, 'clang-tidy') if errors_count + warn_count != 0: step.success = False url = format_url( "https://github.com/google/llvm-premerge-checks/blob/master/docs/clang_tidy.md" "#review-comments.", "why?") step.messages.append( f'clang-tidy found {errors_count} errors and {warn_count} warnings. {inline_comments} of them are added ' f'as review comments {url}') logging.debug(f'report: {report}') logging.debug(f'step: {step}')
name = url return f"\033]1339;url='{url}';content='{name}'\a\n" if __name__ == '__main__': build_dir = '' logging.basicConfig(level=logging.WARNING, format='%(levelname)-7s %(message)s') scripts_dir = pathlib.Path(__file__).parent.absolute() phab = PhabTalk(os.getenv('CONDUIT_TOKEN'), 'https://reviews.llvm.org/api/', False) maybe_add_url_artifact(phab, os.getenv('BUILDKITE_BUILD_URL'), 'Buildkite build') artifacts_dir = os.path.join(os.getcwd(), 'artifacts') os.makedirs(artifacts_dir, exist_ok=True) report = Report() timings = {} cmake_result = run_step('cmake', report, cmake_report) if cmake_result == CheckResult.SUCCESS: compile_result = run_step('ninja all', report, ninja_all_report) if compile_result == CheckResult.SUCCESS: run_step('ninja check all', report, ninja_check_all_report) run_step( 'clang-tidy', report, lambda x: clang_tidy_report.run( 'HEAD~1', os.path.join(scripts_dir, 'clang-tidy.ignore'), x)) run_step( 'clang-format', report, lambda x: clang_format_report.run( 'HEAD~1', os.path.join(scripts_dir, 'clang-format.ignore'), x)) print('+++ summary') print( f'Branch {os.getenv("BUILDKITE_BRANCH")} at {os.getenv("BUILDKITE_REPO")}'
type=str, default='detect', help= "Projects to select, either a list or projects like 'clang;libc', or " "'detect' to automatically infer proejcts from the diff, or " "'default' to add all enabled projects") args = parser.parse_args() logging.basicConfig(level=args.log_level, format='%(levelname)-7s %(message)s') build_dir = '' step_key = os.getenv("BUILDKITE_STEP_KEY") scripts_dir = pathlib.Path(__file__).parent.absolute() artifacts_dir = os.path.join(os.getcwd(), 'artifacts') os.makedirs(artifacts_dir, exist_ok=True) report_path = f'{step_key}_result.json' report = Report() report.os = f'{os.getenv("BUILDKITE_AGENT_META_DATA_OS")}' report.name = step_key report.success = False # Create report with failure in case something below fails. with open(report_path, 'w') as f: json.dump(report.__dict__, f, default=as_dict) report.success = True cmake = run_step('cmake', report, lambda s, r: cmake_report(args.projects, s, r)) if cmake.success: ninja_all = run_step( 'ninja all', report, partial(ninja_all_report, filter_output=args.filter_output)) if ninja_all.success: run_step(