From 5d364a61ba7f22c299e3a732646e936d23fc0ab9 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Tue, 1 Sep 2026 11:45:55 -0700 Subject: [PATCH 1/3] Add fixture for stale PR owner detection Capture PR 75475 demonstrating that a stale pull request head is incorrectly interpreted as changing package owners. --- .../src/_tests/fixtures/75475/_downloads.json | 3 + .../src/_tests/fixtures/75475/_files.json | 4 + .../src/_tests/fixtures/75475/_response.json | 228 ++++++++++++++++++ .../src/_tests/fixtures/75475/derived.json | 69 ++++++ .../src/_tests/fixtures/75475/mutations.json | 11 + .../src/_tests/fixtures/75475/result.json | 20 ++ 6 files changed, 335 insertions(+) create mode 100644 packages/mergebot/src/_tests/fixtures/75475/_downloads.json create mode 100644 packages/mergebot/src/_tests/fixtures/75475/_files.json create mode 100644 packages/mergebot/src/_tests/fixtures/75475/_response.json create mode 100644 packages/mergebot/src/_tests/fixtures/75475/derived.json create mode 100644 packages/mergebot/src/_tests/fixtures/75475/mutations.json create mode 100644 packages/mergebot/src/_tests/fixtures/75475/result.json diff --git a/packages/mergebot/src/_tests/fixtures/75475/_downloads.json b/packages/mergebot/src/_tests/fixtures/75475/_downloads.json new file mode 100644 index 0000000000..d98c3c706f --- /dev/null +++ b/packages/mergebot/src/_tests/fixtures/75475/_downloads.json @@ -0,0 +1,3 @@ +{ + "chrome": 15528020 +} diff --git a/packages/mergebot/src/_tests/fixtures/75475/_files.json b/packages/mergebot/src/_tests/fixtures/75475/_files.json new file mode 100644 index 0000000000..e640d5081e --- /dev/null +++ b/packages/mergebot/src/_tests/fixtures/75475/_files.json @@ -0,0 +1,4 @@ +{ + "eded8b3161a48a130df54daefb1005b3807314b0:types/chrome/package.json": "{\n \"private\": true,\n \"name\": \"@types/chrome\",\n \"version\": \"0.2.9999\",\n \"nonNpm\": \"conflict\",\n \"nonNpmDescription\": \"The complete reference to all APIs made available to Chrome Extensions\",\n \"projects\": [\n \"https://developer.chrome.com/docs/extensions\"\n ],\n \"dependencies\": {\n \"@types/filesystem\": \"*\",\n \"@types/har-format\": \"*\"\n },\n \"devDependencies\": {\n \"@types/chrome\": \"workspace:.\"\n },\n \"owners\": [\n {\n \"name\": \"Matthew Kimber\",\n \"githubUsername\": \"matthewkimber\"\n },\n {\n \"name\": \"otiai10\",\n \"githubUsername\": \"otiai10\"\n },\n {\n \"name\": \"sreimer15\",\n \"githubUsername\": \"sreimer15\"\n },\n {\n \"name\": \"MatCarlson\",\n \"githubUsername\": \"MatCarlson\"\n },\n {\n \"name\": \"ekinsol\",\n \"githubUsername\": \"ekinsol\"\n },\n {\n \"name\": \"Brian Wilson\",\n \"githubUsername\": \"echoabstract\"\n },\n {\n \"name\": \"Sebastiaan Pasma\",\n \"githubUsername\": \"spasma\"\n },\n {\n \"name\": \"bdbai\",\n \"githubUsername\": \"bdbai\"\n },\n {\n \"name\": \"Jason Xian\",\n \"githubUsername\": \"JasonXian\"\n },\n {\n \"name\": \"userTim\",\n \"githubUsername\": \"usertim\"\n },\n {\n \"name\": \"Idan Zeierman\",\n \"githubUsername\": \"idan315\"\n },\n {\n \"name\": \"Nicolas Rodriguez\",\n \"githubUsername\": \"nicolas377\"\n },\n {\n \"name\": \"Ido Salomon\",\n \"githubUsername\": \"idosal\"\n },\n {\n \"name\": \"Federico Brigante\",\n \"githubUsername\": \"fregante\"\n },\n {\n \"name\": \"Erwan Jugand\",\n \"githubUsername\": \"erwanjugand\"\n }\n ]\n}\n", + "4a708f0ad529dfbe67caec58197135d27b223f53:types/chrome/index.d.ts": "// Type definitions for Chrome extension development\n// Project: http://developer.chrome.com/extensions/\n// Definitions by: Matthew Kimber \n// otiai10 \n// couven92 \n// RReverser \n// sreimer15 \n// MatCarlson \n// ekinsol \n// Thierry RΓ©gagnon \n// Brian Wilson \n// Sebastiaan Pasma \n// bdbai \n// pokutuna \n// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped\n// TypeScript Version: 2.4\n\n/// \n/// \n\n////////////////////\n// Global object\n////////////////////\ninterface Window {\n chrome: typeof chrome;\n browser: typeof chrome;\n}\ndeclare var browser: typeof chrome;\n\n////////////////////\n// Accessibility Features\n////////////////////\n/**\n * Use the chrome.accessibilityFeatures API to manage Chrome's accessibility features. This API relies on the ChromeSetting prototype of the type API for getting and setting individual accessibility features. In order to get feature states the extension must request accessibilityFeatures.read permission. For modifying feature state, the extension needs accessibilityFeatures.modify permission. Note that accessibilityFeatures.modify does not imply accessibilityFeatures.read permission.\n * Availability: Since Chrome 37.\n * Permissions: \"accessibilityFeatures.read\"\n * Important: This API works only on Chrome OS.\n */\ndeclare namespace chrome.accessibilityFeatures {\n /** **ChromeOS only.** Spoken feedback (text-to-speech). */\n export var spokenFeedback: chrome.types.ChromeSetting;\n /** **ChromeOS only.** Enlarged cursor. */\n export var largeCursor: chrome.types.ChromeSetting;\n /** **ChromeOS only.** Sticky modifier keys (like shift or alt). */\n export var stickyKeys: chrome.types.ChromeSetting;\n /** **ChromeOS only.** High contrast rendering mode. */\n export var highContrast: chrome.types.ChromeSetting;\n /** **ChromeOS only.** Full screen magnification. */\n export var screenMagnifier: chrome.types.ChromeSetting;\n /** **ChromeOS only.** Auto mouse click after mouse stops moving. */\n export var autoclick: chrome.types.ChromeSetting;\n /** **ChromeOS only.** Virtual on-screen keyboard. */\n export var virtualKeyboard: chrome.types.ChromeSetting;\n /**\n * **ChromeOS only.**\n * Caret highlighting.\n * @since Chrome 51.\n */\n export var caretHighlight: chrome.types.ChromeSetting;\n /**\n * **ChromeOS only.**\n * Cursor highlighting.\n * @since Chrome 51.\n */\n export var cursorHighlight: chrome.types.ChromeSetting;\n /**\n * **ChromeOS only.**\n * Focus highlighting.\n * @since Chrome 51.\n */\n export var focusHighlight: chrome.types.ChromeSetting;\n /**\n * **ChromeOS only.**\n * Select-to-speak.\n * @since Chrome 51.\n */\n export var selectToSpeak: chrome.types.ChromeSetting;\n /**\n * **ChromeOS only.**\n * Switch Access.\n * @since Chrome 51.\n */\n export var switchAccess: chrome.types.ChromeSetting;\n /**\n * @since Chrome 42.\n */\n export var animationPolicy: chrome.types.ChromeSetting;\n}\n\n////////////////////\n// Alarms\n////////////////////\n/**\n * Use the chrome.alarms API to schedule code to run periodically or at a specified time in the future.\n * Availability: Since Chrome 22.\n * Permissions: \"alarms\"\n */\ndeclare namespace chrome.alarms {\n export interface AlarmCreateInfo {\n /** Optional. Length of time in minutes after which the onAlarm event should fire. */\n delayInMinutes?: number;\n /** Optional. If set, the onAlarm event should fire every periodInMinutes minutes after the initial event specified by when or delayInMinutes. If not set, the alarm will only fire once. */\n periodInMinutes?: number;\n /** Optional. Time at which the alarm should fire, in milliseconds past the epoch (e.g. Date.now() + n). */\n when?: number;\n }\n\n export interface Alarm {\n /** Optional. If not null, the alarm is a repeating alarm and will fire again in periodInMinutes minutes. */\n periodInMinutes?: number;\n /** Time at which this alarm was scheduled to fire, in milliseconds past the epoch (e.g. Date.now() + n). For performance reasons, the alarm may have been delayed an arbitrary amount beyond this. */\n scheduledTime: number;\n /** Name of this alarm. */\n name: string;\n }\n\n export interface AlarmEvent extends chrome.events.Event<(alarm: Alarm) => void> { }\n\n /**\n * Creates an alarm. Near the time(s) specified by alarmInfo, the onAlarm event is fired. If there is another alarm with the same name (or no name if none is specified), it will be cancelled and replaced by this alarm.\n * In order to reduce the load on the user's machine, Chrome limits alarms to at most once every 1 minute but may delay them an arbitrary amount more. That is, setting delayInMinutes or periodInMinutes to less than 1 will not be honored and will cause a warning. when can be set to less than 1 minute after \"now\" without warning but won't actually cause the alarm to fire for at least 1 minute.\n * To help you debug your app or extension, when you've loaded it unpacked, there's no limit to how often the alarm can fire.\n * @param alarmInfo Describes when the alarm should fire. The initial time must be specified by either when or delayInMinutes (but not both). If periodInMinutes is set, the alarm will repeat every periodInMinutes minutes after the initial event. If neither when or delayInMinutes is set for a repeating alarm, periodInMinutes is used as the default for delayInMinutes.\n */\n export function create(alarmInfo: AlarmCreateInfo): void;\n /**\n * Creates an alarm. Near the time(s) specified by alarmInfo, the onAlarm event is fired. If there is another alarm with the same name (or no name if none is specified), it will be cancelled and replaced by this alarm.\n * In order to reduce the load on the user's machine, Chrome limits alarms to at most once every 1 minute but may delay them an arbitrary amount more. That is, setting delayInMinutes or periodInMinutes to less than 1 will not be honored and will cause a warning. when can be set to less than 1 minute after \"now\" without warning but won't actually cause the alarm to fire for at least 1 minute.\n * To help you debug your app or extension, when you've loaded it unpacked, there's no limit to how often the alarm can fire.\n * @param name Optional name to identify this alarm. Defaults to the empty string.\n * @param alarmInfo Describes when the alarm should fire. The initial time must be specified by either when or delayInMinutes (but not both). If periodInMinutes is set, the alarm will repeat every periodInMinutes minutes after the initial event. If neither when or delayInMinutes is set for a repeating alarm, periodInMinutes is used as the default for delayInMinutes.\n */\n export function create(name: string, alarmInfo: AlarmCreateInfo): void;\n /**\n * Gets an array of all the alarms.\n * @param callback The callback parameter should be a function that looks like this:\n * function(array of Alarm alarms) {...};\n */\n export function getAll(callback: (alarms: Alarm[]) => void): void;\n /**\n * Clears all alarms.\n * @param callback If you specify the callback parameter, it should be a function that looks like this:\n * function(boolean wasCleared) {...};\n */\n export function clearAll(callback?: (wasCleared: boolean) => void): void;\n /**\n * Clears the alarm with the given name.\n * @param name The name of the alarm to clear. Defaults to the empty string.\n * @param callback If you specify the callback parameter, it should be a function that looks like this:\n * function(boolean wasCleared) {...};\n */\n export function clear(name?: string, callback?: (wasCleared: boolean) => void): void;\n /**\n * Clears the alarm without a name.\n * @param callback If you specify the callback parameter, it should be a function that looks like this:\n * function(boolean wasCleared) {...};\n */\n export function clear(callback: (wasCleared: boolean) => void): void;\n /**\n * Retrieves details about the specified alarm.\n * @param callback The callback parameter should be a function that looks like this:\n * function( Alarm alarm) {...};\n */\n export function get(callback: (alarm: Alarm) => void): void;\n /**\n * Retrieves details about the specified alarm.\n * @param name The name of the alarm to get. Defaults to the empty string.\n * @param callback The callback parameter should be a function that looks like this:\n * function( Alarm alarm) {...};\n */\n export function get(name: string, callback: (alarm: Alarm) => void): void;\n\n /** Fired when an alarm has elapsed. Useful for event pages. */\n export var onAlarm: AlarmEvent;\n}\n\n////////////////////\n// Browser\n////////////////////\n/**\n * Use the chrome.browser API to interact with the Chrome browser associated with\n * the current application and Chrome profile.\n */\ndeclare namespace chrome.browser {\n export interface Options {\n /** The URL to navigate to when the new tab is initially opened. */\n url: string;\n }\n\n /**\n * Opens a new tab in a browser window associated with the current application\n * and Chrome profile. If no browser window for the Chrome profile is opened,\n * a new one is opened prior to creating the new tab.\n * @param options Configures how the tab should be opened.\n * @param callback Called when the tab was successfully\n * created, or failed to be created. If failed, runtime.lastError will be set.\n */\n export functio" +} diff --git a/packages/mergebot/src/_tests/fixtures/75475/_response.json b/packages/mergebot/src/_tests/fixtures/75475/_response.json new file mode 100644 index 0000000000..6644a515d7 --- /dev/null +++ b/packages/mergebot/src/_tests/fixtures/75475/_response.json @@ -0,0 +1,228 @@ +{ + "data": { + "repository": { + "id": "MDEwOlJlcG9zaXRvcnk2MDkzMzE2", + "pullRequest": { + "id": "PR_kwDOAFz6BM8AAAABBycrvQ", + "title": "chrome: Add global `browser` alias", + "createdAt": "2026-09-01T18:13:36Z", + "author": { + "login": "dgp1130", + "__typename": "User" + }, + "authorAssociation": "CONTRIBUTOR", + "baseRef": { + "name": "master", + "__typename": "Ref" + }, + "labels": { + "nodes": [ + { + "name": "Critical package", + "__typename": "Label" + }, + { + "name": "Edits Owners", + "__typename": "Label" + } + ], + "__typename": "LabelConnection" + }, + "isDraft": false, + "mergeable": "MERGEABLE", + "number": 75475, + "state": "OPEN", + "headRefOid": "4a708f0ad529dfbe67caec58197135d27b223f53", + "baseRefOid": "eded8b3161a48a130df54daefb1005b3807314b0", + "changedFiles": 2, + "additions": 8, + "deletions": 0, + "commitIds": { + "totalCount": 1, + "nodes": [ + { + "commit": { + "oid": "4a708f0ad529dfbe67caec58197135d27b223f53", + "parents": { + "nodes": [ + { + "oid": "5af8fdf21579b2afcb42b447b2def9b5dcc8c540", + "__typename": "Commit" + } + ], + "__typename": "CommitConnection" + }, + "__typename": "Commit" + }, + "__typename": "PullRequestCommit" + } + ], + "__typename": "PullRequestCommitConnection" + }, + "timelineItems": { + "nodes": [], + "__typename": "PullRequestTimelineItemsConnection" + }, + "reviews": { + "totalCount": 0, + "nodes": [], + "__typename": "PullRequestReviewConnection" + }, + "commits": { + "totalCount": 1, + "nodes": [ + { + "commit": { + "checkSuites": { + "nodes": [ + { + "databaseId": 90895116570, + "app": { + "name": "GitHub Actions", + "__typename": "App" + }, + "conclusion": "SUCCESS", + "resourcePath": "/DefinitelyTyped/DefinitelyTyped/commit/4a708f0ad529dfbe67caec58197135d27b223f53/checks?check_suite_id=90895116570", + "status": "COMPLETED", + "url": "https://github.com/DefinitelyTyped/DefinitelyTyped/commit/4a708f0ad529dfbe67caec58197135d27b223f53/checks?check_suite_id=90895116570", + "checkRuns": { + "nodes": [ + { + "title": null, + "__typename": "CheckRun" + } + ], + "__typename": "CheckRunConnection" + }, + "createdAt": "2026-09-01T18:13:42Z", + "workflowRun": { + "databaseId": 33542389231, + "file": { + "path": ".github/workflows/CI.yml", + "__typename": "WorkflowRunFile" + }, + "__typename": "WorkflowRun" + }, + "__typename": "CheckSuite" + } + ], + "__typename": "CheckSuiteConnection" + }, + "status": null, + "authoredDate": "2026-09-01T17:35:31Z", + "committedDate": "2026-09-01T18:05:38Z", + "pushedDate": null, + "oid": "4a708f0ad529dfbe67caec58197135d27b223f53", + "__typename": "Commit" + }, + "__typename": "PullRequestCommit" + } + ], + "__typename": "PullRequestCommitConnection" + }, + "comments": { + "totalCount": 3, + "nodes": [ + { + "id": "IC_kwDOAFz6BM8AAAABR7oZYA", + "author": { + "login": "typescript-automation", + "__typename": "Bot" + }, + "authorAssociation": "CONTRIBUTOR", + "databaseId": 5498345824, + "body": "@dgp1130 Thank you for submitting this PR!\n\n***This is a live comment that I will keep updated.***\n\n## 1 package in this PR\n\n* `chrome` β€” [on npm](https://www.npmjs.com/package/chrome), [on unpkg](https://unpkg.com/browse/chrome@latest/)\n - 4 added owners: @couven92, @rreverser, @tregagnon, @pokutuna\n - 7 removed owners: @JasonXian, @usertim, @idan315, @nicolas377, @idosal, @fregante, @erwanjugand\n\n## Code Reviews\n\nBecause this is a widely-used package, a DT maintainer will need to review it before it can be merged.\n\nYou can test the changes of this PR [in the Playground](https://www.typescriptlang.org/play/?dtPR=75475&install-plugin=playground-dt-review).\n\n## Status\n\n * βœ… No merge conflicts\n * βœ… Continuous integration tests have passed\n * πŸ• Most recent commit is approved by a DT maintainer\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", + "createdAt": "2026-09-01T18:14:14Z", + "reactions": { + "nodes": [], + "__typename": "ReactionConnection" + }, + "__typename": "IssueComment" + }, + { + "id": "IC_kwDOAFz6BM8AAAABR7obgQ", + "author": { + "login": "typescript-automation", + "__typename": "Bot" + }, + "authorAssociation": "CONTRIBUTOR", + "databaseId": 5498346369, + "body": "πŸ”” @matthewkimber @otiai10 @sreimer15 @MatCarlson @ekinsol @echoabstract @spasma @bdbai @JasonXian @usertim @idan315 @nicolas377 @idosal @fregante @erwanjugand β€” please [review this PR](https://github.com/DefinitelyTyped/DefinitelyTyped/pull/75475/files) in the next few days. Be sure to explicitly select **`Approve`** or **`Request Changes`** in the GitHub UI so I know what's going on.\n", + "createdAt": "2026-09-01T18:14:17Z", + "reactions": { + "nodes": [], + "__typename": "ReactionConnection" + }, + "__typename": "IssueComment" + }, + { + "id": "IC_kwDOAFz6BM8AAAABR7sgwQ", + "author": { + "login": "DangerBotOSS", + "__typename": "User" + }, + "authorAssociation": "NONE", + "databaseId": 5498413249, + "body": "\n\n\n\n\n## Formatting\n\nThe following files are not formatted:\n1. types/chrome/test/index.ts\n\nConsider running `pnpm dprint fmt` on these files to make review easier.\n

\n Generated by :no_entry_sign: dangerJS against 4a708f0ad529dfbe67caec58197135d27b223f53\n

\n", + "createdAt": "2026-09-01T18:20:01Z", + "reactions": { + "nodes": [], + "__typename": "ReactionConnection" + }, + "__typename": "IssueComment" + } + ], + "__typename": "IssueCommentConnection" + }, + "files": { + "totalCount": 2, + "nodes": [ + { + "path": "types/chrome/index.d.ts", + "additions": 2, + "deletions": 0, + "__typename": "PullRequestChangedFile" + }, + { + "path": "types/chrome/test/index.ts", + "additions": 6, + "deletions": 0, + "__typename": "PullRequestChangedFile" + } + ], + "pageInfo": { + "hasNextPage": false, + "endCursor": "Mg", + "__typename": "PageInfo" + }, + "__typename": "PullRequestChangedFileConnection" + }, + "projectItems": { + "nodes": [ + { + "id": "PVTI_lADOADeBNM4AkH1qzg48-iw", + "project": { + "id": "PVT_kwDOADeBNM4AkH1q", + "number": 1, + "__typename": "ProjectV2" + }, + "fieldValueByName": { + "name": "Needs Maintainer Review", + "field": { + "id": "PVTSSF_lADOADeBNM4AkH1qzgcYOEM", + "__typename": "ProjectV2SingleSelectField" + }, + "__typename": "ProjectV2ItemFieldSingleSelectValue" + }, + "updatedAt": "2026-09-01T18:20:43Z", + "__typename": "ProjectV2Item" + } + ], + "__typename": "ProjectV2ItemConnection" + }, + "__typename": "PullRequest" + }, + "__typename": "Repository" + } + } +} diff --git a/packages/mergebot/src/_tests/fixtures/75475/derived.json b/packages/mergebot/src/_tests/fixtures/75475/derived.json new file mode 100644 index 0000000000..5c6f6c4cab --- /dev/null +++ b/packages/mergebot/src/_tests/fixtures/75475/derived.json @@ -0,0 +1,69 @@ +{ + "type": "info", + "now": "2026-09-01T18:45:54.506Z", + "pr_number": 75475, + "author": "dgp1130", + "headCommitOid": "4a708f0ad529dfbe67caec58197135d27b223f53", + "mergeBaseOid": "eded8b3161a48a130df54daefb1005b3807314b0", + "lastPushDate": "2026-09-01T18:13:36.000Z", + "lastActivityDate": "2026-09-01T18:20:01.000Z", + "hasMergeConflict": false, + "isFirstContribution": false, + "tooManyFiles": false, + "hugeChange": false, + "tooManyCommits": false, + "tooManyReviews": false, + "popularityLevel": "Critical", + "pkgInfo": [ + { + "name": "chrome", + "kind": "edit", + "files": [ + { + "path": "types/chrome/index.d.ts", + "kind": "definition" + }, + { + "path": "types/chrome/test/index.ts", + "kind": "test" + } + ], + "owners": [ + "matthewkimber", + "otiai10", + "sreimer15", + "MatCarlson", + "ekinsol", + "echoabstract", + "spasma", + "bdbai", + "JasonXian", + "usertim", + "idan315", + "nicolas377", + "idosal", + "fregante", + "erwanjugand" + ], + "addedOwners": [ + "couven92", + "rreverser", + "tregagnon", + "pokutuna" + ], + "deletedOwners": [ + "JasonXian", + "usertim", + "idan315", + "nicolas377", + "idosal", + "fregante", + "erwanjugand" + ], + "popularityLevel": "Critical" + } + ], + "reviews": [], + "mainBotCommentID": 5498345824, + "ciResult": "pass" +} diff --git a/packages/mergebot/src/_tests/fixtures/75475/mutations.json b/packages/mergebot/src/_tests/fixtures/75475/mutations.json new file mode 100644 index 0000000000..e83f8b72e0 --- /dev/null +++ b/packages/mergebot/src/_tests/fixtures/75475/mutations.json @@ -0,0 +1,11 @@ +[ + { + "mutation": "mutation ($input: UpdateIssueCommentInput!) {\n updateIssueComment(input: $input) {\n __typename\n }\n}", + "variables": { + "input": { + "id": "IC_kwDOAFz6BM8AAAABR7oZYA", + "body": "@dgp1130 Thank you for submitting this PR!\n\n***This is a live comment that I will keep updated.***\n\n## 1 package in this PR\n\n* `chrome` β€” [on npm](https://www.npmjs.com/package/chrome), [on unpkg](https://unpkg.com/browse/chrome@latest/)\n - 4 added owners: @couven92, @rreverser, @tregagnon, @pokutuna\n - 7 removed owners: @JasonXian, @usertim, @idan315, @nicolas377, @idosal, @fregante, @erwanjugand\n\n## Code Reviews\n\nBecause this is a widely-used package, a DT maintainer will need to review it before it can be merged.\n\nYou can test the changes of this PR [in the Playground](https://www.typescriptlang.org/play/?dtPR=75475&install-plugin=playground-dt-review).\n\n## Status\n\n * βœ… No merge conflicts\n * βœ… Continuous integration tests have passed\n * πŸ• Most recent commit is approved by a DT maintainer\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" + } + } + } +] diff --git a/packages/mergebot/src/_tests/fixtures/75475/result.json b/packages/mergebot/src/_tests/fixtures/75475/result.json new file mode 100644 index 0000000000..cb5fa247f1 --- /dev/null +++ b/packages/mergebot/src/_tests/fixtures/75475/result.json @@ -0,0 +1,20 @@ +{ + "projectColumn": "Needs Maintainer Review", + "labels": [ + "Critical package", + "Edits Owners" + ], + "responseComments": [ + { + "tag": "welcome", + "status": "@dgp1130 Thank you for submitting this PR!\n\n***This is a live comment that I will keep updated.***\n\n## 1 package in this PR\n\n* `chrome` β€” [on npm](https://www.npmjs.com/package/chrome), [on unpkg](https://unpkg.com/browse/chrome@latest/)\n - 4 added owners: @couven92, @rreverser, @tregagnon, @pokutuna\n - 7 removed owners: @JasonXian, @usertim, @idan315, @nicolas377, @idosal, @fregante, @erwanjugand\n\n## Code Reviews\n\nBecause this is a widely-used package, a DT maintainer will need to review it before it can be merged.\n\nYou can test the changes of this PR [in the Playground](https://www.typescriptlang.org/play/?dtPR=75475&install-plugin=playground-dt-review).\n\n## Status\n\n * βœ… No merge conflicts\n * βœ… Continuous integration tests have passed\n * πŸ• Most recent commit is approved by a DT maintainer\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 ..." + }, + { + "tag": "pinging-reviewers", + "status": "πŸ”” @matthewkimber @otiai10 @sreimer15 @MatCarlson @ekinsol @echoabstract @spasma @bdbai @JasonXian @usertim @idan315 @nicolas377 @idosal @fregante @erwanjugand β€” please [review this PR](https://github.com/DefinitelyTyped/DefinitelyTyped/pull/75475/files) in the next few days. Be sure to explicitly select **`Approve`** or **`Request Changes`** in the GitHub UI so I know what's going on." + } + ], + "shouldClose": false, + "shouldMerge": false, + "shouldUpdateLabels": true +} From ea3c9d3160dd3bd72f009827b29521c683ce7335 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Tue, 1 Sep 2026 11:48:10 -0700 Subject: [PATCH 2/3] Use test merge commits for package state Read proposed package metadata from GitHub's generated test merge commit instead of the raw pull request head. Verify that its parents match the reported base and head before trusting it, and fail closed when it is unavailable. --- .../src/_tests/fixturedActions.test.ts | 59 ++++++++++++++++++- .../src/_tests/fixtures/75475/_files.json | 2 +- .../src/_tests/fixtures/75475/_response.json | 18 ++++++ .../src/_tests/fixtures/75475/derived.json | 19 +----- .../src/_tests/fixtures/75475/mutations.json | 13 +++- .../src/_tests/fixtures/75475/result.json | 5 +- packages/mergebot/src/pr-info.ts | 21 ++++++- packages/mergebot/src/queries/pr-query.ts | 9 +++ packages/mergebot/src/queries/schema/gql.ts | 6 +- .../mergebot/src/queries/schema/graphql.ts | 4 +- 10 files changed, 128 insertions(+), 28 deletions(-) diff --git a/packages/mergebot/src/_tests/fixturedActions.test.ts b/packages/mergebot/src/_tests/fixturedActions.test.ts index 7f3caf0405..9e66ddaace 100644 --- a/packages/mergebot/src/_tests/fixturedActions.test.ts +++ b/packages/mergebot/src/_tests/fixturedActions.test.ts @@ -35,8 +35,28 @@ async function testFixture(dir: string) { const prInfo = response.data.repository?.pullRequest; if (!prInfo) throw new Error("Should never happen"); + // Fixtures recorded before potentialMergeCommit was queried use the head tree. + const prInfoWithMergeCommit = + prInfo.potentialMergeCommit === undefined + ? { + ...prInfo, + potentialMergeCommit: { + __typename: "Commit" as const, + oid: prInfo.headRefOid, + parents: { + __typename: "CommitConnection" as const, + totalCount: 2, + nodes: [ + { __typename: "Commit" as const, oid: prInfo.baseRefOid }, + { __typename: "Commit" as const, oid: prInfo.headRefOid }, + ], + }, + }, + } + : prInfo; + const derived = await deriveStateForPR( - prInfo, + prInfoWithMergeCommit, (expr: string) => Promise.resolve(files[expr] as string), (name: string, _until?: Date) => (name in downloads ? downloads[name] : 0), new Date(readJsonSync(derivedPath).now), @@ -58,3 +78,40 @@ describe("Test fixtures", () => { } }); }); + +describe("Potential merge commit validation", () => { + const response: PRQueryResponse = readJsonSync(join(__dirname, "fixtures", "75475", "_response.json")); + const prInfo = response.data.repository?.pullRequest; + const potentialMergeCommit = prInfo?.potentialMergeCommit; + if (!prInfo || !potentialMergeCommit) throw new Error("Fixture must have a potential merge commit"); + + it("fails closed when the potential merge commit is unavailable", async () => { + const derived = await deriveStateForPR({ ...prInfo, potentialMergeCommit: null }); + + expect(derived).toMatchObject({ + type: "error", + message: "No potential merge commit found", + }); + }); + + it("fails closed when the potential merge commit has unexpected parents", async () => { + const derived = await deriveStateForPR({ + ...prInfo, + potentialMergeCommit: { + ...potentialMergeCommit, + parents: { + ...potentialMergeCommit.parents, + nodes: [ + { __typename: "Commit", oid: prInfo.baseRefOid }, + { __typename: "Commit", oid: "attacker-controlled-oid" }, + ], + }, + }, + }); + + expect(derived).toMatchObject({ + type: "error", + message: "Potential merge commit does not match the pull request base and head", + }); + }); +}); diff --git a/packages/mergebot/src/_tests/fixtures/75475/_files.json b/packages/mergebot/src/_tests/fixtures/75475/_files.json index e640d5081e..13e2ce7e4e 100644 --- a/packages/mergebot/src/_tests/fixtures/75475/_files.json +++ b/packages/mergebot/src/_tests/fixtures/75475/_files.json @@ -1,4 +1,4 @@ { "eded8b3161a48a130df54daefb1005b3807314b0:types/chrome/package.json": "{\n \"private\": true,\n \"name\": \"@types/chrome\",\n \"version\": \"0.2.9999\",\n \"nonNpm\": \"conflict\",\n \"nonNpmDescription\": \"The complete reference to all APIs made available to Chrome Extensions\",\n \"projects\": [\n \"https://developer.chrome.com/docs/extensions\"\n ],\n \"dependencies\": {\n \"@types/filesystem\": \"*\",\n \"@types/har-format\": \"*\"\n },\n \"devDependencies\": {\n \"@types/chrome\": \"workspace:.\"\n },\n \"owners\": [\n {\n \"name\": \"Matthew Kimber\",\n \"githubUsername\": \"matthewkimber\"\n },\n {\n \"name\": \"otiai10\",\n \"githubUsername\": \"otiai10\"\n },\n {\n \"name\": \"sreimer15\",\n \"githubUsername\": \"sreimer15\"\n },\n {\n \"name\": \"MatCarlson\",\n \"githubUsername\": \"MatCarlson\"\n },\n {\n \"name\": \"ekinsol\",\n \"githubUsername\": \"ekinsol\"\n },\n {\n \"name\": \"Brian Wilson\",\n \"githubUsername\": \"echoabstract\"\n },\n {\n \"name\": \"Sebastiaan Pasma\",\n \"githubUsername\": \"spasma\"\n },\n {\n \"name\": \"bdbai\",\n \"githubUsername\": \"bdbai\"\n },\n {\n \"name\": \"Jason Xian\",\n \"githubUsername\": \"JasonXian\"\n },\n {\n \"name\": \"userTim\",\n \"githubUsername\": \"usertim\"\n },\n {\n \"name\": \"Idan Zeierman\",\n \"githubUsername\": \"idan315\"\n },\n {\n \"name\": \"Nicolas Rodriguez\",\n \"githubUsername\": \"nicolas377\"\n },\n {\n \"name\": \"Ido Salomon\",\n \"githubUsername\": \"idosal\"\n },\n {\n \"name\": \"Federico Brigante\",\n \"githubUsername\": \"fregante\"\n },\n {\n \"name\": \"Erwan Jugand\",\n \"githubUsername\": \"erwanjugand\"\n }\n ]\n}\n", - "4a708f0ad529dfbe67caec58197135d27b223f53:types/chrome/index.d.ts": "// Type definitions for Chrome extension development\n// Project: http://developer.chrome.com/extensions/\n// Definitions by: Matthew Kimber \n// otiai10 \n// couven92 \n// RReverser \n// sreimer15 \n// MatCarlson \n// ekinsol \n// Thierry RΓ©gagnon \n// Brian Wilson \n// Sebastiaan Pasma \n// bdbai \n// pokutuna \n// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped\n// TypeScript Version: 2.4\n\n/// \n/// \n\n////////////////////\n// Global object\n////////////////////\ninterface Window {\n chrome: typeof chrome;\n browser: typeof chrome;\n}\ndeclare var browser: typeof chrome;\n\n////////////////////\n// Accessibility Features\n////////////////////\n/**\n * Use the chrome.accessibilityFeatures API to manage Chrome's accessibility features. This API relies on the ChromeSetting prototype of the type API for getting and setting individual accessibility features. In order to get feature states the extension must request accessibilityFeatures.read permission. For modifying feature state, the extension needs accessibilityFeatures.modify permission. Note that accessibilityFeatures.modify does not imply accessibilityFeatures.read permission.\n * Availability: Since Chrome 37.\n * Permissions: \"accessibilityFeatures.read\"\n * Important: This API works only on Chrome OS.\n */\ndeclare namespace chrome.accessibilityFeatures {\n /** **ChromeOS only.** Spoken feedback (text-to-speech). */\n export var spokenFeedback: chrome.types.ChromeSetting;\n /** **ChromeOS only.** Enlarged cursor. */\n export var largeCursor: chrome.types.ChromeSetting;\n /** **ChromeOS only.** Sticky modifier keys (like shift or alt). */\n export var stickyKeys: chrome.types.ChromeSetting;\n /** **ChromeOS only.** High contrast rendering mode. */\n export var highContrast: chrome.types.ChromeSetting;\n /** **ChromeOS only.** Full screen magnification. */\n export var screenMagnifier: chrome.types.ChromeSetting;\n /** **ChromeOS only.** Auto mouse click after mouse stops moving. */\n export var autoclick: chrome.types.ChromeSetting;\n /** **ChromeOS only.** Virtual on-screen keyboard. */\n export var virtualKeyboard: chrome.types.ChromeSetting;\n /**\n * **ChromeOS only.**\n * Caret highlighting.\n * @since Chrome 51.\n */\n export var caretHighlight: chrome.types.ChromeSetting;\n /**\n * **ChromeOS only.**\n * Cursor highlighting.\n * @since Chrome 51.\n */\n export var cursorHighlight: chrome.types.ChromeSetting;\n /**\n * **ChromeOS only.**\n * Focus highlighting.\n * @since Chrome 51.\n */\n export var focusHighlight: chrome.types.ChromeSetting;\n /**\n * **ChromeOS only.**\n * Select-to-speak.\n * @since Chrome 51.\n */\n export var selectToSpeak: chrome.types.ChromeSetting;\n /**\n * **ChromeOS only.**\n * Switch Access.\n * @since Chrome 51.\n */\n export var switchAccess: chrome.types.ChromeSetting;\n /**\n * @since Chrome 42.\n */\n export var animationPolicy: chrome.types.ChromeSetting;\n}\n\n////////////////////\n// Alarms\n////////////////////\n/**\n * Use the chrome.alarms API to schedule code to run periodically or at a specified time in the future.\n * Availability: Since Chrome 22.\n * Permissions: \"alarms\"\n */\ndeclare namespace chrome.alarms {\n export interface AlarmCreateInfo {\n /** Optional. Length of time in minutes after which the onAlarm event should fire. */\n delayInMinutes?: number;\n /** Optional. If set, the onAlarm event should fire every periodInMinutes minutes after the initial event specified by when or delayInMinutes. If not set, the alarm will only fire once. */\n periodInMinutes?: number;\n /** Optional. Time at which the alarm should fire, in milliseconds past the epoch (e.g. Date.now() + n). */\n when?: number;\n }\n\n export interface Alarm {\n /** Optional. If not null, the alarm is a repeating alarm and will fire again in periodInMinutes minutes. */\n periodInMinutes?: number;\n /** Time at which this alarm was scheduled to fire, in milliseconds past the epoch (e.g. Date.now() + n). For performance reasons, the alarm may have been delayed an arbitrary amount beyond this. */\n scheduledTime: number;\n /** Name of this alarm. */\n name: string;\n }\n\n export interface AlarmEvent extends chrome.events.Event<(alarm: Alarm) => void> { }\n\n /**\n * Creates an alarm. Near the time(s) specified by alarmInfo, the onAlarm event is fired. If there is another alarm with the same name (or no name if none is specified), it will be cancelled and replaced by this alarm.\n * In order to reduce the load on the user's machine, Chrome limits alarms to at most once every 1 minute but may delay them an arbitrary amount more. That is, setting delayInMinutes or periodInMinutes to less than 1 will not be honored and will cause a warning. when can be set to less than 1 minute after \"now\" without warning but won't actually cause the alarm to fire for at least 1 minute.\n * To help you debug your app or extension, when you've loaded it unpacked, there's no limit to how often the alarm can fire.\n * @param alarmInfo Describes when the alarm should fire. The initial time must be specified by either when or delayInMinutes (but not both). If periodInMinutes is set, the alarm will repeat every periodInMinutes minutes after the initial event. If neither when or delayInMinutes is set for a repeating alarm, periodInMinutes is used as the default for delayInMinutes.\n */\n export function create(alarmInfo: AlarmCreateInfo): void;\n /**\n * Creates an alarm. Near the time(s) specified by alarmInfo, the onAlarm event is fired. If there is another alarm with the same name (or no name if none is specified), it will be cancelled and replaced by this alarm.\n * In order to reduce the load on the user's machine, Chrome limits alarms to at most once every 1 minute but may delay them an arbitrary amount more. That is, setting delayInMinutes or periodInMinutes to less than 1 will not be honored and will cause a warning. when can be set to less than 1 minute after \"now\" without warning but won't actually cause the alarm to fire for at least 1 minute.\n * To help you debug your app or extension, when you've loaded it unpacked, there's no limit to how often the alarm can fire.\n * @param name Optional name to identify this alarm. Defaults to the empty string.\n * @param alarmInfo Describes when the alarm should fire. The initial time must be specified by either when or delayInMinutes (but not both). If periodInMinutes is set, the alarm will repeat every periodInMinutes minutes after the initial event. If neither when or delayInMinutes is set for a repeating alarm, periodInMinutes is used as the default for delayInMinutes.\n */\n export function create(name: string, alarmInfo: AlarmCreateInfo): void;\n /**\n * Gets an array of all the alarms.\n * @param callback The callback parameter should be a function that looks like this:\n * function(array of Alarm alarms) {...};\n */\n export function getAll(callback: (alarms: Alarm[]) => void): void;\n /**\n * Clears all alarms.\n * @param callback If you specify the callback parameter, it should be a function that looks like this:\n * function(boolean wasCleared) {...};\n */\n export function clearAll(callback?: (wasCleared: boolean) => void): void;\n /**\n * Clears the alarm with the given name.\n * @param name The name of the alarm to clear. Defaults to the empty string.\n * @param callback If you specify the callback parameter, it should be a function that looks like this:\n * function(boolean wasCleared) {...};\n */\n export function clear(name?: string, callback?: (wasCleared: boolean) => void): void;\n /**\n * Clears the alarm without a name.\n * @param callback If you specify the callback parameter, it should be a function that looks like this:\n * function(boolean wasCleared) {...};\n */\n export function clear(callback: (wasCleared: boolean) => void): void;\n /**\n * Retrieves details about the specified alarm.\n * @param callback The callback parameter should be a function that looks like this:\n * function( Alarm alarm) {...};\n */\n export function get(callback: (alarm: Alarm) => void): void;\n /**\n * Retrieves details about the specified alarm.\n * @param name The name of the alarm to get. Defaults to the empty string.\n * @param callback The callback parameter should be a function that looks like this:\n * function( Alarm alarm) {...};\n */\n export function get(name: string, callback: (alarm: Alarm) => void): void;\n\n /** Fired when an alarm has elapsed. Useful for event pages. */\n export var onAlarm: AlarmEvent;\n}\n\n////////////////////\n// Browser\n////////////////////\n/**\n * Use the chrome.browser API to interact with the Chrome browser associated with\n * the current application and Chrome profile.\n */\ndeclare namespace chrome.browser {\n export interface Options {\n /** The URL to navigate to when the new tab is initially opened. */\n url: string;\n }\n\n /**\n * Opens a new tab in a browser window associated with the current application\n * and Chrome profile. If no browser window for the Chrome profile is opened,\n * a new one is opened prior to creating the new tab.\n * @param options Configures how the tab should be opened.\n * @param callback Called when the tab was successfully\n * created, or failed to be created. If failed, runtime.lastError will be set.\n */\n export functio" + "b9c31f6adfcb83f41dac2e1a3a628801f2e775eb:types/chrome/package.json": "{\n \"private\": true,\n \"name\": \"@types/chrome\",\n \"version\": \"0.2.9999\",\n \"nonNpm\": \"conflict\",\n \"nonNpmDescription\": \"The complete reference to all APIs made available to Chrome Extensions\",\n \"projects\": [\n \"https://developer.chrome.com/docs/extensions\"\n ],\n \"dependencies\": {\n \"@types/filesystem\": \"*\",\n \"@types/har-format\": \"*\"\n },\n \"devDependencies\": {\n \"@types/chrome\": \"workspace:.\"\n },\n \"owners\": [\n {\n \"name\": \"Matthew Kimber\",\n \"githubUsername\": \"matthewkimber\"\n },\n {\n \"name\": \"otiai10\",\n \"githubUsername\": \"otiai10\"\n },\n {\n \"name\": \"sreimer15\",\n \"githubUsername\": \"sreimer15\"\n },\n {\n \"name\": \"MatCarlson\",\n \"githubUsername\": \"MatCarlson\"\n },\n {\n \"name\": \"ekinsol\",\n \"githubUsername\": \"ekinsol\"\n },\n {\n \"name\": \"Brian Wilson\",\n \"githubUsername\": \"echoabstract\"\n },\n {\n \"name\": \"Sebastiaan Pasma\",\n \"githubUsername\": \"spasma\"\n },\n {\n \"name\": \"bdbai\",\n \"githubUsername\": \"bdbai\"\n },\n {\n \"name\": \"Jason Xian\",\n \"githubUsername\": \"JasonXian\"\n },\n {\n \"name\": \"userTim\",\n \"githubUsername\": \"usertim\"\n },\n {\n \"name\": \"Idan Zeierman\",\n \"githubUsername\": \"idan315\"\n },\n {\n \"name\": \"Nicolas Rodriguez\",\n \"githubUsername\": \"nicolas377\"\n },\n {\n \"name\": \"Ido Salomon\",\n \"githubUsername\": \"idosal\"\n },\n {\n \"name\": \"Federico Brigante\",\n \"githubUsername\": \"fregante\"\n },\n {\n \"name\": \"Erwan Jugand\",\n \"githubUsername\": \"erwanjugand\"\n }\n ]\n}\n" } diff --git a/packages/mergebot/src/_tests/fixtures/75475/_response.json b/packages/mergebot/src/_tests/fixtures/75475/_response.json index 6644a515d7..cc5e4efc22 100644 --- a/packages/mergebot/src/_tests/fixtures/75475/_response.json +++ b/packages/mergebot/src/_tests/fixtures/75475/_response.json @@ -34,6 +34,24 @@ "state": "OPEN", "headRefOid": "4a708f0ad529dfbe67caec58197135d27b223f53", "baseRefOid": "eded8b3161a48a130df54daefb1005b3807314b0", + "potentialMergeCommit": { + "oid": "b9c31f6adfcb83f41dac2e1a3a628801f2e775eb", + "parents": { + "totalCount": 2, + "nodes": [ + { + "oid": "eded8b3161a48a130df54daefb1005b3807314b0", + "__typename": "Commit" + }, + { + "oid": "4a708f0ad529dfbe67caec58197135d27b223f53", + "__typename": "Commit" + } + ], + "__typename": "CommitConnection" + }, + "__typename": "Commit" + }, "changedFiles": 2, "additions": 8, "deletions": 0, diff --git a/packages/mergebot/src/_tests/fixtures/75475/derived.json b/packages/mergebot/src/_tests/fixtures/75475/derived.json index 5c6f6c4cab..7dd10ddaab 100644 --- a/packages/mergebot/src/_tests/fixtures/75475/derived.json +++ b/packages/mergebot/src/_tests/fixtures/75475/derived.json @@ -1,6 +1,6 @@ { "type": "info", - "now": "2026-09-01T18:45:54.506Z", + "now": "2026-09-01T18:46:48.035Z", "pr_number": 75475, "author": "dgp1130", "headCommitOid": "4a708f0ad529dfbe67caec58197135d27b223f53", @@ -45,21 +45,8 @@ "fregante", "erwanjugand" ], - "addedOwners": [ - "couven92", - "rreverser", - "tregagnon", - "pokutuna" - ], - "deletedOwners": [ - "JasonXian", - "usertim", - "idan315", - "nicolas377", - "idosal", - "fregante", - "erwanjugand" - ], + "addedOwners": [], + "deletedOwners": [], "popularityLevel": "Critical" } ], diff --git a/packages/mergebot/src/_tests/fixtures/75475/mutations.json b/packages/mergebot/src/_tests/fixtures/75475/mutations.json index e83f8b72e0..5189dfbb0d 100644 --- a/packages/mergebot/src/_tests/fixtures/75475/mutations.json +++ b/packages/mergebot/src/_tests/fixtures/75475/mutations.json @@ -4,7 +4,18 @@ "variables": { "input": { "id": "IC_kwDOAFz6BM8AAAABR7oZYA", - "body": "@dgp1130 Thank you for submitting this PR!\n\n***This is a live comment that I will keep updated.***\n\n## 1 package in this PR\n\n* `chrome` β€” [on npm](https://www.npmjs.com/package/chrome), [on unpkg](https://unpkg.com/browse/chrome@latest/)\n - 4 added owners: @couven92, @rreverser, @tregagnon, @pokutuna\n - 7 removed owners: @JasonXian, @usertim, @idan315, @nicolas377, @idosal, @fregante, @erwanjugand\n\n## Code Reviews\n\nBecause this is a widely-used package, a DT maintainer will need to review it before it can be merged.\n\nYou can test the changes of this PR [in the Playground](https://www.typescriptlang.org/play/?dtPR=75475&install-plugin=playground-dt-review).\n\n## Status\n\n * βœ… No merge conflicts\n * βœ… Continuous integration tests have passed\n * πŸ• Most recent commit is approved by a DT maintainer\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" + "body": "@dgp1130 Thank you for submitting this PR!\n\n***This is a live comment that I will keep updated.***\n\n## 1 package in this PR\n\n* `chrome` β€” [on npm](https://www.npmjs.com/package/chrome), [on unpkg](https://unpkg.com/browse/chrome@latest/)\n\n## Code Reviews\n\nBecause this is a widely-used package, a DT maintainer will need to review it before it can be merged.\n\nYou can test the changes of this PR [in the Playground](https://www.typescriptlang.org/play/?dtPR=75475&install-plugin=playground-dt-review).\n\n## Status\n\n * βœ… No merge conflicts\n * βœ… Continuous integration tests have passed\n * πŸ• Most recent commit is approved by a DT maintainer\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" + } + } + }, + { + "mutation": "mutation ($input: RemoveLabelsFromLabelableInput!) {\n removeLabelsFromLabelable(input: $input) {\n __typename\n }\n}", + "variables": { + "input": { + "labelIds": [ + "MDU6TGFiZWwyNDk1OTc2ODI5" + ], + "labelableId": "PR_kwDOAFz6BM8AAAABBycrvQ" } } } diff --git a/packages/mergebot/src/_tests/fixtures/75475/result.json b/packages/mergebot/src/_tests/fixtures/75475/result.json index cb5fa247f1..33b01784c8 100644 --- a/packages/mergebot/src/_tests/fixtures/75475/result.json +++ b/packages/mergebot/src/_tests/fixtures/75475/result.json @@ -1,13 +1,12 @@ { "projectColumn": "Needs Maintainer Review", "labels": [ - "Critical package", - "Edits Owners" + "Critical package" ], "responseComments": [ { "tag": "welcome", - "status": "@dgp1130 Thank you for submitting this PR!\n\n***This is a live comment that I will keep updated.***\n\n## 1 package in this PR\n\n* `chrome` β€” [on npm](https://www.npmjs.com/package/chrome), [on unpkg](https://unpkg.com/browse/chrome@latest/)\n - 4 added owners: @couven92, @rreverser, @tregagnon, @pokutuna\n - 7 removed owners: @JasonXian, @usertim, @idan315, @nicolas377, @idosal, @fregante, @erwanjugand\n\n## Code Reviews\n\nBecause this is a widely-used package, a DT maintainer will need to review it before it can be merged.\n\nYou can test the changes of this PR [in the Playground](https://www.typescriptlang.org/play/?dtPR=75475&install-plugin=playground-dt-review).\n\n## Status\n\n * βœ… No merge conflicts\n * βœ… Continuous integration tests have passed\n * πŸ• Most recent commit is approved by a DT maintainer\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 ..." + "status": "@dgp1130 Thank you for submitting this PR!\n\n***This is a live comment that I will keep updated.***\n\n## 1 package in this PR\n\n* `chrome` β€” [on npm](https://www.npmjs.com/package/chrome), [on unpkg](https://unpkg.com/browse/chrome@latest/)\n\n## Code Reviews\n\nBecause this is a widely-used package, a DT maintainer will need to review it before it can be merged.\n\nYou can test the changes of this PR [in the Playground](https://www.typescriptlang.org/play/?dtPR=75475&install-plugin=playground-dt-review).\n\n## Status\n\n * βœ… No merge conflicts\n * βœ… Continuous integration tests have passed\n * πŸ• Most recent commit is approved by a DT maintainer\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 ..." }, { "tag": "pinging-reviewers", diff --git a/packages/mergebot/src/pr-info.ts b/packages/mergebot/src/pr-info.ts index 52bc288e92..f2adc7323a 100644 --- a/packages/mergebot/src/pr-info.ts +++ b/packages/mergebot/src/pr-info.ts @@ -204,6 +204,23 @@ function getTrustedBase(pr: PR_repository_pullRequest): string { return pr.baseRefOid || "master"; } +function getTrustedMergeCommit(pr: PR_repository_pullRequest): string | Error { + const mergeCommit = pr.potentialMergeCommit; + if (!mergeCommit) return new Error("No potential merge commit found"); + + const parents = noNullish(mergeCommit.parents.nodes?.map((parent) => parent?.oid)); + if ( + mergeCommit.parents.totalCount !== 2 || + parents.length !== 2 || + !parents.includes(pr.baseRefOid) || + !parents.includes(pr.headRefOid) + ) { + return new Error("Potential merge commit does not match the pull request base and head"); + } + + return mergeCommit.oid; +} + // The GQL response => Useful data for us export async function deriveStateForPR( prInfo: PR_repository_pullRequest, @@ -223,6 +240,8 @@ export async function deriveStateForPR( // Always compare against the actual base branch tip, never against an OID derived from PR // commit ancestry (which an attacker can influence by pushing >100 commits). const baseId = getTrustedBase(prInfo); + const mergeId = getTrustedMergeCommit(prInfo); + if (mergeId instanceof Error) return botError(mergeId.message); // commitIds is `commits(last: 100)`; if there are more commits than that, we cannot reason // safely about the PR's history and force a maintainer review downstream. const tooManyCommits = (prInfo.commitIds.totalCount ?? 0) > (prInfo.commitIds.nodes?.length ?? 0); @@ -261,7 +280,7 @@ export async function deriveStateForPR( .map((f) => f.path) .sort(); if (paths.length > fileLimit) paths.length = fileLimit; // redundant, but just in case - const pkgInfoEtc = await getPackageInfosEtc(paths, prInfo.headRefOid, baseId, fetchFile, async (name) => + const pkgInfoEtc = await getPackageInfosEtc(paths, mergeId, baseId, fetchFile, async (name) => getDownloads(name, lastPushDate), ); if (pkgInfoEtc instanceof Error) return botError(pkgInfoEtc.message); diff --git a/packages/mergebot/src/queries/pr-query.ts b/packages/mergebot/src/queries/pr-query.ts index ff0baca416..3869c37ff0 100644 --- a/packages/mergebot/src/queries/pr-query.ts +++ b/packages/mergebot/src/queries/pr-query.ts @@ -46,6 +46,15 @@ const getPRInfoQueryFirst: TypedDocumentNode = gql` state headRefOid baseRefOid + potentialMergeCommit { + oid + parents(first: 2) { + totalCount + nodes { + oid + } + } + } changedFiles additions deletions diff --git a/packages/mergebot/src/queries/schema/gql.ts b/packages/mergebot/src/queries/schema/gql.ts index 1762dc7747..4fd3709814 100644 --- a/packages/mergebot/src/queries/schema/gql.ts +++ b/packages/mergebot/src/queries/schema/gql.ts @@ -22,7 +22,7 @@ type Documents = { "\n query GetFileContent($owner: String!, $name: String!, $expr: String!) {\n repository(owner: $owner, name: $name) {\n id\n object(expression: $expr) {\n ... on Blob {\n text\n byteSize\n }\n }\n }\n }\n": typeof types.GetFileContentDocument, "\n query GetLabels($endCursor: String) {\n repository(name: \"DefinitelyTyped\", owner: \"DefinitelyTyped\") {\n id\n labels(first: 100, after: $endCursor) {\n nodes {\n id\n name\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n }\n }\n }\n": typeof types.GetLabelsDocument, "\n query GetProjectColumns($cursor: String) {\n repository(name: \"DefinitelyTyped\", owner: \"DefinitelyTyped\") {\n id\n projectV2(number: 1) {\n id\n fields(first: 100, after: $cursor) {\n pageInfo {\n startCursor\n hasNextPage\n endCursor\n }\n nodes {\n ... on ProjectV2SingleSelectField {\n name\n options {\n id\n name\n }\n }\n }\n }\n }\n }\n }\n": typeof types.GetProjectColumnsDocument, - "\n query PR($prNumber: Int!) {\n repository(owner: \"DefinitelyTyped\", name: \"DefinitelyTyped\") {\n id\n pullRequest(number: $prNumber) {\n id\n title\n createdAt\n author {\n login\n }\n authorAssociation\n baseRef {\n name\n }\n labels(first: 100) {\n nodes {\n name\n }\n }\n isDraft\n mergeable\n number\n state\n headRefOid\n baseRefOid\n changedFiles\n additions\n deletions\n\n commitIds: commits(last: 100) {\n totalCount\n nodes {\n commit {\n oid\n parents(first: 3) {\n nodes {\n oid\n }\n }\n }\n }\n }\n\n timelineItems(\n last: 200\n itemTypes: [\n REOPENED_EVENT\n READY_FOR_REVIEW_EVENT\n MOVED_COLUMNS_IN_PROJECT_EVENT\n HEAD_REF_FORCE_PUSHED_EVENT\n ]\n ) {\n nodes {\n ... on ReopenedEvent {\n createdAt\n }\n ... on ReadyForReviewEvent {\n createdAt\n }\n ... on MovedColumnsInProjectEvent {\n actor {\n login\n }\n createdAt\n projectColumnName\n }\n ... on HeadRefForcePushedEvent {\n actor {\n login\n }\n createdAt\n }\n }\n }\n\n reviews(last: 100) {\n totalCount\n nodes {\n author {\n login\n }\n commit {\n oid\n }\n comments(last: 10) {\n nodes {\n author {\n login\n }\n createdAt\n }\n }\n authorAssociation\n state\n submittedAt\n url\n }\n }\n\n commits(last: 1) {\n totalCount\n nodes {\n commit {\n checkSuites(first: 100) {\n nodes {\n databaseId\n app {\n name\n }\n conclusion\n resourcePath\n status\n url\n checkRuns(last: 1) {\n nodes {\n title\n }\n }\n createdAt\n workflowRun {\n databaseId\n file {\n path\n }\n }\n }\n }\n status {\n state\n contexts {\n state\n description\n creator {\n login\n }\n targetUrl\n }\n }\n authoredDate\n committedDate\n pushedDate\n oid\n }\n }\n }\n\n comments(last: 100) {\n totalCount\n nodes {\n id\n author {\n login\n }\n authorAssociation\n databaseId\n body\n createdAt\n reactions(first: 100, content: THUMBS_UP) {\n nodes {\n user {\n login\n }\n }\n }\n }\n }\n\n files(first: 100) {\n totalCount\n nodes {\n path\n additions\n deletions\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n }\n\n projectItems(first: 10) {\n nodes {\n id\n project {\n id\n number\n }\n fieldValueByName(name: \"Status\") {\n ... on ProjectV2ItemFieldSingleSelectValue {\n name\n field {\n ... on ProjectV2SingleSelectField {\n id\n }\n }\n }\n }\n updatedAt\n }\n }\n }\n }\n }\n": typeof types.PrDocument, + "\n query PR($prNumber: Int!) {\n repository(owner: \"DefinitelyTyped\", name: \"DefinitelyTyped\") {\n id\n pullRequest(number: $prNumber) {\n id\n title\n createdAt\n author {\n login\n }\n authorAssociation\n baseRef {\n name\n }\n labels(first: 100) {\n nodes {\n name\n }\n }\n isDraft\n mergeable\n number\n state\n headRefOid\n baseRefOid\n potentialMergeCommit {\n oid\n parents(first: 2) {\n totalCount\n nodes {\n oid\n }\n }\n }\n changedFiles\n additions\n deletions\n\n commitIds: commits(last: 100) {\n totalCount\n nodes {\n commit {\n oid\n parents(first: 3) {\n nodes {\n oid\n }\n }\n }\n }\n }\n\n timelineItems(\n last: 200\n itemTypes: [\n REOPENED_EVENT\n READY_FOR_REVIEW_EVENT\n MOVED_COLUMNS_IN_PROJECT_EVENT\n HEAD_REF_FORCE_PUSHED_EVENT\n ]\n ) {\n nodes {\n ... on ReopenedEvent {\n createdAt\n }\n ... on ReadyForReviewEvent {\n createdAt\n }\n ... on MovedColumnsInProjectEvent {\n actor {\n login\n }\n createdAt\n projectColumnName\n }\n ... on HeadRefForcePushedEvent {\n actor {\n login\n }\n createdAt\n }\n }\n }\n\n reviews(last: 100) {\n totalCount\n nodes {\n author {\n login\n }\n commit {\n oid\n }\n comments(last: 10) {\n nodes {\n author {\n login\n }\n createdAt\n }\n }\n authorAssociation\n state\n submittedAt\n url\n }\n }\n\n commits(last: 1) {\n totalCount\n nodes {\n commit {\n checkSuites(first: 100) {\n nodes {\n databaseId\n app {\n name\n }\n conclusion\n resourcePath\n status\n url\n checkRuns(last: 1) {\n nodes {\n title\n }\n }\n createdAt\n workflowRun {\n databaseId\n file {\n path\n }\n }\n }\n }\n status {\n state\n contexts {\n state\n description\n creator {\n login\n }\n targetUrl\n }\n }\n authoredDate\n committedDate\n pushedDate\n oid\n }\n }\n }\n\n comments(last: 100) {\n totalCount\n nodes {\n id\n author {\n login\n }\n authorAssociation\n databaseId\n body\n createdAt\n reactions(first: 100, content: THUMBS_UP) {\n nodes {\n user {\n login\n }\n }\n }\n }\n }\n\n files(first: 100) {\n totalCount\n nodes {\n path\n additions\n deletions\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n }\n\n projectItems(first: 10) {\n nodes {\n id\n project {\n id\n number\n }\n fieldValueByName(name: \"Status\") {\n ... on ProjectV2ItemFieldSingleSelectValue {\n name\n field {\n ... on ProjectV2SingleSelectField {\n id\n }\n }\n }\n }\n updatedAt\n }\n }\n }\n }\n }\n": typeof types.PrDocument, "\n query PRFiles($prNumber: Int!, $endCursor: String) {\n repository(owner: \"DefinitelyTyped\", name: \"DefinitelyTyped\") {\n pullRequest(number: $prNumber) {\n files(first: 100, after: $endCursor) {\n totalCount\n nodes {\n path\n additions\n deletions\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n }\n }\n }\n }\n": typeof types.PrFilesDocument, "\n query GetProjectBoardCards($cursor: String) {\n repository(owner: \"DefinitelyTyped\", name: \"DefinitelyTyped\") {\n projectV2(number: 1) {\n id\n items(first: 100, after: $cursor) {\n pageInfo {\n startCursor\n hasNextPage\n endCursor\n }\n totalCount\n nodes {\n id\n fieldValueByName(name: \"Status\") {\n ... on ProjectV2ItemFieldSingleSelectValue {\n name\n }\n }\n updatedAt\n }\n }\n }\n }\n }\n": typeof types.GetProjectBoardCardsDocument, }; @@ -35,7 +35,7 @@ const documents: Documents = { "\n query GetFileContent($owner: String!, $name: String!, $expr: String!) {\n repository(owner: $owner, name: $name) {\n id\n object(expression: $expr) {\n ... on Blob {\n text\n byteSize\n }\n }\n }\n }\n": types.GetFileContentDocument, "\n query GetLabels($endCursor: String) {\n repository(name: \"DefinitelyTyped\", owner: \"DefinitelyTyped\") {\n id\n labels(first: 100, after: $endCursor) {\n nodes {\n id\n name\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n }\n }\n }\n": types.GetLabelsDocument, "\n query GetProjectColumns($cursor: String) {\n repository(name: \"DefinitelyTyped\", owner: \"DefinitelyTyped\") {\n id\n projectV2(number: 1) {\n id\n fields(first: 100, after: $cursor) {\n pageInfo {\n startCursor\n hasNextPage\n endCursor\n }\n nodes {\n ... on ProjectV2SingleSelectField {\n name\n options {\n id\n name\n }\n }\n }\n }\n }\n }\n }\n": types.GetProjectColumnsDocument, - "\n query PR($prNumber: Int!) {\n repository(owner: \"DefinitelyTyped\", name: \"DefinitelyTyped\") {\n id\n pullRequest(number: $prNumber) {\n id\n title\n createdAt\n author {\n login\n }\n authorAssociation\n baseRef {\n name\n }\n labels(first: 100) {\n nodes {\n name\n }\n }\n isDraft\n mergeable\n number\n state\n headRefOid\n baseRefOid\n changedFiles\n additions\n deletions\n\n commitIds: commits(last: 100) {\n totalCount\n nodes {\n commit {\n oid\n parents(first: 3) {\n nodes {\n oid\n }\n }\n }\n }\n }\n\n timelineItems(\n last: 200\n itemTypes: [\n REOPENED_EVENT\n READY_FOR_REVIEW_EVENT\n MOVED_COLUMNS_IN_PROJECT_EVENT\n HEAD_REF_FORCE_PUSHED_EVENT\n ]\n ) {\n nodes {\n ... on ReopenedEvent {\n createdAt\n }\n ... on ReadyForReviewEvent {\n createdAt\n }\n ... on MovedColumnsInProjectEvent {\n actor {\n login\n }\n createdAt\n projectColumnName\n }\n ... on HeadRefForcePushedEvent {\n actor {\n login\n }\n createdAt\n }\n }\n }\n\n reviews(last: 100) {\n totalCount\n nodes {\n author {\n login\n }\n commit {\n oid\n }\n comments(last: 10) {\n nodes {\n author {\n login\n }\n createdAt\n }\n }\n authorAssociation\n state\n submittedAt\n url\n }\n }\n\n commits(last: 1) {\n totalCount\n nodes {\n commit {\n checkSuites(first: 100) {\n nodes {\n databaseId\n app {\n name\n }\n conclusion\n resourcePath\n status\n url\n checkRuns(last: 1) {\n nodes {\n title\n }\n }\n createdAt\n workflowRun {\n databaseId\n file {\n path\n }\n }\n }\n }\n status {\n state\n contexts {\n state\n description\n creator {\n login\n }\n targetUrl\n }\n }\n authoredDate\n committedDate\n pushedDate\n oid\n }\n }\n }\n\n comments(last: 100) {\n totalCount\n nodes {\n id\n author {\n login\n }\n authorAssociation\n databaseId\n body\n createdAt\n reactions(first: 100, content: THUMBS_UP) {\n nodes {\n user {\n login\n }\n }\n }\n }\n }\n\n files(first: 100) {\n totalCount\n nodes {\n path\n additions\n deletions\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n }\n\n projectItems(first: 10) {\n nodes {\n id\n project {\n id\n number\n }\n fieldValueByName(name: \"Status\") {\n ... on ProjectV2ItemFieldSingleSelectValue {\n name\n field {\n ... on ProjectV2SingleSelectField {\n id\n }\n }\n }\n }\n updatedAt\n }\n }\n }\n }\n }\n": types.PrDocument, + "\n query PR($prNumber: Int!) {\n repository(owner: \"DefinitelyTyped\", name: \"DefinitelyTyped\") {\n id\n pullRequest(number: $prNumber) {\n id\n title\n createdAt\n author {\n login\n }\n authorAssociation\n baseRef {\n name\n }\n labels(first: 100) {\n nodes {\n name\n }\n }\n isDraft\n mergeable\n number\n state\n headRefOid\n baseRefOid\n potentialMergeCommit {\n oid\n parents(first: 2) {\n totalCount\n nodes {\n oid\n }\n }\n }\n changedFiles\n additions\n deletions\n\n commitIds: commits(last: 100) {\n totalCount\n nodes {\n commit {\n oid\n parents(first: 3) {\n nodes {\n oid\n }\n }\n }\n }\n }\n\n timelineItems(\n last: 200\n itemTypes: [\n REOPENED_EVENT\n READY_FOR_REVIEW_EVENT\n MOVED_COLUMNS_IN_PROJECT_EVENT\n HEAD_REF_FORCE_PUSHED_EVENT\n ]\n ) {\n nodes {\n ... on ReopenedEvent {\n createdAt\n }\n ... on ReadyForReviewEvent {\n createdAt\n }\n ... on MovedColumnsInProjectEvent {\n actor {\n login\n }\n createdAt\n projectColumnName\n }\n ... on HeadRefForcePushedEvent {\n actor {\n login\n }\n createdAt\n }\n }\n }\n\n reviews(last: 100) {\n totalCount\n nodes {\n author {\n login\n }\n commit {\n oid\n }\n comments(last: 10) {\n nodes {\n author {\n login\n }\n createdAt\n }\n }\n authorAssociation\n state\n submittedAt\n url\n }\n }\n\n commits(last: 1) {\n totalCount\n nodes {\n commit {\n checkSuites(first: 100) {\n nodes {\n databaseId\n app {\n name\n }\n conclusion\n resourcePath\n status\n url\n checkRuns(last: 1) {\n nodes {\n title\n }\n }\n createdAt\n workflowRun {\n databaseId\n file {\n path\n }\n }\n }\n }\n status {\n state\n contexts {\n state\n description\n creator {\n login\n }\n targetUrl\n }\n }\n authoredDate\n committedDate\n pushedDate\n oid\n }\n }\n }\n\n comments(last: 100) {\n totalCount\n nodes {\n id\n author {\n login\n }\n authorAssociation\n databaseId\n body\n createdAt\n reactions(first: 100, content: THUMBS_UP) {\n nodes {\n user {\n login\n }\n }\n }\n }\n }\n\n files(first: 100) {\n totalCount\n nodes {\n path\n additions\n deletions\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n }\n\n projectItems(first: 10) {\n nodes {\n id\n project {\n id\n number\n }\n fieldValueByName(name: \"Status\") {\n ... on ProjectV2ItemFieldSingleSelectValue {\n name\n field {\n ... on ProjectV2SingleSelectField {\n id\n }\n }\n }\n }\n updatedAt\n }\n }\n }\n }\n }\n": types.PrDocument, "\n query PRFiles($prNumber: Int!, $endCursor: String) {\n repository(owner: \"DefinitelyTyped\", name: \"DefinitelyTyped\") {\n pullRequest(number: $prNumber) {\n files(first: 100, after: $endCursor) {\n totalCount\n nodes {\n path\n additions\n deletions\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n }\n }\n }\n }\n": types.PrFilesDocument, "\n query GetProjectBoardCards($cursor: String) {\n repository(owner: \"DefinitelyTyped\", name: \"DefinitelyTyped\") {\n projectV2(number: 1) {\n id\n items(first: 100, after: $cursor) {\n pageInfo {\n startCursor\n hasNextPage\n endCursor\n }\n totalCount\n nodes {\n id\n fieldValueByName(name: \"Status\") {\n ... on ProjectV2ItemFieldSingleSelectValue {\n name\n }\n }\n updatedAt\n }\n }\n }\n }\n }\n": types.GetProjectBoardCardsDocument, }; @@ -89,7 +89,7 @@ export function graphql(source: "\n query GetProjectColumns($cursor: String) {\ /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "\n query PR($prNumber: Int!) {\n repository(owner: \"DefinitelyTyped\", name: \"DefinitelyTyped\") {\n id\n pullRequest(number: $prNumber) {\n id\n title\n createdAt\n author {\n login\n }\n authorAssociation\n baseRef {\n name\n }\n labels(first: 100) {\n nodes {\n name\n }\n }\n isDraft\n mergeable\n number\n state\n headRefOid\n baseRefOid\n changedFiles\n additions\n deletions\n\n commitIds: commits(last: 100) {\n totalCount\n nodes {\n commit {\n oid\n parents(first: 3) {\n nodes {\n oid\n }\n }\n }\n }\n }\n\n timelineItems(\n last: 200\n itemTypes: [\n REOPENED_EVENT\n READY_FOR_REVIEW_EVENT\n MOVED_COLUMNS_IN_PROJECT_EVENT\n HEAD_REF_FORCE_PUSHED_EVENT\n ]\n ) {\n nodes {\n ... on ReopenedEvent {\n createdAt\n }\n ... on ReadyForReviewEvent {\n createdAt\n }\n ... on MovedColumnsInProjectEvent {\n actor {\n login\n }\n createdAt\n projectColumnName\n }\n ... on HeadRefForcePushedEvent {\n actor {\n login\n }\n createdAt\n }\n }\n }\n\n reviews(last: 100) {\n totalCount\n nodes {\n author {\n login\n }\n commit {\n oid\n }\n comments(last: 10) {\n nodes {\n author {\n login\n }\n createdAt\n }\n }\n authorAssociation\n state\n submittedAt\n url\n }\n }\n\n commits(last: 1) {\n totalCount\n nodes {\n commit {\n checkSuites(first: 100) {\n nodes {\n databaseId\n app {\n name\n }\n conclusion\n resourcePath\n status\n url\n checkRuns(last: 1) {\n nodes {\n title\n }\n }\n createdAt\n workflowRun {\n databaseId\n file {\n path\n }\n }\n }\n }\n status {\n state\n contexts {\n state\n description\n creator {\n login\n }\n targetUrl\n }\n }\n authoredDate\n committedDate\n pushedDate\n oid\n }\n }\n }\n\n comments(last: 100) {\n totalCount\n nodes {\n id\n author {\n login\n }\n authorAssociation\n databaseId\n body\n createdAt\n reactions(first: 100, content: THUMBS_UP) {\n nodes {\n user {\n login\n }\n }\n }\n }\n }\n\n files(first: 100) {\n totalCount\n nodes {\n path\n additions\n deletions\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n }\n\n projectItems(first: 10) {\n nodes {\n id\n project {\n id\n number\n }\n fieldValueByName(name: \"Status\") {\n ... on ProjectV2ItemFieldSingleSelectValue {\n name\n field {\n ... on ProjectV2SingleSelectField {\n id\n }\n }\n }\n }\n updatedAt\n }\n }\n }\n }\n }\n"): (typeof documents)["\n query PR($prNumber: Int!) {\n repository(owner: \"DefinitelyTyped\", name: \"DefinitelyTyped\") {\n id\n pullRequest(number: $prNumber) {\n id\n title\n createdAt\n author {\n login\n }\n authorAssociation\n baseRef {\n name\n }\n labels(first: 100) {\n nodes {\n name\n }\n }\n isDraft\n mergeable\n number\n state\n headRefOid\n baseRefOid\n changedFiles\n additions\n deletions\n\n commitIds: commits(last: 100) {\n totalCount\n nodes {\n commit {\n oid\n parents(first: 3) {\n nodes {\n oid\n }\n }\n }\n }\n }\n\n timelineItems(\n last: 200\n itemTypes: [\n REOPENED_EVENT\n READY_FOR_REVIEW_EVENT\n MOVED_COLUMNS_IN_PROJECT_EVENT\n HEAD_REF_FORCE_PUSHED_EVENT\n ]\n ) {\n nodes {\n ... on ReopenedEvent {\n createdAt\n }\n ... on ReadyForReviewEvent {\n createdAt\n }\n ... on MovedColumnsInProjectEvent {\n actor {\n login\n }\n createdAt\n projectColumnName\n }\n ... on HeadRefForcePushedEvent {\n actor {\n login\n }\n createdAt\n }\n }\n }\n\n reviews(last: 100) {\n totalCount\n nodes {\n author {\n login\n }\n commit {\n oid\n }\n comments(last: 10) {\n nodes {\n author {\n login\n }\n createdAt\n }\n }\n authorAssociation\n state\n submittedAt\n url\n }\n }\n\n commits(last: 1) {\n totalCount\n nodes {\n commit {\n checkSuites(first: 100) {\n nodes {\n databaseId\n app {\n name\n }\n conclusion\n resourcePath\n status\n url\n checkRuns(last: 1) {\n nodes {\n title\n }\n }\n createdAt\n workflowRun {\n databaseId\n file {\n path\n }\n }\n }\n }\n status {\n state\n contexts {\n state\n description\n creator {\n login\n }\n targetUrl\n }\n }\n authoredDate\n committedDate\n pushedDate\n oid\n }\n }\n }\n\n comments(last: 100) {\n totalCount\n nodes {\n id\n author {\n login\n }\n authorAssociation\n databaseId\n body\n createdAt\n reactions(first: 100, content: THUMBS_UP) {\n nodes {\n user {\n login\n }\n }\n }\n }\n }\n\n files(first: 100) {\n totalCount\n nodes {\n path\n additions\n deletions\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n }\n\n projectItems(first: 10) {\n nodes {\n id\n project {\n id\n number\n }\n fieldValueByName(name: \"Status\") {\n ... on ProjectV2ItemFieldSingleSelectValue {\n name\n field {\n ... on ProjectV2SingleSelectField {\n id\n }\n }\n }\n }\n updatedAt\n }\n }\n }\n }\n }\n"]; +export function graphql(source: "\n query PR($prNumber: Int!) {\n repository(owner: \"DefinitelyTyped\", name: \"DefinitelyTyped\") {\n id\n pullRequest(number: $prNumber) {\n id\n title\n createdAt\n author {\n login\n }\n authorAssociation\n baseRef {\n name\n }\n labels(first: 100) {\n nodes {\n name\n }\n }\n isDraft\n mergeable\n number\n state\n headRefOid\n baseRefOid\n potentialMergeCommit {\n oid\n parents(first: 2) {\n totalCount\n nodes {\n oid\n }\n }\n }\n changedFiles\n additions\n deletions\n\n commitIds: commits(last: 100) {\n totalCount\n nodes {\n commit {\n oid\n parents(first: 3) {\n nodes {\n oid\n }\n }\n }\n }\n }\n\n timelineItems(\n last: 200\n itemTypes: [\n REOPENED_EVENT\n READY_FOR_REVIEW_EVENT\n MOVED_COLUMNS_IN_PROJECT_EVENT\n HEAD_REF_FORCE_PUSHED_EVENT\n ]\n ) {\n nodes {\n ... on ReopenedEvent {\n createdAt\n }\n ... on ReadyForReviewEvent {\n createdAt\n }\n ... on MovedColumnsInProjectEvent {\n actor {\n login\n }\n createdAt\n projectColumnName\n }\n ... on HeadRefForcePushedEvent {\n actor {\n login\n }\n createdAt\n }\n }\n }\n\n reviews(last: 100) {\n totalCount\n nodes {\n author {\n login\n }\n commit {\n oid\n }\n comments(last: 10) {\n nodes {\n author {\n login\n }\n createdAt\n }\n }\n authorAssociation\n state\n submittedAt\n url\n }\n }\n\n commits(last: 1) {\n totalCount\n nodes {\n commit {\n checkSuites(first: 100) {\n nodes {\n databaseId\n app {\n name\n }\n conclusion\n resourcePath\n status\n url\n checkRuns(last: 1) {\n nodes {\n title\n }\n }\n createdAt\n workflowRun {\n databaseId\n file {\n path\n }\n }\n }\n }\n status {\n state\n contexts {\n state\n description\n creator {\n login\n }\n targetUrl\n }\n }\n authoredDate\n committedDate\n pushedDate\n oid\n }\n }\n }\n\n comments(last: 100) {\n totalCount\n nodes {\n id\n author {\n login\n }\n authorAssociation\n databaseId\n body\n createdAt\n reactions(first: 100, content: THUMBS_UP) {\n nodes {\n user {\n login\n }\n }\n }\n }\n }\n\n files(first: 100) {\n totalCount\n nodes {\n path\n additions\n deletions\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n }\n\n projectItems(first: 10) {\n nodes {\n id\n project {\n id\n number\n }\n fieldValueByName(name: \"Status\") {\n ... on ProjectV2ItemFieldSingleSelectValue {\n name\n field {\n ... on ProjectV2SingleSelectField {\n id\n }\n }\n }\n }\n updatedAt\n }\n }\n }\n }\n }\n"): (typeof documents)["\n query PR($prNumber: Int!) {\n repository(owner: \"DefinitelyTyped\", name: \"DefinitelyTyped\") {\n id\n pullRequest(number: $prNumber) {\n id\n title\n createdAt\n author {\n login\n }\n authorAssociation\n baseRef {\n name\n }\n labels(first: 100) {\n nodes {\n name\n }\n }\n isDraft\n mergeable\n number\n state\n headRefOid\n baseRefOid\n potentialMergeCommit {\n oid\n parents(first: 2) {\n totalCount\n nodes {\n oid\n }\n }\n }\n changedFiles\n additions\n deletions\n\n commitIds: commits(last: 100) {\n totalCount\n nodes {\n commit {\n oid\n parents(first: 3) {\n nodes {\n oid\n }\n }\n }\n }\n }\n\n timelineItems(\n last: 200\n itemTypes: [\n REOPENED_EVENT\n READY_FOR_REVIEW_EVENT\n MOVED_COLUMNS_IN_PROJECT_EVENT\n HEAD_REF_FORCE_PUSHED_EVENT\n ]\n ) {\n nodes {\n ... on ReopenedEvent {\n createdAt\n }\n ... on ReadyForReviewEvent {\n createdAt\n }\n ... on MovedColumnsInProjectEvent {\n actor {\n login\n }\n createdAt\n projectColumnName\n }\n ... on HeadRefForcePushedEvent {\n actor {\n login\n }\n createdAt\n }\n }\n }\n\n reviews(last: 100) {\n totalCount\n nodes {\n author {\n login\n }\n commit {\n oid\n }\n comments(last: 10) {\n nodes {\n author {\n login\n }\n createdAt\n }\n }\n authorAssociation\n state\n submittedAt\n url\n }\n }\n\n commits(last: 1) {\n totalCount\n nodes {\n commit {\n checkSuites(first: 100) {\n nodes {\n databaseId\n app {\n name\n }\n conclusion\n resourcePath\n status\n url\n checkRuns(last: 1) {\n nodes {\n title\n }\n }\n createdAt\n workflowRun {\n databaseId\n file {\n path\n }\n }\n }\n }\n status {\n state\n contexts {\n state\n description\n creator {\n login\n }\n targetUrl\n }\n }\n authoredDate\n committedDate\n pushedDate\n oid\n }\n }\n }\n\n comments(last: 100) {\n totalCount\n nodes {\n id\n author {\n login\n }\n authorAssociation\n databaseId\n body\n createdAt\n reactions(first: 100, content: THUMBS_UP) {\n nodes {\n user {\n login\n }\n }\n }\n }\n }\n\n files(first: 100) {\n totalCount\n nodes {\n path\n additions\n deletions\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n }\n\n projectItems(first: 10) {\n nodes {\n id\n project {\n id\n number\n }\n fieldValueByName(name: \"Status\") {\n ... on ProjectV2ItemFieldSingleSelectValue {\n name\n field {\n ... on ProjectV2SingleSelectField {\n id\n }\n }\n }\n }\n updatedAt\n }\n }\n }\n }\n }\n"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ diff --git a/packages/mergebot/src/queries/schema/graphql.ts b/packages/mergebot/src/queries/schema/graphql.ts index 35bda0e800..a63c90e225 100644 --- a/packages/mergebot/src/queries/schema/graphql.ts +++ b/packages/mergebot/src/queries/schema/graphql.ts @@ -8351,7 +8351,7 @@ export type PrQuery = { __typename?: 'Query', repository?: { __typename?: 'Repos | { __typename?: 'Mannequin', login: string } | { __typename?: 'Organization', login: string } | { __typename?: 'User', login: string } - | null, baseRef?: { __typename?: 'Ref', name: string } | null, labels?: { __typename?: 'LabelConnection', nodes?: Array<{ __typename?: 'Label', name: string } | null> | null } | null, commitIds: { __typename?: 'PullRequestCommitConnection', totalCount: number, nodes?: Array<{ __typename?: 'PullRequestCommit', commit: { __typename?: 'Commit', oid: string, parents: { __typename?: 'CommitConnection', nodes?: Array<{ __typename?: 'Commit', oid: string } | null> | null } } } | null> | null }, timelineItems: { __typename?: 'PullRequestTimelineItemsConnection', nodes?: Array< + | null, baseRef?: { __typename?: 'Ref', name: string } | null, labels?: { __typename?: 'LabelConnection', nodes?: Array<{ __typename?: 'Label', name: string } | null> | null } | null, potentialMergeCommit?: { __typename?: 'Commit', oid: string, parents: { __typename?: 'CommitConnection', totalCount: number, nodes?: Array<{ __typename?: 'Commit', oid: string } | null> | null } } | null, commitIds: { __typename?: 'PullRequestCommitConnection', totalCount: number, nodes?: Array<{ __typename?: 'PullRequestCommit', commit: { __typename?: 'Commit', oid: string, parents: { __typename?: 'CommitConnection', nodes?: Array<{ __typename?: 'Commit', oid: string } | null> | null } } } | null> | null }, timelineItems: { __typename?: 'PullRequestTimelineItemsConnection', nodes?: Array< | { __typename?: 'AddedToMergeQueueEvent' } | { __typename?: 'AddedToProjectEvent' } | { __typename?: 'AssignedEvent' } @@ -8504,6 +8504,6 @@ export const GetDiscussionCommentsDocument = {"kind":"Document","definitions":[{ export const GetFileContentDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetFileContent"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"owner"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"name"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"expr"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"repository"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"owner"},"value":{"kind":"Variable","name":{"kind":"Name","value":"owner"}}},{"kind":"Argument","name":{"kind":"Name","value":"name"},"value":{"kind":"Variable","name":{"kind":"Name","value":"name"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"object"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"expression"},"value":{"kind":"Variable","name":{"kind":"Name","value":"expr"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Blob"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"text"}},{"kind":"Field","name":{"kind":"Name","value":"byteSize"}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const GetLabelsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetLabels"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"endCursor"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"repository"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"name"},"value":{"kind":"StringValue","value":"DefinitelyTyped","block":false}},{"kind":"Argument","name":{"kind":"Name","value":"owner"},"value":{"kind":"StringValue","value":"DefinitelyTyped","block":false}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"labels"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"100"}},{"kind":"Argument","name":{"kind":"Name","value":"after"},"value":{"kind":"Variable","name":{"kind":"Name","value":"endCursor"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"pageInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hasNextPage"}},{"kind":"Field","name":{"kind":"Name","value":"endCursor"}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const GetProjectColumnsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetProjectColumns"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"cursor"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"repository"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"name"},"value":{"kind":"StringValue","value":"DefinitelyTyped","block":false}},{"kind":"Argument","name":{"kind":"Name","value":"owner"},"value":{"kind":"StringValue","value":"DefinitelyTyped","block":false}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"projectV2"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"number"},"value":{"kind":"IntValue","value":"1"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"fields"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"100"}},{"kind":"Argument","name":{"kind":"Name","value":"after"},"value":{"kind":"Variable","name":{"kind":"Name","value":"cursor"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"pageInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"startCursor"}},{"kind":"Field","name":{"kind":"Name","value":"hasNextPage"}},{"kind":"Field","name":{"kind":"Name","value":"endCursor"}}]}},{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ProjectV2SingleSelectField"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"options"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode; -export const PrDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"PR"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"prNumber"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"repository"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"owner"},"value":{"kind":"StringValue","value":"DefinitelyTyped","block":false}},{"kind":"Argument","name":{"kind":"Name","value":"name"},"value":{"kind":"StringValue","value":"DefinitelyTyped","block":false}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"pullRequest"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"number"},"value":{"kind":"Variable","name":{"kind":"Name","value":"prNumber"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"author"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"login"}}]}},{"kind":"Field","name":{"kind":"Name","value":"authorAssociation"}},{"kind":"Field","name":{"kind":"Name","value":"baseRef"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"labels"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"100"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"isDraft"}},{"kind":"Field","name":{"kind":"Name","value":"mergeable"}},{"kind":"Field","name":{"kind":"Name","value":"number"}},{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"headRefOid"}},{"kind":"Field","name":{"kind":"Name","value":"baseRefOid"}},{"kind":"Field","name":{"kind":"Name","value":"changedFiles"}},{"kind":"Field","name":{"kind":"Name","value":"additions"}},{"kind":"Field","name":{"kind":"Name","value":"deletions"}},{"kind":"Field","alias":{"kind":"Name","value":"commitIds"},"name":{"kind":"Name","value":"commits"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"last"},"value":{"kind":"IntValue","value":"100"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"commit"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"oid"}},{"kind":"Field","name":{"kind":"Name","value":"parents"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"3"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"oid"}}]}}]}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"timelineItems"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"last"},"value":{"kind":"IntValue","value":"200"}},{"kind":"Argument","name":{"kind":"Name","value":"itemTypes"},"value":{"kind":"ListValue","values":[{"kind":"EnumValue","value":"REOPENED_EVENT"},{"kind":"EnumValue","value":"READY_FOR_REVIEW_EVENT"},{"kind":"EnumValue","value":"MOVED_COLUMNS_IN_PROJECT_EVENT"},{"kind":"EnumValue","value":"HEAD_REF_FORCE_PUSHED_EVENT"}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ReopenedEvent"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ReadyForReviewEvent"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"MovedColumnsInProjectEvent"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"actor"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"login"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"projectColumnName"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"HeadRefForcePushedEvent"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"actor"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"login"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"reviews"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"last"},"value":{"kind":"IntValue","value":"100"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"author"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"login"}}]}},{"kind":"Field","name":{"kind":"Name","value":"commit"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"oid"}}]}},{"kind":"Field","name":{"kind":"Name","value":"comments"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"last"},"value":{"kind":"IntValue","value":"10"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"author"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"login"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"authorAssociation"}},{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"submittedAt"}},{"kind":"Field","name":{"kind":"Name","value":"url"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"commits"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"last"},"value":{"kind":"IntValue","value":"1"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"commit"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"checkSuites"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"100"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"databaseId"}},{"kind":"Field","name":{"kind":"Name","value":"app"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"conclusion"}},{"kind":"Field","name":{"kind":"Name","value":"resourcePath"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"checkRuns"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"last"},"value":{"kind":"IntValue","value":"1"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"workflowRun"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"databaseId"}},{"kind":"Field","name":{"kind":"Name","value":"file"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"path"}}]}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"status"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"contexts"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"creator"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"login"}}]}},{"kind":"Field","name":{"kind":"Name","value":"targetUrl"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"authoredDate"}},{"kind":"Field","name":{"kind":"Name","value":"committedDate"}},{"kind":"Field","name":{"kind":"Name","value":"pushedDate"}},{"kind":"Field","name":{"kind":"Name","value":"oid"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"comments"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"last"},"value":{"kind":"IntValue","value":"100"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"author"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"login"}}]}},{"kind":"Field","name":{"kind":"Name","value":"authorAssociation"}},{"kind":"Field","name":{"kind":"Name","value":"databaseId"}},{"kind":"Field","name":{"kind":"Name","value":"body"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"reactions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"100"}},{"kind":"Argument","name":{"kind":"Name","value":"content"},"value":{"kind":"EnumValue","value":"THUMBS_UP"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"login"}}]}}]}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"files"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"100"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"path"}},{"kind":"Field","name":{"kind":"Name","value":"additions"}},{"kind":"Field","name":{"kind":"Name","value":"deletions"}}]}},{"kind":"Field","name":{"kind":"Name","value":"pageInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hasNextPage"}},{"kind":"Field","name":{"kind":"Name","value":"endCursor"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"projectItems"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"10"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"project"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"number"}}]}},{"kind":"Field","name":{"kind":"Name","value":"fieldValueByName"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"name"},"value":{"kind":"StringValue","value":"Status","block":false}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ProjectV2ItemFieldSingleSelectValue"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"field"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ProjectV2SingleSelectField"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode; +export const PrDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"PR"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"prNumber"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"repository"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"owner"},"value":{"kind":"StringValue","value":"DefinitelyTyped","block":false}},{"kind":"Argument","name":{"kind":"Name","value":"name"},"value":{"kind":"StringValue","value":"DefinitelyTyped","block":false}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"pullRequest"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"number"},"value":{"kind":"Variable","name":{"kind":"Name","value":"prNumber"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"author"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"login"}}]}},{"kind":"Field","name":{"kind":"Name","value":"authorAssociation"}},{"kind":"Field","name":{"kind":"Name","value":"baseRef"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"labels"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"100"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"isDraft"}},{"kind":"Field","name":{"kind":"Name","value":"mergeable"}},{"kind":"Field","name":{"kind":"Name","value":"number"}},{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"headRefOid"}},{"kind":"Field","name":{"kind":"Name","value":"baseRefOid"}},{"kind":"Field","name":{"kind":"Name","value":"potentialMergeCommit"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"oid"}},{"kind":"Field","name":{"kind":"Name","value":"parents"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"2"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"oid"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"changedFiles"}},{"kind":"Field","name":{"kind":"Name","value":"additions"}},{"kind":"Field","name":{"kind":"Name","value":"deletions"}},{"kind":"Field","alias":{"kind":"Name","value":"commitIds"},"name":{"kind":"Name","value":"commits"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"last"},"value":{"kind":"IntValue","value":"100"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"commit"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"oid"}},{"kind":"Field","name":{"kind":"Name","value":"parents"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"3"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"oid"}}]}}]}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"timelineItems"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"last"},"value":{"kind":"IntValue","value":"200"}},{"kind":"Argument","name":{"kind":"Name","value":"itemTypes"},"value":{"kind":"ListValue","values":[{"kind":"EnumValue","value":"REOPENED_EVENT"},{"kind":"EnumValue","value":"READY_FOR_REVIEW_EVENT"},{"kind":"EnumValue","value":"MOVED_COLUMNS_IN_PROJECT_EVENT"},{"kind":"EnumValue","value":"HEAD_REF_FORCE_PUSHED_EVENT"}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ReopenedEvent"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ReadyForReviewEvent"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"MovedColumnsInProjectEvent"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"actor"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"login"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"projectColumnName"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"HeadRefForcePushedEvent"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"actor"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"login"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"reviews"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"last"},"value":{"kind":"IntValue","value":"100"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"author"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"login"}}]}},{"kind":"Field","name":{"kind":"Name","value":"commit"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"oid"}}]}},{"kind":"Field","name":{"kind":"Name","value":"comments"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"last"},"value":{"kind":"IntValue","value":"10"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"author"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"login"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"authorAssociation"}},{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"submittedAt"}},{"kind":"Field","name":{"kind":"Name","value":"url"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"commits"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"last"},"value":{"kind":"IntValue","value":"1"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"commit"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"checkSuites"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"100"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"databaseId"}},{"kind":"Field","name":{"kind":"Name","value":"app"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"conclusion"}},{"kind":"Field","name":{"kind":"Name","value":"resourcePath"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"checkRuns"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"last"},"value":{"kind":"IntValue","value":"1"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"workflowRun"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"databaseId"}},{"kind":"Field","name":{"kind":"Name","value":"file"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"path"}}]}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"status"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"contexts"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"creator"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"login"}}]}},{"kind":"Field","name":{"kind":"Name","value":"targetUrl"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"authoredDate"}},{"kind":"Field","name":{"kind":"Name","value":"committedDate"}},{"kind":"Field","name":{"kind":"Name","value":"pushedDate"}},{"kind":"Field","name":{"kind":"Name","value":"oid"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"comments"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"last"},"value":{"kind":"IntValue","value":"100"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"author"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"login"}}]}},{"kind":"Field","name":{"kind":"Name","value":"authorAssociation"}},{"kind":"Field","name":{"kind":"Name","value":"databaseId"}},{"kind":"Field","name":{"kind":"Name","value":"body"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"reactions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"100"}},{"kind":"Argument","name":{"kind":"Name","value":"content"},"value":{"kind":"EnumValue","value":"THUMBS_UP"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"login"}}]}}]}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"files"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"100"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"path"}},{"kind":"Field","name":{"kind":"Name","value":"additions"}},{"kind":"Field","name":{"kind":"Name","value":"deletions"}}]}},{"kind":"Field","name":{"kind":"Name","value":"pageInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hasNextPage"}},{"kind":"Field","name":{"kind":"Name","value":"endCursor"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"projectItems"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"10"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"project"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"number"}}]}},{"kind":"Field","name":{"kind":"Name","value":"fieldValueByName"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"name"},"value":{"kind":"StringValue","value":"Status","block":false}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ProjectV2ItemFieldSingleSelectValue"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"field"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ProjectV2SingleSelectField"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const PrFilesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"PRFiles"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"prNumber"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"endCursor"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"repository"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"owner"},"value":{"kind":"StringValue","value":"DefinitelyTyped","block":false}},{"kind":"Argument","name":{"kind":"Name","value":"name"},"value":{"kind":"StringValue","value":"DefinitelyTyped","block":false}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"pullRequest"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"number"},"value":{"kind":"Variable","name":{"kind":"Name","value":"prNumber"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"files"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"100"}},{"kind":"Argument","name":{"kind":"Name","value":"after"},"value":{"kind":"Variable","name":{"kind":"Name","value":"endCursor"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"path"}},{"kind":"Field","name":{"kind":"Name","value":"additions"}},{"kind":"Field","name":{"kind":"Name","value":"deletions"}}]}},{"kind":"Field","name":{"kind":"Name","value":"pageInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hasNextPage"}},{"kind":"Field","name":{"kind":"Name","value":"endCursor"}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const GetProjectBoardCardsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetProjectBoardCards"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"cursor"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"repository"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"owner"},"value":{"kind":"StringValue","value":"DefinitelyTyped","block":false}},{"kind":"Argument","name":{"kind":"Name","value":"name"},"value":{"kind":"StringValue","value":"DefinitelyTyped","block":false}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"projectV2"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"number"},"value":{"kind":"IntValue","value":"1"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"100"}},{"kind":"Argument","name":{"kind":"Name","value":"after"},"value":{"kind":"Variable","name":{"kind":"Name","value":"cursor"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"pageInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"startCursor"}},{"kind":"Field","name":{"kind":"Name","value":"hasNextPage"}},{"kind":"Field","name":{"kind":"Name","value":"endCursor"}}]}},{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"fieldValueByName"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"name"},"value":{"kind":"StringValue","value":"Status","block":false}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ProjectV2ItemFieldSingleSelectValue"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode; \ No newline at end of file From 1c6a2d0ac78622bf5aeed8e6cb0ffa05352d9652 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Tue, 1 Sep 2026 12:05:04 -0700 Subject: [PATCH 3/3] Preserve handling for conflicting pull requests Use the trusted base tree as placeholder package state when GitHub cannot create a test merge commit due to conflicts. The conflict still blocks merging until the author resolves it. --- packages/mergebot/src/_tests/fixturedActions.test.ts | 2 +- packages/mergebot/src/pr-info.ts | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/mergebot/src/_tests/fixturedActions.test.ts b/packages/mergebot/src/_tests/fixturedActions.test.ts index 9e66ddaace..ccd2e64293 100644 --- a/packages/mergebot/src/_tests/fixturedActions.test.ts +++ b/packages/mergebot/src/_tests/fixturedActions.test.ts @@ -37,7 +37,7 @@ async function testFixture(dir: string) { // Fixtures recorded before potentialMergeCommit was queried use the head tree. const prInfoWithMergeCommit = - prInfo.potentialMergeCommit === undefined + prInfo.potentialMergeCommit === undefined && prInfo.mergeable !== "CONFLICTING" ? { ...prInfo, potentialMergeCommit: { diff --git a/packages/mergebot/src/pr-info.ts b/packages/mergebot/src/pr-info.ts index f2adc7323a..454394853a 100644 --- a/packages/mergebot/src/pr-info.ts +++ b/packages/mergebot/src/pr-info.ts @@ -240,7 +240,9 @@ export async function deriveStateForPR( // Always compare against the actual base branch tip, never against an OID derived from PR // commit ancestry (which an attacker can influence by pushing >100 commits). const baseId = getTrustedBase(prInfo); - const mergeId = getTrustedMergeCommit(prInfo); + // GitHub cannot generate a test merge commit for a conflicting PR. Use the trusted base + // tree as a placeholder until the author resolves the conflict and merging becomes possible. + const mergeId = prInfo.mergeable === "CONFLICTING" ? baseId : getTrustedMergeCommit(prInfo); if (mergeId instanceof Error) return botError(mergeId.message); // commitIds is `commits(last: 100)`; if there are more commits than that, we cannot reason // safely about the PR's history and force a maintainer review downstream.