From 74c92e9667f7183362ea56e39f874e4d1fde8bc2 Mon Sep 17 00:00:00 2001 From: Seprintour Date: Wed, 26 Apr 2023 22:08:46 +0100 Subject: [PATCH 1/6] feat: add pull request to processor --- src/handlers/assign/auto.ts | 27 +++++++++++++++++++++++++++ src/handlers/processors.ts | 3 ++- 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 src/handlers/assign/auto.ts diff --git a/src/handlers/assign/auto.ts b/src/handlers/assign/auto.ts new file mode 100644 index 000000000..9a58bc045 --- /dev/null +++ b/src/handlers/assign/auto.ts @@ -0,0 +1,27 @@ +import { getBotContext, getLogger } from "../../bindings"; +import { Payload } from "../../types"; + +// Use `context.octokit.rest` to get the pull requests for the repository +export const getPullRequests = async () => { + const context = getBotContext(); + const logger = getLogger(); + const payload = context.payload as Payload; + try { + const { data: pulls } = await context.octokit.rest.pulls.list({ + owner: payload.repository.owner.login, + repo: payload.repository.name, + state: "open", + }); + return pulls; + } catch (e: unknown) { + logger.debug(`Fetching pull requests failed!, reason: ${e}`); + return {}; + } +}; + +// Check for pull requests linked to their respective issues but not assigned to them +export const checkPullRequests = async () => { + const pulls = await getPullRequests(); + + console.log(pulls); +}; diff --git a/src/handlers/processors.ts b/src/handlers/processors.ts index 5021ab54e..96accdb97 100644 --- a/src/handlers/processors.ts +++ b/src/handlers/processors.ts @@ -5,6 +5,7 @@ import { checkBountiesToUnassign, collectAnalytics, checkWeeklyUpdate } from "./ import { nullHandler } from "./shared"; import { handleComment } from "./comment"; import { handleIssueClosed } from "./payout"; +import { checkPullRequests } from "./assign/auto"; export const processors: Record = { [GithubEvent.ISSUES_LABELED]: { @@ -42,4 +43,4 @@ export const processors: Record = { /** * @dev The handlers which will run on every event hooked */ -export const wildcardProcessors: ActionHandler[] = [checkBountiesToUnassign, collectAnalytics, checkWeeklyUpdate]; +export const wildcardProcessors: ActionHandler[] = [checkBountiesToUnassign, collectAnalytics, checkWeeklyUpdate, checkPullRequests]; From d996c043c1de23868bcfb86e5aa90cbfa16046f8 Mon Sep 17 00:00:00 2001 From: Seprintour Date: Wed, 26 Apr 2023 22:59:16 +0100 Subject: [PATCH 2/6] fix: add pull requests to watch and processor --- app.yml | 26 +++++++++++++------------- src/handlers/processors.ts | 7 ++++++- src/types/payload.ts | 3 +++ 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/app.yml b/app.yml index 557e6c2cd..1ac1d99dd 100644 --- a/app.yml +++ b/app.yml @@ -24,18 +24,18 @@ default_events: # - gollum - issue_comment - issues -# - label -# - milestone -# - member -# - membership -# - org_block -# - organization -# - page_build -# - project -# - project_card -# - project_column -# - public -# - pull_request + # - label + # - milestone + # - member + # - membership + # - org_block + # - organization + # - page_build + # - project + # - project_card + # - project_column + # - public + - pull_request # - pull_request_review # - pull_request_review_comment # - push @@ -82,7 +82,7 @@ default_permissions: # Pull requests and related comments, assignees, labels, milestones, and merges. # https://developer.github.com/v3/apps/permissions/#permission-on-pull-requests - # pull_requests: read + pull_requests: read # Manage the post-receive hooks for a repository. # https://developer.github.com/v3/apps/permissions/#permission-on-repository-hooks diff --git a/src/handlers/processors.ts b/src/handlers/processors.ts index 96accdb97..151e28ceb 100644 --- a/src/handlers/processors.ts +++ b/src/handlers/processors.ts @@ -38,9 +38,14 @@ export const processors: Record = { action: [handleIssueClosed], post: [nullHandler], }, + [GithubEvent.PULL_REQUEST_OPENED]: { + pre: [nullHandler], + action: [checkPullRequests], + post: [nullHandler], + }, }; /** * @dev The handlers which will run on every event hooked */ -export const wildcardProcessors: ActionHandler[] = [checkBountiesToUnassign, collectAnalytics, checkWeeklyUpdate, checkPullRequests]; +export const wildcardProcessors: ActionHandler[] = [checkBountiesToUnassign, collectAnalytics, checkWeeklyUpdate]; diff --git a/src/types/payload.ts b/src/types/payload.ts index dea72faf0..9418c29ac 100644 --- a/src/types/payload.ts +++ b/src/types/payload.ts @@ -13,6 +13,9 @@ export enum GithubEvent { // issue_comment ISSUE_COMMENT_CREATED = "issue_comment.created", ISSUE_COMMENT_EDITED = "issue_comment.edited", + + // pull_request + PULL_REQUEST_OPENED = "pull_request.opened", } export enum UserType { From b1cb405db69c77446e94671a6277aaa9387347e1 Mon Sep 17 00:00:00 2001 From: Seprintour Date: Thu, 27 Apr 2023 09:29:06 +0100 Subject: [PATCH 3/6] feat: loop pr and find issue (wrong issue linked by api) --- src/handlers/assign/auto.ts | 53 ++++++++++++++++++++++++++++++++----- 1 file changed, 47 insertions(+), 6 deletions(-) diff --git a/src/handlers/assign/auto.ts b/src/handlers/assign/auto.ts index 9a58bc045..f9bd70c4a 100644 --- a/src/handlers/assign/auto.ts +++ b/src/handlers/assign/auto.ts @@ -1,9 +1,10 @@ +import { Context } from "probot"; import { getBotContext, getLogger } from "../../bindings"; -import { Payload } from "../../types"; +import { addAssignees, listIssuesForRepo } from "../../helpers"; +import { IssueType, Payload } from "../../types"; // Use `context.octokit.rest` to get the pull requests for the repository -export const getPullRequests = async () => { - const context = getBotContext(); +export const getPullRequests = async (context: Context) => { const logger = getLogger(); const payload = context.payload as Payload; try { @@ -15,13 +16,53 @@ export const getPullRequests = async () => { return pulls; } catch (e: unknown) { logger.debug(`Fetching pull requests failed!, reason: ${e}`); - return {}; + return []; } }; // Check for pull requests linked to their respective issues but not assigned to them export const checkPullRequests = async () => { - const pulls = await getPullRequests(); + const context = getBotContext(); + const logger = getLogger(); + const pulls = await getPullRequests(context); + + if (pulls.length === 0) { + logger.debug(`No pull requests found at this time`); + return; + } + + const payload = context.payload as Payload; + + // get issues for repo + //const issues = await listIssuesForRepo(IssueType.OPEN); - console.log(pulls); + // Loop through the pull requests and assign them to their respective issues if needed + for (const pull of pulls) { + // Check if the pull request is linked to an issue + const issueUrl = pull.issue_url; + const linkedIssueNumber = issueUrl.substring(issueUrl.lastIndexOf("/") + 1); + if (!linkedIssueNumber) { + continue; + } + + // Check if the pull request opener is assigned to the issue + const opener = pull!.user!.login; + const { data: issue } = await context.octokit.rest.issues.get({ + owner: payload.repository.owner.login, + repo: payload.repository.name, + issue_number: +linkedIssueNumber, + }); + + // if issue is already assigned, continue + if (issue!.assignees!.length > 0) { + continue; + } + + const assignedUsernames = issue!.assignees!.map((assignee) => assignee.login); + if (!assignedUsernames.includes(opener)) { + await addAssignees(+linkedIssueNumber, [opener]); + logger.debug(`Assigned pull request #${pull.number} opener to issue ${linkedIssueNumber}.`); + console.log(`Assigned pull request #${pull.number} opener to issue ${linkedIssueNumber}.`); + } + } }; From de0b8203f58c57d3246fbe2080f0c218d5c0f837 Mon Sep 17 00:00:00 2001 From: Seprintour Date: Thu, 27 Apr 2023 10:40:18 +0100 Subject: [PATCH 4/6] feat: auto-link pr to issue done --- src/handlers/assign/auto.ts | 19 ++++++++++++------- src/helpers/parser.ts | 18 ++++++++++++++++++ 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/src/handlers/assign/auto.ts b/src/handlers/assign/auto.ts index f9bd70c4a..fe3798fc1 100644 --- a/src/handlers/assign/auto.ts +++ b/src/handlers/assign/auto.ts @@ -1,6 +1,7 @@ import { Context } from "probot"; import { getBotContext, getLogger } from "../../bindings"; import { addAssignees, listIssuesForRepo } from "../../helpers"; +import { gitLinkedIssueParser } from "../../helpers/parser"; import { IssueType, Payload } from "../../types"; // Use `context.octokit.rest` to get the pull requests for the repository @@ -33,18 +34,21 @@ export const checkPullRequests = async () => { const payload = context.payload as Payload; - // get issues for repo - //const issues = await listIssuesForRepo(IssueType.OPEN); - // Loop through the pull requests and assign them to their respective issues if needed for (const pull of pulls) { - // Check if the pull request is linked to an issue - const issueUrl = pull.issue_url; - const linkedIssueNumber = issueUrl.substring(issueUrl.lastIndexOf("/") + 1); - if (!linkedIssueNumber) { + let pullRequestLinked = await gitLinkedIssueParser({ + owner: payload.repository.owner.login, + repo: payload.repository.name, + issue_number: pull.number, + }); + + // if pullRequestLinked is empty, continue + if (pullRequestLinked == "") { continue; } + const linkedIssueNumber = pullRequestLinked.substring(pullRequestLinked.lastIndexOf("/") + 1); + // Check if the pull request opener is assigned to the issue const opener = pull!.user!.login; const { data: issue } = await context.octokit.rest.issues.get({ @@ -55,6 +59,7 @@ export const checkPullRequests = async () => { // if issue is already assigned, continue if (issue!.assignees!.length > 0) { + logger.debug(`Issue already assigned, ignoring...`); continue; } diff --git a/src/helpers/parser.ts b/src/helpers/parser.ts index 517c7cbe9..b05411fee 100644 --- a/src/helpers/parser.ts +++ b/src/helpers/parser.ts @@ -24,3 +24,21 @@ export const gitIssueParser = async ({ owner, repo, issue_number }: GitParser): return true; } }; + +export const gitLinkedIssueParser = async ({ owner, repo, issue_number }: GitParser): Promise => { + try { + const { data } = await axios.get(`https://github.com/${owner}/${repo}/pull/${issue_number}`); + const dom = parse(data); + const devForm = dom.querySelector("[data-target='create-branch.developmentForm']") as HTMLElement; + const linkedPRs = devForm.querySelectorAll(".my-1"); + + if (linkedPRs.length === 0) { + return ""; + } + + const prUrl = linkedPRs[0].querySelector("a")?.attrs?.href || ""; + return prUrl; + } catch (error) { + return ""; + } +}; From 3c14120fa62820d08c1102c878f897149a185e44 Mon Sep 17 00:00:00 2001 From: Seprintour Date: Thu, 27 Apr 2023 10:43:57 +0100 Subject: [PATCH 5/6] fix: remove unused --- src/handlers/assign/auto.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/handlers/assign/auto.ts b/src/handlers/assign/auto.ts index fe3798fc1..c58f5214d 100644 --- a/src/handlers/assign/auto.ts +++ b/src/handlers/assign/auto.ts @@ -1,8 +1,8 @@ import { Context } from "probot"; import { getBotContext, getLogger } from "../../bindings"; -import { addAssignees, listIssuesForRepo } from "../../helpers"; +import { addAssignees } from "../../helpers"; import { gitLinkedIssueParser } from "../../helpers/parser"; -import { IssueType, Payload } from "../../types"; +import { Payload } from "../../types"; // Use `context.octokit.rest` to get the pull requests for the repository export const getPullRequests = async (context: Context) => { From 8de67730dd8aead50b5d1ced2e0c4b5c5186f387 Mon Sep 17 00:00:00 2001 From: Seprintour Date: Fri, 28 Apr 2023 07:39:22 +0100 Subject: [PATCH 6/6] fix: move helpers to helper file --- src/handlers/assign/auto.ts | 28 +++------------------------- src/helpers/issue.ts | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 25 deletions(-) diff --git a/src/handlers/assign/auto.ts b/src/handlers/assign/auto.ts index c58f5214d..d46a46aa1 100644 --- a/src/handlers/assign/auto.ts +++ b/src/handlers/assign/auto.ts @@ -1,26 +1,8 @@ -import { Context } from "probot"; import { getBotContext, getLogger } from "../../bindings"; -import { addAssignees } from "../../helpers"; +import { addAssignees, getIssueByNumber, getPullRequests } from "../../helpers"; import { gitLinkedIssueParser } from "../../helpers/parser"; import { Payload } from "../../types"; -// Use `context.octokit.rest` to get the pull requests for the repository -export const getPullRequests = async (context: Context) => { - const logger = getLogger(); - const payload = context.payload as Payload; - try { - const { data: pulls } = await context.octokit.rest.pulls.list({ - owner: payload.repository.owner.login, - repo: payload.repository.name, - state: "open", - }); - return pulls; - } catch (e: unknown) { - logger.debug(`Fetching pull requests failed!, reason: ${e}`); - return []; - } -}; - // Check for pull requests linked to their respective issues but not assigned to them export const checkPullRequests = async () => { const context = getBotContext(); @@ -51,11 +33,8 @@ export const checkPullRequests = async () => { // Check if the pull request opener is assigned to the issue const opener = pull!.user!.login; - const { data: issue } = await context.octokit.rest.issues.get({ - owner: payload.repository.owner.login, - repo: payload.repository.name, - issue_number: +linkedIssueNumber, - }); + + let issue = await getIssueByNumber(context, +linkedIssueNumber); // if issue is already assigned, continue if (issue!.assignees!.length > 0) { @@ -67,7 +46,6 @@ export const checkPullRequests = async () => { if (!assignedUsernames.includes(opener)) { await addAssignees(+linkedIssueNumber, [opener]); logger.debug(`Assigned pull request #${pull.number} opener to issue ${linkedIssueNumber}.`); - console.log(`Assigned pull request #${pull.number} opener to issue ${linkedIssueNumber}.`); } } }; diff --git a/src/helpers/issue.ts b/src/helpers/issue.ts index 74f16b17f..d60df9ee7 100644 --- a/src/helpers/issue.ts +++ b/src/helpers/issue.ts @@ -1,3 +1,4 @@ +import { Context } from "probot"; import { getBotContext, getLogger } from "../bindings"; import { Comment, Payload } from "../types"; import { checkRateLimitGit } from "../utils"; @@ -187,3 +188,37 @@ export const deleteLabel = async (label: string): Promise => { logger.debug(`Label deletion failed!, reason: ${e}`); } }; + +// Use `context.octokit.rest` to get the pull requests for the repository +export const getPullRequests = async (context: Context, state: "open" | "closed" | "all" = "open") => { + const logger = getLogger(); + const payload = context.payload as Payload; + try { + const { data: pulls } = await context.octokit.rest.pulls.list({ + owner: payload.repository.owner.login, + repo: payload.repository.name, + state, + }); + return pulls; + } catch (e: unknown) { + logger.debug(`Fetching pull requests failed!, reason: ${e}`); + return []; + } +}; + +// Get issues by issue number +export const getIssueByNumber = async (context: Context, issue_number: number) => { + const logger = getLogger(); + const payload = context.payload as Payload; + try { + const { data: issue } = await context.octokit.rest.issues.get({ + owner: payload.repository.owner.login, + repo: payload.repository.name, + issue_number, + }); + return issue; + } catch (e: unknown) { + logger.debug(`Fetching issue failed!, reason: ${e}`); + return; + } +};