From 66e8c41f1a122fda27673001bae1de646ad157fe Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Fri, 13 Oct 2023 06:39:51 -0400 Subject: [PATCH 1/3] docs: better labels for the crash actions --- openedx_webhooks/templates/main.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openedx_webhooks/templates/main.html b/openedx_webhooks/templates/main.html index aaa3d5fb..af83a8eb 100644 --- a/openedx_webhooks/templates/main.html +++ b/openedx_webhooks/templates/main.html @@ -31,8 +31,8 @@

GitHub

Other

From 5672258aa81fd5633540257f730801cb0611c6d9 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Fri, 13 Oct 2023 06:40:12 -0400 Subject: [PATCH 2/3] test: a label that will cause a deep celery crash --- openedx_webhooks/tasks/pr_tracking.py | 4 ++++ tests/test_pull_request_opened.py | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/openedx_webhooks/tasks/pr_tracking.py b/openedx_webhooks/tasks/pr_tracking.py index c4c76e60..9fded581 100644 --- a/openedx_webhooks/tasks/pr_tracking.py +++ b/openedx_webhooks/tasks/pr_tracking.py @@ -227,6 +227,10 @@ def desired_support_state(pr: PrDict) -> PrDesiredInfo: label_names = set(lbl["name"] for lbl in pr["labels"]) desired.jira_nicks = {name.partition(":")[-1] for name in label_names if name.startswith("jira:")} + if "crash!123" in label_names: + # Low-tech backdoor way to test error handling and reporting. + raise Exception(f"A crash label was applied by {user}") + desired.jira_title = pr["title"] desired.jira_description = pr["body"] or "" diff --git a/tests/test_pull_request_opened.py b/tests/test_pull_request_opened.py index cccd272a..46798243 100644 --- a/tests/test_pull_request_opened.py +++ b/tests/test_pull_request_opened.py @@ -334,6 +334,13 @@ def test_add_to_multiple_projects(fake_github): } +def test_crash_label(fake_github): + pr = fake_github.make_pull_request("openedx", user="nedbat") + pr.set_labels(["crash!123"]) + with pytest.raises(Exception, match="A crash label was applied by nedbat"): + pull_request_changed(pr.as_json()) + + def test_jira_labelling(fake_github, fake_jira, fake_jira2): # A PR with a "jira:" label makes a Jira issue. pr = fake_github.make_pull_request("openedx", user="nedbat", title="Ned's PR") From 10dd87593be271b1d00977dc65e2bbbb010d042b Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Tue, 17 Oct 2023 17:19:46 -0400 Subject: [PATCH 3/3] fix: isolate actions and raise exceptions as a group --- openedx_webhooks/tasks/pr_tracking.py | 29 +++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/openedx_webhooks/tasks/pr_tracking.py b/openedx_webhooks/tasks/pr_tracking.py index 9fded581..76682327 100644 --- a/openedx_webhooks/tasks/pr_tracking.py +++ b/openedx_webhooks/tasks/pr_tracking.py @@ -4,6 +4,7 @@ from __future__ import annotations +import contextlib import copy import dataclasses import itertools @@ -318,10 +319,23 @@ def __init__( self.bot_data = copy.deepcopy(current.bot_data) self.fix_result: FixResult = FixResult() + self.exceptions: List[Exception] = [] def result(self) -> FixResult: return self.fix_result + @contextlib.contextmanager + def saved_exceptions(self): + """ + A context manager to wrap around isolatable steps. + + An exception raised in the with-block will be added to `self.exceptions`. + """ + try: + yield + except Exception as exc: + self.exceptions.append(exc) + def fix(self) -> None: """ The main routine for making needed changes. @@ -335,19 +349,26 @@ def fix(self) -> None: if self.desired.cla_check != self.current.cla_check: assert self.desired.cla_check is not None - self.actions.set_cla_status(status=self.desired.cla_check) + with self.saved_exceptions(): + self.actions.set_cla_status(status=self.desired.cla_check) if self.desired.is_ospr: - self._fix_ospr() + with self.saved_exceptions(): + self._fix_ospr() if self.desired.is_refused: - self._fix_comments() + with self.saved_exceptions(): + self._fix_comments() # Make needed Jira issues. current_jira_nicks = {ji.nick for ji in self.current.bot_data.jira_issues} for jira_nick in self.desired.jira_nicks: if jira_nick not in current_jira_nicks: - self._make_jira_issue(jira_nick) + with self.saved_exceptions(): + self._make_jira_issue(jira_nick) + + if self.exceptions: + raise ExceptionGroup("Some actions failed", self.exceptions) def _fix_comments(self) -> None: fix_comment = True