Repository: DefinitelyTyped/dt-mergebot
Pull request #398 (2021-05-10 16:29:08)

Add `merge` blessing and more
Finally sorts out the blessing thing:

* The previous kind of "review" blessing (dismisses a review) happens
  when moving a PR to the `Waiting for Code Reviews` column.

* New "merge" blessing when moving a PR to `Waiting for Author to
  Merge`: makes it possible to self-merge a PR almost as-is (still
  requires no conflicts and a passing CI).


Patch:
diff --git a/docs/how-it-works.md b/docs/how-it-works.md
index 108207d..f043eb6 100644
--- a/docs/how-it-works.md
+++ b/docs/how-it-works.md
@@ -18,8 +18,9 @@ _Disclaimer: This could be out of date, the source of truth is always [compute-p
 - PRs which change DT types and there are no other Definition Owners
 - PRs which change a DT module's infra (`tsconfig.json` etc)
 
-PRs that are blessed (see `info.maintainerBlessed`) are excluded when
-possible.
+PRs that are blessed (see `info.blessingKind`) as `review` are excluded
+when possible, and PRs that are blessed as `merge` can be merged without
+reviews.
 
 ### Stale PRs
 
diff --git a/src/basic.ts b/src/basic.ts
index 8e162cc..95d654e 100644
--- a/src/basic.ts
+++ b/src/basic.ts
@@ -47,3 +47,7 @@ export const LabelNames = [
     "Needs Actions Permission",
     ...StalenessKinds,
 ] as const;
+
+export type ApproverKind = "maintainer" | "owner" | "other";
+
+export type BlessingKind = "merge" | "review" | undefined;
diff --git a/src/compute-pr-actions.ts b/src/compute-pr-actions.ts
index 8124e22..1a3c367 100644
--- a/src/compute-pr-actions.ts
+++ b/src/compute-pr-actions.ts
@@ -1,4 +1,4 @@
-import { ColumnName, LabelName, StalenessKind } from "./basic";
+import { ColumnName, LabelName, StalenessKind, ApproverKind, BlessingKind } from "./basic";
 import * as Comments from "./comments";
 import * as emoji from "./emoji";
 import * as urls from "./urls";
@@ -48,8 +48,6 @@ type Staleness = {
     readonly doTimelineActions: (actions: Actions) => void;
 }
 
-type ApproverKind = "maintainer" | "owner" | "other";
-
 // used to pass around pr info with additional values
 interface ExtendedPrInfo extends PrInfo {
     readonly orig: PrInfo;
@@ -69,6 +67,7 @@ interface ExtendedPrInfo extends PrInfo {
     readonly approverKind: ApproverKind;
     readonly requireMaintainer: boolean;
     readonly blessable: boolean;
+    readonly blessingKind: BlessingKind;
     readonly approvedReviews: (ReviewInfo & { type: "approved" })[];
     readonly changereqReviews: (ReviewInfo & { type: "changereq" })[];
     readonly staleReviews: (ReviewInfo & { type: "stale" })[];
@@ -110,6 +109,7 @@ function extendPrInfo(info: PrInfo): ExtendedPrInfo {
     const hasEditedPackages = packages.length > newPackages.length;
     const requireMaintainer = possiblyEditsInfra || checkConfig || hasMultiplePackages || isUntested || hasNewPackages || tooManyOwners;
     const blessable = !(hasNewPackages || possiblyEditsInfra || noOtherOwners);
+    const blessingKind = getBlessingKind();
     const approvedReviews = info.reviews.filter(r => r.type === "approved") as ExtendedPrInfo["approvedReviews"];
     const changereqReviews = info.reviews.filter(r => r.type === "changereq") as ExtendedPrInfo["changereqReviews"];
     const staleReviews = info.reviews.filter(r => r.type === "stale") as ExtendedPrInfo["staleReviews"];
@@ -122,7 +122,8 @@ function extendPrInfo(info: PrInfo): ExtendedPrInfo {
     const blockedCI = info.ciResult === "action_required";
     const ciResult = // override ciResult: treated as in-progress if it's approved (blockedCI distinguishes it from real unknown)
         blockedCI && !possiblyEditsInfra ? "unknown" : info.ciResult;
-    const canBeSelfMerged = info.ciResult === "pass" && !info.hasMergeConflict && approved;
+    const canBeSelfMerged = info.ciResult === "pass" && !info.hasMergeConflict
+        && (approved || blessingKind === "merge");
     const hasValidMergeRequest = !!(info.mergeOfferDate && info.mergeRequestDate && info.mergeRequestDate > info.mergeOfferDate);
     const needsAuthorAction = failedCI || info.hasMergeConflict || hasChangereqs;
     //      => could be dropped from the extended info and replaced with: info.staleness?.kind === "Abandoned"
@@ -133,7 +134,7 @@ function extendPrInfo(info: PrInfo): ExtendedPrInfo {
         authorIsOwner, editsInfra, possiblyEditsInfra, checkConfig,
         allOwners, otherOwners, noOtherOwners, tooManyOwners, editsOwners,
         canBeSelfMerged, hasValidMergeRequest, pendingCriticalPackages, approved, approverKind,
-        requireMaintainer, blessable, failedCI, blockedCI, ciResult, staleness,
+        requireMaintainer, blessable, blessingKind, failedCI, blockedCI, ciResult, staleness,
         packages, hasMultiplePackages, hasDefinitions, hasTests, isUntested, newPackages, hasNewPackages, hasEditedPackages,
         approvedReviews, changereqReviews, staleReviews, approvedBy, hasChangereqs,
         needsAuthorAction, reviewColumn, isAuthor,
@@ -153,6 +154,12 @@ function extendPrInfo(info: PrInfo): ExtendedPrInfo {
         return undefined;
     }
 
+    function getBlessingKind() {
+        return info.maintainerBlessed === "Waiting for Author to Merge" ? "merge"
+            : info.maintainerBlessed === "Waiting for Code Reviews" ? "review"
+            : undefined;
+    }
+
     function getApprovedBy() {
         return hasChangereqs ? []
             : approvedReviews.map(r => r.isMaintainer ? "maintainer"
@@ -167,7 +174,7 @@ function extendPrInfo(info: PrInfo): ExtendedPrInfo {
     }
 
     function getApproverKind() {
-        const blessed = blessable && info.maintainerBlessed;
+        const blessed = blessable && blessingKind === "review";
         const who: ApproverKind =
             requireMaintainer ? "maintainer" : ({
                 "Well-liked by everyone": "other",
@@ -448,20 +455,22 @@ function createWelcomeComment(info: ExtendedPrInfo, post: (c: Comments.Comment)
     display(``,
             `## Code Reviews`,
             ``);
-    if (info.hasNewPackages) {
+    if (info.blessingKind === "merge") {
+        display("This PR can be merged.");
+    } else if (info.hasNewPackages) {
         display(`This PR adds a new definition, so it needs to be reviewed by ${requiredApprover} before it can be merged.`);
-    } else if (info.popularityLevel === "Critical" && !info.maintainerBlessed) {
+    } else if (info.popularityLevel === "Critical" && info.blessingKind !== "review") {
         display(`Because this is a widely-used package, ${requiredApprover} will need to review it before it can be merged.`);
     } else if (!info.requireMaintainer) {
         const and = info.hasDefinitions && info.hasTests ? "and updated the tests (👏)" : "and there were no type definition changes";
         display(`Because you edited one package ${and}, I can help you merge this PR once someone else signs off on it.`);
-    } else if (info.noOtherOwners && !info.maintainerBlessed) {
+    } else if (info.noOtherOwners && info.blessingKind !== "review") {
         display(`There aren't any other owners of this package, so ${requiredApprover} will review it.`);
-    } else if (info.hasMultiplePackages && !info.maintainerBlessed) {
+    } else if (info.hasMultiplePackages && info.blessingKind !== "review") {
         display(`Because this PR edits multiple packages, it can be merged once it's reviewed by ${requiredApprover}.`);
-    } else if (info.checkConfig && !info.maintainerBlessed) {
+    } else if (info.checkConfig && info.blessingKind !== "review") {
         display(`Because this PR edits the configuration file, it can be merged once it's reviewed by ${requiredApprover}.`);
-    } else if (!info.maintainerBlessed) {
+    } else if (info.blessingKind !== "review") {
         display(`This PR can be merged once it's reviewed by ${requiredApprover}.`);
     } else {
         display("This PR can be merged once it's reviewed.");
@@ -471,13 +480,12 @@ function createWelcomeComment(info: ExtendedPrInfo, post: (c: Comments.Comment)
             `## Status`,
             ``,
             ` * ${emoji.failed(info.hasMergeConflict)} No merge conflicts`);
-
     {
         const result = emoji.result(info.ciResult);
         display(` * ${result.emoji} Continuous integration tests ${result.text}`);
     }
 
-    const approved = emoji.pending(!info.approved);
+    const approved = emoji.pending(!(info.approved || info.blessingKind === "merge"));
 
     if (info.hasNewPackages) {
         display(` * ${approved} Only ${requiredApprover} can approve changes when there are new packages added`);
@@ -485,7 +493,7 @@ function createWelcomeComment(info: ExtendedPrInfo, post: (c: Comments.Comment)
         const infraFiles = info.pkgInfo.find(p => p.name === null)!.files;
         const links = infraFiles.map(reviewLink);
         display(` * ${approved} ${RequiredApprover} needs to approve changes which affect DT infrastructure (${links.join(", ")})`);
-    } else if (criticalNum > 1 && info.maintainerBlessed) {
+    } else if (criticalNum > 1 && info.blessingKind === "review") {
         display(` * ${approved} ${RequiredApprover} needs to approve changes which affect more than one package`);
         for (const p of info.pkgInfo) {
             if (!(p.name && p.popularityLevel === "Critical")) continue;
@@ -493,7 +501,7 @@ function createWelcomeComment(info: ExtendedPrInfo, post: (c: Comments.Comment)
         }
     } else if (info.hasMultiplePackages) {
         display(` * ${approved} ${RequiredApprover} needs to approve changes which affect more than one package`);
-    } else if (!info.requireMaintainer || info.maintainerBlessed) {
+    } else if (!info.requireMaintainer || info.blessingKind === "review") {
         display(` * ${approved} Most recent commit is approved by ${requiredApprover}`);
     } else if (info.noOtherOwners) {
         display(` * ${approved} ${RequiredApprover} can merge changes when there are no other reviewers`);


Test patch:
diff --git a/src/_tests/fixtures/52848/_downloads.json b/src/_tests/fixtures/52848/_downloads.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/src/_tests/fixtures/52848/_downloads.json
@@ -0,0 +1,1 @@
+{}
diff --git a/src/_tests/fixtures/52848/_files.json b/src/_tests/fixtures/52848/_files.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/src/_tests/fixtures/52848/_files.json
@@ -0,0 +1,1 @@
+{}
diff --git a/src/_tests/fixtures/52848/_response.json b/src/_tests/fixtures/52848/_response.json
new file mode 100644
index 0000000..5ee9705
--- /dev/null
+++ b/src/_tests/fixtures/52848/_response.json
@@ -0,0 +1,243 @@
+{
+  "data": {
+    "repository": {
+      "id": "MDEwOlJlcG9zaXRvcnk2MDkzMzE2",
+      "pullRequest": {
+        "id": "MDExOlB1bGxSZXF1ZXN0NjM3MDk5NjE4",
+        "title": "update some Chinese translation",
+        "createdAt": "2021-05-10T07:59:10Z",
+        "author": {
+          "login": "Runtu4378",
+          "__typename": "User"
+        },
+        "authorAssociation": "FIRST_TIME_CONTRIBUTOR",
+        "baseRef": {
+          "name": "master",
+          "__typename": "Ref"
+        },
+        "labels": {
+          "nodes": [
+            {
+              "name": "Edits Infrastructure",
+              "__typename": "Label"
+            }
+          ],
+          "__typename": "LabelConnection"
+        },
+        "isDraft": false,
+        "mergeable": "MERGEABLE",
+        "number": 52848,
+        "state": "OPEN",
+        "headRefOid": "14499debca98ea23cfc47a80a288d5bd32798cff",
+        "timelineItems": {
+          "nodes": [
+            {
+              "actor": {
+                "login": "elibarzilay",
+                "__typename": "User"
+              },
+              "createdAt": "2021-05-10T15:42:52Z",
+              "projectColumnName": "Waiting for Author to Merge",
+              "__typename": "MovedColumnsInProjectEvent"
+            },
+            {
+              "actor": {
+                "login": "typescript-bot",
+                "__typename": "User"
+              },
+              "createdAt": "2021-05-10T15:43:44Z",
+              "projectColumnName": "Needs Maintainer Action",
+              "__typename": "MovedColumnsInProjectEvent"
+            }
+          ],
+          "__typename": "PullRequestTimelineItemsConnection"
+        },
+        "reviews": {
+          "nodes": [],
+          "__typename": "PullRequestReviewConnection"
+        },
+        "commits": {
+          "totalCount": 2,
+          "nodes": [
+            {
+              "commit": {
+                "checkSuites": {
+                  "nodes": [
+                    {
+                      "databaseId": 2696551272,
+                      "app": {
+                        "name": "GitHub Actions",
+                        "__typename": "App"
+                      },
+                      "conclusion": "SUCCESS",
+                      "resourcePath": "/DefinitelyTyped/DefinitelyTyped/commit/14499debca98ea23cfc47a80a288d5bd32798cff/checks?check_suite_id=2696551272",
+                      "status": "COMPLETED",
+                      "url": "https://github.com/DefinitelyTyped/DefinitelyTyped/commit/14499debca98ea23cfc47a80a288d5bd32798cff/checks?check_suite_id=2696551272",
+                      "checkRuns": {
+                        "nodes": [
+                          {
+                            "title": "build",
+                            "__typename": "CheckRun"
+                          }
+                        ],
+                        "__typename": "CheckRunConnection"
+                      },
+                      "__typename": "CheckSuite"
+                    },
+                    {
+                      "databaseId": 2696551642,
+                      "app": {
+                        "name": "GitHub Actions",
+                        "__typename": "App"
+                      },
+                      "conclusion": null,
+                      "resourcePath": "/DefinitelyTyped/DefinitelyTyped/commit/14499debca98ea23cfc47a80a288d5bd32798cff/checks?check_suite_id=2696551642",
+                      "status": "IN_PROGRESS",
+                      "url": "https://github.com/DefinitelyTyped/DefinitelyTyped/commit/14499debca98ea23cfc47a80a288d5bd32798cff/checks?check_suite_id=2696551642",
+                      "checkRuns": {
+                        "nodes": [
+                          {
+                            "title": null,
+                            "__typename": "CheckRun"
+                          }
+                        ],
+                        "__typename": "CheckRunConnection"
+                      },
+                      "__typename": "CheckSuite"
+                    },
+                    {
+                      "databaseId": 2696551646,
+                      "app": {
+                        "name": "GitHub Actions",
+                        "__typename": "App"
+                      },
+                      "conclusion": "SUCCESS",
+                      "resourcePath": "/DefinitelyTyped/DefinitelyTyped/commit/14499debca98ea23cfc47a80a288d5bd32798cff/checks?check_suite_id=2696551646",
+                      "status": "COMPLETED",
+                      "url": "https://github.com/DefinitelyTyped/DefinitelyTyped/commit/14499debca98ea23cfc47a80a288d5bd32798cff/checks?check_suite_id=2696551646",
+                      "checkRuns": {
+                        "nodes": [
+                          {
+                            "title": null,
+                            "__typename": "CheckRun"
+                          }
+                        ],
+                        "__typename": "CheckRunConnection"
+                      },
+                      "__typename": "CheckSuite"
+                    },
+                    {
+                      "databaseId": 2696552691,
+                      "app": {
+                        "name": "Azure Pipelines",
+                        "__typename": "App"
+                      },
+                      "conclusion": "SUCCESS",
+                      "resourcePath": "/DefinitelyTyped/DefinitelyTyped/commit/14499debca98ea23cfc47a80a288d5bd32798cff/checks?check_suite_id=2696552691",
+                      "status": "COMPLETED",
+                      "url": "https://github.com/DefinitelyTyped/DefinitelyTyped/commit/14499debca98ea23cfc47a80a288d5bd32798cff/checks?check_suite_id=2696552691",
+                      "checkRuns": {
+                        "nodes": [
+                          {
+                            "title": "Build #97749 succeeded",
+                            "__typename": "CheckRun"
+                          }
+                        ],
+                        "__typename": "CheckRunConnection"
+                      },
+                      "__typename": "CheckSuite"
+                    }
+                  ],
+                  "__typename": "CheckSuiteConnection"
+                },
+                "status": null,
+                "authoredDate": "2021-05-10T07:57:06Z",
+                "committedDate": "2021-05-10T07:57:06Z",
+                "pushedDate": "2021-05-10T07:57:17Z",
+                "oid": "14499debca98ea23cfc47a80a288d5bd32798cff",
+                "__typename": "Commit"
+              },
+              "__typename": "PullRequestCommit"
+            }
+          ],
+          "__typename": "PullRequestCommitConnection"
+        },
+        "comments": {
+          "totalCount": 2,
+          "nodes": [
+            {
+              "id": "MDEyOklzc3VlQ29tbWVudDgzNjMyODk5Mg==",
+              "author": {
+                "login": "typescript-bot",
+                "__typename": "User"
+              },
+              "body": "@Runtu4378 Thank you for submitting this PR! I see this is your first time submitting to DefinitelyTyped 👋 — I'm the local bot who will help you through the process of getting things through.\n\n***This is a live comment which I will keep updated.***\n\nThis PR touches some part of DefinitelyTyped infrastructure, so a DT maintainer will need to review it. This is rare — did you mean to do this?\n\n## 0 packages in this PR (and infra files)\n\n* Infra files\n  - [`README.cn.md`](https://github.com/DefinitelyTyped/DefinitelyTyped/pull/52848/files/14499debca98ea23cfc47a80a288d5bd32798cff#diff-97a2a8d47e133a2b24ad7bc1a2e25710f534470e28a73efb62da312a3bb19235)\n\n## Code Reviews\n\nThis PR can be merged once it's reviewed.\n\n## Status\n\n * ✅ No merge conflicts\n * ✅ Continuous integration tests have passed\n * 🕐 A DT maintainer needs to approve changes which affect DT infrastructure ([`README.cn.md`](https://github.com/DefinitelyTyped/DefinitelyTyped/pull/52848/files/14499debca98ea23cfc47a80a288d5bd32798cff#diff-97a2a8d47e133a2b24ad7bc1a2e25710f534470e28a73efb62da312a3bb19235))\n\nOnce every item on this list is checked, I'll ask you for permission to merge and publish the changes.\n\n----------------------\n... diagnostics scrubbed ...\n<!--typescript_bot_welcome-->",
+              "createdAt": "2021-05-10T07:59:48Z",
+              "reactions": {
+                "nodes": [],
+                "__typename": "ReactionConnection"
+              },
+              "__typename": "IssueComment"
+            },
+            {
+              "id": "MDEyOklzc3VlQ29tbWVudDgzNjMyOTAwMg==",
+              "author": {
+                "login": "typescript-bot",
+                "__typename": "User"
+              },
+              "body": "🔔 @Runtu4378 — you're the only owner, but it would still be good if you find someone to [review this PR](https://github.com/DefinitelyTyped/DefinitelyTyped/pull/52848/files) in the next few days, otherwise a maintainer will look at it. (And if you do find someone, maybe even recruit them to be a second owner to make future changes easier...)\n<!--typescript_bot_pinging-reviewers-others-->",
+              "createdAt": "2021-05-10T07:59:49Z",
+              "reactions": {
+                "nodes": [],
+                "__typename": "ReactionConnection"
+              },
+              "__typename": "IssueComment"
+            }
+          ],
+          "__typename": "IssueCommentConnection"
+        },
+        "files": {
+          "totalCount": 1,
+          "nodes": [
+            {
+              "path": "README.cn.md",
+              "additions": 81,
+              "deletions": 56,
+              "__typename": "PullRequestChangedFile"
+            }
+          ],
+          "pageInfo": {
+            "hasNextPage": false,
+            "endCursor": "MQ",
+            "__typename": "PageInfo"
+          },
+          "__typename": "PullRequestChangedFileConnection"
+        },
+        "projectCards": {
+          "nodes": [
+            {
+              "id": "MDExOlByb2plY3RDYXJkNjA3NjQ2MTM=",
+              "project": {
+                "id": "MDc6UHJvamVjdDM3NDExMDQ=",
+                "number": 5,
+                "name": "New Pull Request Status Board",
+                "__typename": "Project"
+              },
+              "column": {
+                "id": "MDEzOlByb2plY3RDb2x1bW45ODY3MDA2",
+                "name": "Needs Maintainer Action",
+                "__typename": "ProjectColumn"
+              },
+              "__typename": "ProjectCard"
+            }
+          ],
+          "__typename": "ProjectCardConnection"
+        },
+        "__typename": "PullRequest"
+      },
+      "__typename": "Repository"
+    }
+  },
+  "loading": false,
+  "networkStatus": 7
+}
diff --git a/src/_tests/fixtures/52848/derived.json b/src/_tests/fixtures/52848/derived.json
new file mode 100644
index 0000000..1f23552
--- /dev/null
+++ b/src/_tests/fixtures/52848/derived.json
@@ -0,0 +1,32 @@
+{
+  "type": "info",
+  "now": "2021-05-10T15:46:37.788Z",
+  "pr_number": 52848,
+  "author": "Runtu4378",
+  "headCommitOid": "14499debca98ea23cfc47a80a288d5bd32798cff",
+  "lastPushDate": "2021-05-10T07:57:17.000Z",
+  "lastActivityDate": "2021-05-10T15:42:52.000Z",
+  "maintainerBlessed": "Waiting for Author to Merge",
+  "hasMergeConflict": false,
+  "isFirstContribution": true,
+  "tooManyFiles": false,
+  "popularityLevel": "Well-liked by everyone",
+  "pkgInfo": [
+    {
+      "name": null,
+      "kind": "edit",
+      "files": [
+        {
+          "path": "README.cn.md",
+          "kind": "infrastructure"
+        }
+      ],
+      "owners": [],
+      "addedOwners": [],
+      "deletedOwners": [],
+      "popularityLevel": "Critical"
+    }
+  ],
+  "reviews": [],
+  "ciResult": "pass"
+}
diff --git a/src/_tests/fixtures/52848/mutations.json b/src/_tests/fixtures/52848/mutations.json
new file mode 100644
index 0000000..3008628
--- /dev/null
+++ b/src/_tests/fixtures/52848/mutations.json
@@ -0,0 +1,40 @@
+[
+  {
+    "mutation": "mutation ($input: AddLabelsToLabelableInput!) {\n  addLabelsToLabelable(input: $input) {\n    __typename\n  }\n}\n",
+    "variables": {
+      "input": {
+        "labelIds": [
+          "MDU6TGFiZWw2OTcwMTg5NzI="
+        ],
+        "labelableId": "MDExOlB1bGxSZXF1ZXN0NjM3MDk5NjE4"
+      }
+    }
+  },
+  {
+    "mutation": "mutation ($input: MoveProjectCardInput!) {\n  moveProjectCard(input: $input) {\n    __typename\n  }\n}\n",
+    "variables": {
+      "input": {
+        "cardId": "MDExOlByb2plY3RDYXJkNjA3NjQ2MTM=",
+        "columnId": "MDEzOlByb2plY3RDb2x1bW43NTUyOTIz"
+      }
+    }
+  },
+  {
+    "mutation": "mutation ($input: UpdateIssueCommentInput!) {\n  updateIssueComment(input: $input) {\n    __typename\n  }\n}\n",
+    "variables": {
+      "input": {
+        "id": "MDEyOklzc3VlQ29tbWVudDgzNjMyODk5Mg==",
+        "body": "@Runtu4378 Thank you for submitting this PR! I see this is your first time submitting to DefinitelyTyped 👋 — I'm the local bot who will help you through the process of getting things through.\n\n***This is a live comment which I will keep updated.***\n\nThis PR touches some part of DefinitelyTyped infrastructure, so a DT maintainer will need to review it. This is rare — did you mean to do this?\n\n## 0 packages in this PR (and infra files)\n\n* Infra files\n  - [`README.cn.md`](https://github.com/DefinitelyTyped/DefinitelyTyped/pull/52848/files/14499debca98ea23cfc47a80a288d5bd32798cff#diff-97a2a8d47e133a2b24ad7bc1a2e25710f534470e28a73efb62da312a3bb19235)\n\n## Code Reviews\n\nThis PR can be merged.\n\n## Status\n\n * ✅ No merge conflicts\n * ✅ Continuous integration tests have passed\n * ✅ A DT maintainer needs to approve changes which affect DT infrastructure ([`README.cn.md`](https://github.com/DefinitelyTyped/DefinitelyTyped/pull/52848/files/14499debca98ea23cfc47a80a288d5bd32798cff#diff-97a2a8d47e133a2b24ad7bc1a2e25710f534470e28a73efb62da312a3bb19235))\n\nAll of the items on the list are green. **To merge, you need to post a comment including the string \"Ready to merge\"** to bring in your changes.\n\n----------------------\n... diagnostics scrubbed ...\n<!--typescript_bot_welcome-->"
+      }
+    }
+  },
+  {
+    "mutation": "mutation ($input: AddCommentInput!) {\n  addComment(input: $input) {\n    __typename\n  }\n}\n",
+    "variables": {
+      "input": {
+        "subjectId": "MDExOlB1bGxSZXF1ZXN0NjM3MDk5NjE4",
+        "body": "@Runtu4378 Everything looks good here. Great job! I am ready to merge this PR (at 14499de) on your behalf.\n\nIf you'd like that to happen, please post a comment saying:\n\n> Ready to merge\n\nand I'll merge this PR almost instantly. Thanks for helping out! :heart:\n\n<!--typescript_bot_merge-offer-->"
+      }
+    }
+  }
+]
diff --git a/src/_tests/fixtures/52848/result.json b/src/_tests/fixtures/52848/result.json
new file mode 100644
index 0000000..795057b
--- /dev/null
+++ b/src/_tests/fixtures/52848/result.json
@@ -0,0 +1,24 @@
+{
+  "projectColumn": "Waiting for Author to Merge",
+  "labels": [
+    "Edits Infrastructure",
+    "Self Merge"
+  ],
+  "responseComments": [
+    {
+      "tag": "welcome",
+      "status": "@Runtu4378 Thank you for submitting this PR! I see this is your first time submitting to DefinitelyTyped 👋 — I'm the local bot who will help you through the process of getting things through.\n\n***This is a live comment which I will keep updated.***\n\nThis PR touches some part of DefinitelyTyped infrastructure, so a DT maintainer will need to review it. This is rare — did you mean to do this?\n\n## 0 packages in this PR (and infra files)\n\n* Infra files\n  - [`README.cn.md`](https://github.com/DefinitelyTyped/DefinitelyTyped/pull/52848/files/14499debca98ea23cfc47a80a288d5bd32798cff#diff-97a2a8d47e133a2b24ad7bc1a2e25710f534470e28a73efb62da312a3bb19235)\n\n## Code Reviews\n\nThis PR can be merged.\n\n## Status\n\n * ✅ No merge conflicts\n * ✅ Continuous integration tests have passed\n * ✅ A DT maintainer needs to approve changes which affect DT infrastructure ([`README.cn.md`](https://github.com/DefinitelyTyped/DefinitelyTyped/pull/52848/files/14499debca98ea23cfc47a80a288d5bd32798cff#diff-97a2a8d47e133a2b24ad7bc1a2e25710f534470e28a73efb62da312a3bb19235))\n\nAll of the items on the list are green. **To merge, you need to post a comment including the string \"Ready to merge\"** to bring in your changes.\n\n----------------------\n... diagnostics scrubbed ..."
+    },
+    {
+      "tag": "pinging-reviewers-others",
+      "status": "🔔 @Runtu4378 — you're the only owner, but it would still be good if you find someone to [review this PR](https://github.com/DefinitelyTyped/DefinitelyTyped/pull/52848/files) in the next few days, otherwise a maintainer will look at it. (And if you do find someone, maybe even recruit them to be a second owner to make future changes easier...)"
+    },
+    {
+      "tag": "merge-offer",
+      "status": "@Runtu4378 Everything looks good here. Great job! I am ready to merge this PR (at 14499de) on your behalf.\n\nIf you'd like that to happen, please post a comment saying:\n\n> Ready to merge\n\nand I'll merge this PR almost instantly. Thanks for helping out! :heart:\n"
+    }
+  ],
+  "shouldClose": false,
+  "shouldMerge": false,
+  "shouldUpdateLabels": true
+}