Repository: python/bedevere
Pull request #557 (2023-05-06 18:31:11)

gh-556: Fix over-reaching PR-edited event handler
gh-555 introduced the PR edited handler to handle draft PRs. The implementation was over-reaching, and triggered on any edited events (including renaming PR). This PR fixes that by removing the PR-edited handler and replacing it with two more specific handlers that more narrowly match the original intent, namely, respond to changes in the draft state of the PR (publish / unpublish).

Fixes gh-556

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/python/bedevere/pull/557?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=python) Report
> Merging [#557](https://app.codecov.io/gh/python/bedevere/pull/557?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=python) (45129ce) into [main](https://app.codecov.io/gh/python/bedevere/commit/0f350cee365b615a1c4fefd0e2cf77e6215e4164?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=python) (0f350ce) will **not change** coverage.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://app.codecov.io/gh/python/bedevere/pull/557/graphs/tree.svg?width=650&height=150&src=pr&token=S7kyeapERi&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=python)](https://app.codecov.io/gh/python/bedevere/pull/557?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=python)

```diff
@@            Coverage Diff            @@
##              main      #557   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           18        18           
  Lines         2069      2082   +13     
  Branches       234       233    -1     
=========================================
+ Hits          2069      2082   +13     
```

| Flag | Coverage Δ | |
|---|---|---|
| Python_3.10.11 | `100.00% <100.00%> (ø)` | |
| Python_3.11.3 | `100.00% <100.00%> (ø)` | |
| Python_3.8.16 | `100.00% <100.00%> (ø)` | |
| Python_3.9.16 | `100.00% <100.00%> (ø)` | |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=python#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Impacted Files](https://app.codecov.io/gh/python/bedevere/pull/557?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=python) | Coverage Δ | |
|---|---|---|
| [bedevere/stage.py](https://app.codecov.io/gh/python/bedevere/pull/557?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=python#diff-YmVkZXZlcmUvc3RhZ2UucHk=) | `100.00% <100.00%> (ø)` | |
| [tests/test\_stage.py](https://app.codecov.io/gh/python/bedevere/pull/557?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=python#diff-dGVzdHMvdGVzdF9zdGFnZS5weQ==) | `100.00% <100.00%> (ø)` | |


Patch:
diff --git a/bedevere/stage.py b/bedevere/stage.py
index ddd463e..f3914d9 100644
--- a/bedevere/stage.py
+++ b/bedevere/stage.py
@@ -100,6 +100,16 @@ async def stage(gh, issue, blocked_on):
     await gh.post(issue["labels_url"], data=[label_name])
 
 
+async def stage_for_review(gh, pull_request):
+    """Apply "awaiting review" label."""
+    issue = await util.issue_for_PR(gh, pull_request)
+    username = util.user_login(pull_request)
+    if await util.is_core_dev(gh, username):
+        await stage(gh, issue, Blocker.core_review)
+    else:
+        await stage(gh, issue, Blocker.review)
+
+
 @router.register("pull_request", action="opened")
 async def opened_pr(event, gh, *arg, **kwargs):
     """Decide if a new pull request requires a review.
@@ -111,24 +121,20 @@ async def opened_pr(event, gh, *arg, **kwargs):
     pull_request = event.data["pull_request"]
     if pull_request.get("draft"):
         return
-    issue = await util.issue_for_PR(gh, pull_request)
-    username = util.user_login(pull_request)
-    if await util.is_core_dev(gh, username):
-        await stage(gh, issue, Blocker.core_review)
-    else:
-        await stage(gh, issue, Blocker.review)
+    await stage_for_review(gh, pull_request)
 
 
-@router.register("pull_request", action="edited")
-async def edited_pr(event, gh, *arg, **kwargs):
+@router.register("pull_request", action="converted_to_draft")
+async def pr_converted_to_draft(event, gh, *arg, **kwargs):
     pull_request = event.data["pull_request"]
     issue = await util.issue_for_PR(gh, pull_request)
-    username = util.user_login(pull_request)
-    if pull_request.get("draft"):
-        await _remove_stage_labels(gh, issue)
-    else:
-        blocked_on = Blocker.core_review if await util.is_core_dev(gh, username) else Blocker.review
-        await stage(gh, issue, blocked_on)
+    await _remove_stage_labels(gh, issue)
+
+
+@router.register("pull_request", action="ready_for_review")
+async def draft_pr_published(event, gh, *arg, **kwargs):
+    pull_request = event.data["pull_request"]
+    await stage_for_review(gh, pull_request)
 
 
 @router.register("push")


Test patch:
diff --git a/tests/test_stage.py b/tests/test_stage.py
index c25ba89..e5eaf07 100644
--- a/tests/test_stage.py
+++ b/tests/test_stage.py
@@ -99,7 +99,7 @@ async def test_opened_draft_pr():
     assert len(gh.post_) == 0
 
     # Draft PR is published
-    data["action"] = "edited"
+    data["action"] = "ready_for_review"
     data["pull_request"]["draft"] = False
     event = sansio.Event(data, event="pull_request", delivery_id="12345")
     gh = FakeGH(
@@ -111,8 +111,8 @@ async def test_opened_draft_pr():
     assert post_[0] == "https://api.github.com/labels"
     assert post_[1] == [awaiting.Blocker.core_review.value]
 
-    # Published PR is unpublished (set back to Draft)
-    data["action"] = "edited"
+    # Published PR is unpublished (converted to Draft)
+    data["action"] = "converted_to_draft"
     data["pull_request"]["draft"] = True
     encoded_label = "awaiting%20core%20review"
     items[issue_url] = {
@@ -140,6 +140,54 @@ async def test_opened_draft_pr():
     )
 
 
+async def test_edited_pr_title():
+    # regression test for https://github.com/python/bedevere/issues/556
+    # test that editing the PR title doesn't change the Blocker labels
+    username = "itamaro"
+    issue_url = "https://api.github.com/issue/42"
+    data = {
+        "action": "edited",
+        "pull_request": {
+            "user": {
+                "login": username,
+            },
+            "issue_url": issue_url,
+            "draft": False,
+            "title": "So long and thanks for all the fish",
+        },
+        "changes": {
+            "title": "So long and thanks for all the phish",
+        }
+    }
+    event = sansio.Event(data, event="pull_request", delivery_id="12345")
+    teams = [{"name": "python core", "id": 6}]
+    encoded_label = "awaiting%20review"
+    items = {
+        f"https://api.github.com/teams/6/memberships/{username}": gidgethub.BadRequest(
+            status_code=http.HTTPStatus(404)
+        ),
+        issue_url: {
+            "labels": [
+                {
+                    "url": f"https://api.github.com/repos/python/cpython/labels/{encoded_label}",
+                    "name": "awaiting review",
+                },
+                {
+                    "url": "https://api.github.com/repos/python/cpython/labels/CLA%20signed",
+                    "name": "CLA signed",
+                },
+            ],
+            "labels_url": "https://api.github.com/repos/python/cpython/issues/12345/labels{/name}",
+        },
+    }
+    gh = FakeGH(
+        getiter={"https://api.github.com/orgs/python/teams": teams}, getitem=items
+    )
+    await awaiting.router.dispatch(event, gh)
+    assert len(gh.post_) == 0
+    assert gh.delete_url is None
+
+
 async def test_opened_pr():
     # New PR from a core dev.
     username = "brettcannon"