def hide_orphaned_commit_comments(self, pull: PullRequest) -> None:
        # rewriting history of branch removes commits
        # we do not want to show test results for those commits anymore

        # get commits of this pull request
        commit_shas = set([commit.sha for commit in pull.get_commits()])

        # get comments of this pull request
        comments = self.get_pull_request_comments(pull, order_by_updated=False)

        # get all comments that come from this action and are not hidden
        comments = self.get_action_comments(comments)

        # get comment node ids and their commit sha (possibly abbreviated)
        matches = [(comment.get('id'), re.search(r'^[Rr]esults for commit ([0-9a-f]{8,40})\.(?:\s.*)?$', comment.get('body'), re.MULTILINE))
                   for comment in comments]
        comment_commits = [(node_id, match.group(1))
                           for node_id, match in matches
                           if match is not None]

        # get those comment node ids whose commit is not part of this pull request any more
        comment_ids = [(node_id, comment_commit_sha)
                       for (node_id, comment_commit_sha) in comment_commits
                       if not any([sha
                                   for sha in commit_shas
                                   if sha.startswith(comment_commit_sha)])]

        # hide all those comments
        for node_id, comment_commit_sha in comment_ids:
            logger.info(f'hiding unit test result comment for commit {comment_commit_sha}')
            self.hide_comment(node_id)
    def publish_comment(self,
                        title: str,
                        stats: UnitTestRunResults,
                        pull_request: PullRequest,
                        check_run: Optional[CheckRun] = None,
                        cases: Optional[UnitTestCaseResults] = None) -> PullRequest:
        # compare them with earlier stats
        base_check_run = None
        if self._settings.compare_earlier:
            base_commit_sha = self.get_base_commit_sha(pull_request)
            logger.debug(f'comparing against base={base_commit_sha}')
            base_check_run = self.get_check_run(base_commit_sha)
        base_stats = self.get_stats_from_check_run(base_check_run) if base_check_run is not None else None
        stats_with_delta = get_stats_delta(stats, base_stats, 'base') if base_stats is not None else stats
        logger.debug(f'stats with delta: {stats_with_delta}')

        # gather test lists from check run and cases
        before_all_tests, before_skipped_tests = self.get_test_lists_from_check_run(base_check_run)
        all_tests, skipped_tests = get_all_tests_list(cases), get_skipped_tests_list(cases)
        test_changes = SomeTestChanges(before_all_tests, all_tests, before_skipped_tests, skipped_tests)

        details_url = check_run.html_url if check_run else None
        summary = get_long_summary_md(stats_with_delta, details_url, test_changes, self._settings.test_changes_limit)
        body = f'## {title}\n{summary}'

        # reuse existing commend when comment_mode == comment_mode_update
        # if none exists or comment_mode != comment_mode_update, create new comment
        if self._settings.comment_mode != comment_mode_update or not self.reuse_comment(pull_request, body):
            logger.info('creating comment')
            pull_request.create_issue_comment(body)

        return pull_request
    def publish(self, stats: UnitTestRunResults, cases: UnitTestCaseResults,
                conclusion: str):
        logger.info(
            f'publishing {conclusion} results for commit {self._settings.commit}'
        )
        check_run = self.publish_check(stats, cases, conclusion)

        if self._settings.comment_mode != comment_mode_off:
            pull = self.get_pull(self._settings.commit)
            if pull is not None:
                self.publish_comment(self._settings.comment_title, stats, pull,
                                     check_run, cases)
                if self._settings.hide_comment_mode == hide_comments_mode_orphaned:
                    self.hide_orphaned_commit_comments(pull)
                elif self._settings.hide_comment_mode == hide_comments_mode_all_but_latest:
                    self.hide_all_but_latest_comments(pull)
                else:
                    logger.info(
                        'hide_comments disabled, not hiding any comments')
            else:
                logger.info(
                    f'there is no pull request for commit {self._settings.commit}'
                )
        else:
            logger.info(
                'comment_on_pr disabled, not commenting on any pull requests')
    def hide_all_but_latest_comments(self, pull: PullRequest) -> None:
        # we want to reduce the number of shown comments to a minimum

        # get comments of this pull request
        comments = self.get_pull_request_comments(pull, order_by_updated=False)

        # get all comments that come from this action and are not hidden
        comments = self.get_action_comments(comments)

        # take all but the last comment
        comment_ids = [comment.get('id') for comment in comments[:-1]]

        # hide all those comments
        for node_id in comment_ids:
            logger.info(f'hiding unit test result comment {node_id}')
            self.hide_comment(node_id)
    def publish_check(self, stats: UnitTestRunResults,
                      cases: UnitTestCaseResults, compare_earlier: bool,
                      conclusion: str) -> CheckRun:
        # get stats from earlier commits
        before_stats = None
        if compare_earlier:
            before_commit_sha = self._settings.event.get('before')
            logger.debug(f'comparing against before={before_commit_sha}')
            before_stats = self.get_stats_from_commit(before_commit_sha)
        stats_with_delta = get_stats_delta(
            stats, before_stats,
            'earlier') if before_stats is not None else stats
        logger.debug(f'stats with delta: {stats_with_delta}')

        error_annotations = get_error_annotations(stats.errors)
        case_annotations = get_case_annotations(
            cases, self._settings.report_individual_runs)
        file_list_annotations = self.get_test_list_annotations(cases)
        all_annotations = error_annotations + case_annotations + file_list_annotations

        # we can send only 50 annotations at once, so we split them into chunks of 50
        check_run = None
        all_annotations = [
            all_annotations[x:x + 50]
            for x in range(0, len(all_annotations), 50)
        ] or [[]]
        for annotations in all_annotations:
            output = dict(title=get_short_summary(stats),
                          summary=get_long_summary_with_digest_md(
                              stats_with_delta, stats),
                          annotations=[
                              annotation.to_dict()
                              for annotation in annotations
                          ])

            logger.info('creating check')
            check_run = self._repo.create_check_run(
                name=self._settings.check_name,
                head_sha=self._settings.commit,
                status='completed',
                conclusion=conclusion,
                output=output)
            logger.debug(f'created check {check_run}')
        return check_run
    def reuse_comment(self, pull: PullRequest, body: str) -> bool:
        # get comments of this pull request
        comments = self.get_pull_request_comments(pull, order_by_updated=True)

        # get all comments that come from this action and are not hidden
        comments = self.get_action_comments(comments)

        # if there is no such comment, stop here
        if len(comments) == 0:
            return False

        # edit last comment
        comment_id = comments[-1].get("databaseId")
        logger.info(f'editing comment {comment_id}')
        if ':recycle:' not in body:
            body = f'{body}\n:recycle: This comment has been updated with latest results.'

        try:
            pull.get_issue_comment(comment_id).edit(body)
        except Exception as e:
            self._gha.warning(f'Failed to edit existing comment #{comment_id}')
            logger.debug('editing existing comment failed', exc_info=e)

        return True