From 23cb096cd76097760a90699fcad107dcde666712 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 1 Oct 2026 08:14:17 +0000 Subject: [PATCH 1/3] fix: resolve issue #290 --- bert_e/exceptions.py | 7 ++ bert_e/git_host/base.py | 7 ++ bert_e/git_host/github/__init__.py | 5 ++ bert_e/git_host/mock.py | 3 + bert_e/tests/unit/test_comment_update.py | 89 ++++++++++++++++++++++++ bert_e/workflow/pr_utils.py | 34 ++++++++- 6 files changed, 143 insertions(+), 2 deletions(-) create mode 100644 bert_e/tests/unit/test_comment_update.py diff --git a/bert_e/exceptions.py b/bert_e/exceptions.py index f9654dad..afb0d1d5 100644 --- a/bert_e/exceptions.py +++ b/bert_e/exceptions.py @@ -31,6 +31,9 @@ class TemplateException(BertE_Exception): template = None # whether to re-publish if the message is already in the history dont_repeat_if_in_history = -1 + # whether the message is a status that supersedes the previous one: it + # then updates the existing status comment instead of posting a new one + updatable = False def __init__(self, **kwargs): self.kwargs = kwargs @@ -59,6 +62,7 @@ class SilentException(BertE_Exception): # template for informative exceptions class InformationException(TemplateException): dont_repeat_if_in_history = NEVER_REPEAT + updatable = True # template exceptions @@ -131,12 +135,14 @@ class IncorrectFixVersion(TemplateException): code = 112 template = 'incorrect_fix_version.md' status = "failure" + updatable = True class BranchHistoryMismatch(TemplateException): code = 113 template = 'history_mismatch.md' status = "failure" + updatable = True class Conflict(TemplateException): @@ -226,6 +232,7 @@ class QueueOutOfOrder(TemplateException): class ResetComplete(TemplateException): code = 128 template = "reset_complete.md" + updatable = True class LossyResetWarning(TemplateException): diff --git a/bert_e/git_host/base.py b/bert_e/git_host/base.py index a38ab926..424dbb2d 100644 --- a/bert_e/git_host/base.py +++ b/bert_e/git_host/base.py @@ -277,6 +277,13 @@ def text(self) -> str: def id(self) -> int: """The comment's ID""" + def edit(self, msg: str) -> None: + """Replace the comment's contents with `msg`. + + Git hosts that cannot edit comments raise NotImplementedError. + """ + raise NotImplementedError('Comment edition is not supported.') + class AbstractPullRequest(metaclass=ABCMeta): @abstractmethod diff --git a/bert_e/git_host/github/__init__.py b/bert_e/git_host/github/__init__.py index 59515b1c..aa3c4dc7 100644 --- a/bert_e/git_host/github/__init__.py +++ b/bert_e/git_host/github/__init__.py @@ -1018,6 +1018,11 @@ def id(self) -> int: def delete(self) -> None: self.client.delete(self.data['url']) + def edit(self, msg: str) -> None: + self.data = self.update( + self.client, {'body': msg}, url=self.data['url'] + ).data + class CheckRun(base.AbstractGitHostObject): GET_URL = '/repos/{owner}/{repo}/check-runs/{id}' diff --git a/bert_e/git_host/mock.py b/bert_e/git_host/mock.py index 02138b94..d635cb71 100644 --- a/bert_e/git_host/mock.py +++ b/bert_e/git_host/mock.py @@ -409,6 +409,9 @@ class CommentController(Controller, base.AbstractComment): def delete(self): self.controlled.delete() + def edit(self, msg): + self['content'] = {"raw": msg, "markup": "markdown", "html": msg} + @property def author(self): return self['user']['username'].lower() diff --git a/bert_e/tests/unit/test_comment_update.py b/bert_e/tests/unit/test_comment_update.py new file mode 100644 index 00000000..e1225ca6 --- /dev/null +++ b/bert_e/tests/unit/test_comment_update.py @@ -0,0 +1,89 @@ +"""Status comments are edited in place instead of being re-posted.""" +from types import SimpleNamespace + +import pytest + +from bert_e import exceptions +from bert_e.workflow import pr_utils + + +class FakeComment: + def __init__(self, author, text, editable=True): + self.author = author + self.text = text + self.editable = editable + + def edit(self, msg): + if not self.editable: + raise NotImplementedError + self.text = msg + + +class FakePR: + def __init__(self, editable=True): + self.comments = [] + self.editable = editable + + def add_comment(self, msg): + self.comments.append(FakeComment('bert-e', msg, self.editable)) + + +@pytest.fixture +def settings(): + return SimpleNamespace(no_comment=False, interactive=False, + robot='bert-e') + + +def _send(settings, pr, msg, exc_cls): + pr_utils._send_comment(settings, pr, msg, + exc_cls.dont_repeat_if_in_history, + updatable=exc_cls.updatable) + + +def test_updatable_classes(): + assert exceptions.ResetComplete.updatable + assert exceptions.BranchHistoryMismatch.updatable + assert exceptions.IncorrectFixVersion.updatable + assert exceptions.IntegrationDataCreated.updatable + assert not exceptions.HelpMessage.updatable + assert not exceptions.StatusReport.updatable + + +def test_status_comment_is_updated(settings): + pr = FakePR() + _send(settings, pr, 'reset', exceptions.ResetComplete) + _send(settings, pr, 'history mismatch', exceptions.BranchHistoryMismatch) + assert len(pr.comments) == 1 + assert pr.comments[0].text.startswith('history mismatch') + assert pr.comments[0].text.endswith(pr_utils.STATUS_MARKER) + + +def test_identical_status_not_reposted(settings): + pr = FakePR() + _send(settings, pr, 'reset', exceptions.ResetComplete) + with pytest.raises(exceptions.CommentAlreadyExists): + _send(settings, pr, 'reset', exceptions.ResetComplete) + assert len(pr.comments) == 1 + + +def test_non_updatable_message_is_posted(settings): + pr = FakePR() + _send(settings, pr, 'reset', exceptions.ResetComplete) + _send(settings, pr, 'help', exceptions.HelpMessage) + assert len(pr.comments) == 2 + assert pr_utils.STATUS_MARKER not in pr.comments[1].text + + +def test_fallback_when_edit_unsupported(settings): + pr = FakePR(editable=False) + _send(settings, pr, 'reset', exceptions.ResetComplete) + _send(settings, pr, 'other', exceptions.BranchHistoryMismatch) + assert len(pr.comments) == 2 + + +def test_other_authors_comments_untouched(settings): + pr = FakePR() + pr.comments.append(FakeComment('someone', 'hi ' + pr_utils.STATUS_MARKER)) + _send(settings, pr, 'reset', exceptions.ResetComplete) + assert len(pr.comments) == 2 + assert pr.comments[0].text.startswith('hi') diff --git a/bert_e/workflow/pr_utils.py b/bert_e/workflow/pr_utils.py index a09f3991..4ff89e9d 100644 --- a/bert_e/workflow/pr_utils.py +++ b/bert_e/workflow/pr_utils.py @@ -21,6 +21,9 @@ LOG = logging.getLogger(__name__) +# Hidden marker identifying the comment holding the bot's latest status. +STATUS_MARKER = '' + def find_comment(pull_request: AbstractPullRequest, username=None, startswith=None, max_history=None) -> AbstractComment: @@ -50,8 +53,17 @@ def find_comment(pull_request: AbstractPullRequest, username=None, return comment +def find_status_comment(pull_request: AbstractPullRequest, + username) -> AbstractComment: + """Return the latest status comment posted by the bot, if any.""" + for comment in reversed(pull_request.comments): + if comment.author == username and comment.text.rstrip().endswith( + STATUS_MARKER): + return comment + + def _send_comment(settings, pull_request: AbstractPullRequest, msg: str, - dont_repeat_if_in_history=10) -> None: + dont_repeat_if_in_history=10, updatable=False) -> None: """Comment a pull request. Before posting: @@ -59,6 +71,8 @@ def _send_comment(settings, pull_request: AbstractPullRequest, msg: str, request comments history. Optionally (if settings.interactive is set) ask confirmation to the user. + If `updatable` is set, edit the bot's previous status comment in place + (when there is one) instead of posting a new comment. Raises: CommentAlreadyExists: if the comment was already posted. @@ -80,6 +94,21 @@ def _send_comment(settings, pull_request: AbstractPullRequest, msg: str, if not confirm('Do you want to send this comment?'): return + if updatable: + status_comment = find_status_comment(pull_request, settings.robot) + msg = f'{msg}\n\n{STATUS_MARKER}' + if status_comment is not None: + if status_comment.text.strip() == msg.strip(): + raise exceptions.CommentAlreadyExists( + "The status comment is already up to date." + ) + try: + LOG.debug('UPDATING STATUS COMMENT %s', msg) + status_comment.edit(msg) + return + except NotImplementedError: + LOG.debug('Comments cannot be edited, posting a new one.') + LOG.debug('SENDING MESSAGE %s', msg) pull_request.add_comment(msg) @@ -104,6 +133,7 @@ def notify_user(settings, pull_request: AbstractPullRequest, try: _send_bot_status(settings, pull_request, comment) _send_comment(settings, pull_request, str(comment), - comment.dont_repeat_if_in_history) + comment.dont_repeat_if_in_history, + updatable=comment.updatable) except exceptions.CommentAlreadyExists: LOG.info("Comment '%s' already posted", comment.__class__.__name__) From 11bbbab9720314bd43b9ee9e7bc720783fb02c11 Mon Sep 17 00:00:00 2001 From: bot Date: Thu, 1 Oct 2026 09:41:41 +0000 Subject: [PATCH 2/3] fix: resolve issue #290 --- bert_e/exceptions.py | 12 ++- bert_e/git_host/base.py | 6 +- bert_e/git_host/github/__init__.py | 10 +-- bert_e/git_host/mock.py | 5 +- bert_e/tests/unit/test_comment_update.py | 84 ++++++++++---------- bert_e/tests/unit/test_github_comment_api.py | 28 +++++++ bert_e/workflow/pr_utils.py | 42 +++++++--- 7 files changed, 120 insertions(+), 67 deletions(-) create mode 100644 bert_e/tests/unit/test_github_comment_api.py diff --git a/bert_e/exceptions.py b/bert_e/exceptions.py index afb0d1d5..42c2e6ac 100644 --- a/bert_e/exceptions.py +++ b/bert_e/exceptions.py @@ -31,9 +31,9 @@ class TemplateException(BertE_Exception): template = None # whether to re-publish if the message is already in the history dont_repeat_if_in_history = -1 - # whether the message is a status that supersedes the previous one: it - # then updates the existing status comment instead of posting a new one - updatable = False + # whether to edit the previous status comment of the bot instead of + # posting a new one + update_status_comment = False def __init__(self, **kwargs): self.kwargs = kwargs @@ -134,6 +134,7 @@ class MismatchPrefixIssueType(TemplateException): class IncorrectFixVersion(TemplateException): code = 112 template = 'incorrect_fix_version.md' + update_status_comment = True status = "failure" updatable = True @@ -141,6 +142,7 @@ class IncorrectFixVersion(TemplateException): class BranchHistoryMismatch(TemplateException): code = 113 template = 'history_mismatch.md' + update_status_comment = True status = "failure" updatable = True @@ -148,6 +150,7 @@ class BranchHistoryMismatch(TemplateException): class Conflict(TemplateException): code = 114 template = 'conflict.md' + update_status_comment = True status = "failure" @@ -172,6 +175,7 @@ class AfterPullRequest(TemplateException): class IntegrationDataCreated(InformationException): code = 121 template = 'integration_data_created.md' + update_status_comment = True class UnknownCommand(TemplateException): @@ -232,7 +236,7 @@ class QueueOutOfOrder(TemplateException): class ResetComplete(TemplateException): code = 128 template = "reset_complete.md" - updatable = True + update_status_comment = True class LossyResetWarning(TemplateException): diff --git a/bert_e/git_host/base.py b/bert_e/git_host/base.py index 424dbb2d..c043c755 100644 --- a/bert_e/git_host/base.py +++ b/bert_e/git_host/base.py @@ -277,12 +277,12 @@ def text(self) -> str: def id(self) -> int: """The comment's ID""" - def edit(self, msg: str) -> None: - """Replace the comment's contents with `msg`. + def edit(self, text: str) -> None: + """Replace the comment's contents with the given text. Git hosts that cannot edit comments raise NotImplementedError. """ - raise NotImplementedError('Comment edition is not supported.') + raise NotImplementedError('Comment edition is not supported') class AbstractPullRequest(metaclass=ABCMeta): diff --git a/bert_e/git_host/github/__init__.py b/bert_e/git_host/github/__init__.py index aa3c4dc7..f566e86e 100644 --- a/bert_e/git_host/github/__init__.py +++ b/bert_e/git_host/github/__init__.py @@ -269,7 +269,7 @@ def patch(self, url, data, **kwargs): """ url = self._patch_url(url) - response = self.session.post(url, data=data, **kwargs) + response = self.session.patch(url, data=data, **kwargs) response.raise_for_status() return json.loads(response.text) @@ -1018,10 +1018,10 @@ def id(self) -> int: def delete(self) -> None: self.client.delete(self.data['url']) - def edit(self, msg: str) -> None: - self.data = self.update( - self.client, {'body': msg}, url=self.data['url'] - ).data + def edit(self, text: str) -> None: + updated = type(self).update( + self.client, {'body': text}, url=self.data['url']) + self.data = updated.data class CheckRun(base.AbstractGitHostObject): diff --git a/bert_e/git_host/mock.py b/bert_e/git_host/mock.py index d635cb71..4e8a26be 100644 --- a/bert_e/git_host/mock.py +++ b/bert_e/git_host/mock.py @@ -409,8 +409,9 @@ class CommentController(Controller, base.AbstractComment): def delete(self): self.controlled.delete() - def edit(self, msg): - self['content'] = {"raw": msg, "markup": "markdown", "html": msg} + def edit(self, text): + self.controlled.content = {"raw": text, "markup": "markdown", + "html": text} @property def author(self): diff --git a/bert_e/tests/unit/test_comment_update.py b/bert_e/tests/unit/test_comment_update.py index e1225ca6..00438034 100644 --- a/bert_e/tests/unit/test_comment_update.py +++ b/bert_e/tests/unit/test_comment_update.py @@ -1,4 +1,3 @@ -"""Status comments are edited in place instead of being re-posted.""" from types import SimpleNamespace import pytest @@ -8,24 +7,24 @@ class FakeComment: - def __init__(self, author, text, editable=True): + def __init__(self, author, text, can_update=True): self.author = author self.text = text - self.editable = editable + self.can_update = can_update - def edit(self, msg): - if not self.editable: + def edit(self, text): + if not self.can_update: raise NotImplementedError - self.text = msg + self.text = text class FakePR: - def __init__(self, editable=True): + def __init__(self, can_update=True): self.comments = [] - self.editable = editable + self.can_update = can_update def add_comment(self, msg): - self.comments.append(FakeComment('bert-e', msg, self.editable)) + self.comments.append(FakeComment('bert-e', msg, self.can_update)) @pytest.fixture @@ -34,56 +33,53 @@ def settings(): robot='bert-e') -def _send(settings, pr, msg, exc_cls): - pr_utils._send_comment(settings, pr, msg, - exc_cls.dont_repeat_if_in_history, - updatable=exc_cls.updatable) +def send(settings, pr, msg, update=True): + pr_utils._send_comment(settings, pr, msg, 0, update) -def test_updatable_classes(): - assert exceptions.ResetComplete.updatable - assert exceptions.BranchHistoryMismatch.updatable - assert exceptions.IncorrectFixVersion.updatable - assert exceptions.IntegrationDataCreated.updatable - assert not exceptions.HelpMessage.updatable - assert not exceptions.StatusReport.updatable +def test_status_comment_is_updated_in_place(settings): + pr = FakePR() + send(settings, pr, 'history conflict') + send(settings, pr, 'reset complete') + assert len(pr.comments) == 1 + assert pr.comments[0].text.endswith('reset complete') + assert pr.comments[0].text.startswith(pr_utils.STATUS_MARKER) -def test_status_comment_is_updated(settings): +def test_status_comment_updated_even_after_other_comments(settings): pr = FakePR() - _send(settings, pr, 'reset', exceptions.ResetComplete) - _send(settings, pr, 'history mismatch', exceptions.BranchHistoryMismatch) - assert len(pr.comments) == 1 - assert pr.comments[0].text.startswith('history mismatch') - assert pr.comments[0].text.endswith(pr_utils.STATUS_MARKER) + send(settings, pr, 'status 1') + pr.comments.append(FakeComment('bert-e', 'approved')) + send(settings, pr, 'status 2') + assert len(pr.comments) == 2 + assert pr.comments[0].text.endswith('status 2') -def test_identical_status_not_reposted(settings): +def test_identical_status_raises(settings): pr = FakePR() - _send(settings, pr, 'reset', exceptions.ResetComplete) + send(settings, pr, 'same') with pytest.raises(exceptions.CommentAlreadyExists): - _send(settings, pr, 'reset', exceptions.ResetComplete) - assert len(pr.comments) == 1 + send(settings, pr, 'same') -def test_non_updatable_message_is_posted(settings): +def test_other_authors_comments_are_not_edited(settings): pr = FakePR() - _send(settings, pr, 'reset', exceptions.ResetComplete) - _send(settings, pr, 'help', exceptions.HelpMessage) + pr.comments.append( + FakeComment('someone', pr_utils.STATUS_MARKER + '\nfoo')) + send(settings, pr, 'status') assert len(pr.comments) == 2 - assert pr_utils.STATUS_MARKER not in pr.comments[1].text + assert pr.comments[0].text.endswith('foo') -def test_fallback_when_edit_unsupported(settings): - pr = FakePR(editable=False) - _send(settings, pr, 'reset', exceptions.ResetComplete) - _send(settings, pr, 'other', exceptions.BranchHistoryMismatch) - assert len(pr.comments) == 2 +def test_regular_comments_are_not_updated(settings): + pr = FakePR() + send(settings, pr, 'a', update=False) + send(settings, pr, 'b', update=False) + assert [c.text for c in pr.comments] == ['a', 'b'] -def test_other_authors_comments_untouched(settings): - pr = FakePR() - pr.comments.append(FakeComment('someone', 'hi ' + pr_utils.STATUS_MARKER)) - _send(settings, pr, 'reset', exceptions.ResetComplete) +def test_fallback_when_host_cannot_edit(settings): + pr = FakePR(can_update=False) + send(settings, pr, 'a') + send(settings, pr, 'b') assert len(pr.comments) == 2 - assert pr.comments[0].text.startswith('hi') diff --git a/bert_e/tests/unit/test_github_comment_api.py b/bert_e/tests/unit/test_github_comment_api.py new file mode 100644 index 00000000..e34c2a91 --- /dev/null +++ b/bert_e/tests/unit/test_github_comment_api.py @@ -0,0 +1,28 @@ +from unittest import mock + +from bert_e.git_host.github import Client, Comment + + +def test_client_patch_uses_http_patch(): + client = Client.__new__(Client) + client.session = mock.Mock() + client.session.patch.return_value.text = '{}' + client._patch_url = lambda url: url + client.patch('https://api.github.com/c/1', data='{}') + client.session.patch.assert_called_once() + client.session.post.assert_not_called() + + +def test_comment_edit_sends_patch_and_refreshes_data(): + client = mock.Mock() + client.patch.return_value = { + 'id': 1, 'url': 'https://api.github.com/c/1', 'body': 'new', + 'user': {'login': 'bert-e', 'id': 1}, + 'created_at': '2020-01-01T00:00:00Z'} + comment = Comment(client, _validate=False, id=1, + url='https://api.github.com/c/1', body='old', + user={'login': 'bert-e', 'id': 1}, + created_at='2020-01-01T00:00:00Z') + comment.edit('new') + assert client.patch.call_args[0][0] == 'https://api.github.com/c/1' + assert comment.text == 'new' diff --git a/bert_e/workflow/pr_utils.py b/bert_e/workflow/pr_utils.py index 4ff89e9d..e000419f 100644 --- a/bert_e/workflow/pr_utils.py +++ b/bert_e/workflow/pr_utils.py @@ -53,17 +53,38 @@ def find_comment(pull_request: AbstractPullRequest, username=None, return comment -def find_status_comment(pull_request: AbstractPullRequest, - username) -> AbstractComment: - """Return the latest status comment posted by the bot, if any.""" - for comment in reversed(pull_request.comments): - if comment.author == username and comment.text.rstrip().endswith( - STATUS_MARKER): - return comment +STATUS_MARKER = '' + + +def _update_status_comment(settings, pull_request: AbstractPullRequest, + msg: str) -> bool: + """Edit the bot's status comment in place, or create it. + + Returns True if the message was handled (edited or posted), False if + the git host cannot edit comments and a regular comment should be sent. + + Raises: + CommentAlreadyExists: if the status comment already has this text. + + """ + body = f'{STATUS_MARKER}\n{msg}' + previous = find_comment(pull_request, settings.robot, STATUS_MARKER) + if previous is None: + pull_request.add_comment(body) + return True + if previous.text == body: + raise exceptions.CommentAlreadyExists( + "The status comment is already up to date.") + try: + previous.edit(body) + except NotImplementedError: + return False + return True def _send_comment(settings, pull_request: AbstractPullRequest, msg: str, - dont_repeat_if_in_history=10, updatable=False) -> None: + dont_repeat_if_in_history=10, + update_status_comment=False) -> None: """Comment a pull request. Before posting: @@ -110,6 +131,9 @@ def _send_comment(settings, pull_request: AbstractPullRequest, msg: str, LOG.debug('Comments cannot be edited, posting a new one.') LOG.debug('SENDING MESSAGE %s', msg) + if update_status_comment and _update_status_comment( + settings, pull_request, msg): + return pull_request.add_comment(msg) @@ -134,6 +158,6 @@ def notify_user(settings, pull_request: AbstractPullRequest, _send_bot_status(settings, pull_request, comment) _send_comment(settings, pull_request, str(comment), comment.dont_repeat_if_in_history, - updatable=comment.updatable) + comment.update_status_comment) except exceptions.CommentAlreadyExists: LOG.info("Comment '%s' already posted", comment.__class__.__name__) From 24028ce530172c5ae1dfc4987927c23407ba4876 Mon Sep 17 00:00:00 2001 From: bot Date: Thu, 1 Oct 2026 09:42:02 +0000 Subject: [PATCH 3/3] fix: resolve issue #290 (supersede previous attempt) --- bert_e/exceptions.py | 3 --- bert_e/workflow/pr_utils.py | 20 -------------------- 2 files changed, 23 deletions(-) diff --git a/bert_e/exceptions.py b/bert_e/exceptions.py index 42c2e6ac..af8388a4 100644 --- a/bert_e/exceptions.py +++ b/bert_e/exceptions.py @@ -62,7 +62,6 @@ class SilentException(BertE_Exception): # template for informative exceptions class InformationException(TemplateException): dont_repeat_if_in_history = NEVER_REPEAT - updatable = True # template exceptions @@ -136,7 +135,6 @@ class IncorrectFixVersion(TemplateException): template = 'incorrect_fix_version.md' update_status_comment = True status = "failure" - updatable = True class BranchHistoryMismatch(TemplateException): @@ -144,7 +142,6 @@ class BranchHistoryMismatch(TemplateException): template = 'history_mismatch.md' update_status_comment = True status = "failure" - updatable = True class Conflict(TemplateException): diff --git a/bert_e/workflow/pr_utils.py b/bert_e/workflow/pr_utils.py index e000419f..814ea68f 100644 --- a/bert_e/workflow/pr_utils.py +++ b/bert_e/workflow/pr_utils.py @@ -21,9 +21,6 @@ LOG = logging.getLogger(__name__) -# Hidden marker identifying the comment holding the bot's latest status. -STATUS_MARKER = '' - def find_comment(pull_request: AbstractPullRequest, username=None, startswith=None, max_history=None) -> AbstractComment: @@ -92,8 +89,6 @@ def _send_comment(settings, pull_request: AbstractPullRequest, msg: str, request comments history. Optionally (if settings.interactive is set) ask confirmation to the user. - If `updatable` is set, edit the bot's previous status comment in place - (when there is one) instead of posting a new comment. Raises: CommentAlreadyExists: if the comment was already posted. @@ -115,21 +110,6 @@ def _send_comment(settings, pull_request: AbstractPullRequest, msg: str, if not confirm('Do you want to send this comment?'): return - if updatable: - status_comment = find_status_comment(pull_request, settings.robot) - msg = f'{msg}\n\n{STATUS_MARKER}' - if status_comment is not None: - if status_comment.text.strip() == msg.strip(): - raise exceptions.CommentAlreadyExists( - "The status comment is already up to date." - ) - try: - LOG.debug('UPDATING STATUS COMMENT %s', msg) - status_comment.edit(msg) - return - except NotImplementedError: - LOG.debug('Comments cannot be edited, posting a new one.') - LOG.debug('SENDING MESSAGE %s', msg) if update_status_comment and _update_status_comment( settings, pull_request, msg):