From 3406e99818fd460f4b830f29cf96d64f4d84a294 Mon Sep 17 00:00:00 2001 From: Mentlegen <9807008+gentlementlegen@users.noreply.github.com> Date: Sat, 7 Sep 2024 14:36:26 +0900 Subject: [PATCH 01/18] chore(WIP): pull request link --- manifest.json | 2 +- src/handlers/user-start-stop.ts | 15 ++++++++++++++- src/plugin.ts | 6 ++++-- src/types/context.ts | 2 +- 4 files changed, 20 insertions(+), 5 deletions(-) diff --git a/manifest.json b/manifest.json index 1e2d19c..4c36892 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "name": "Start | Stop", "description": "Assign or un-assign yourself from an issue.", - "ubiquity:listeners": ["issue_comment.created", "issues.assigned"], + "ubiquity:listeners": ["issue_comment.created", "issues.assigned", "pull_request.opened"], "commands": { "start": { "ubiquity:example": "/start", diff --git a/src/handlers/user-start-stop.ts b/src/handlers/user-start-stop.ts index 143287d..875278c 100644 --- a/src/handlers/user-start-stop.ts +++ b/src/handlers/user-start-stop.ts @@ -26,7 +26,7 @@ export async function userStartStop(context: Context): Promise { return { status: HttpStatusCode.NOT_MODIFIED }; } -export async function userSelfAssign(context: Context): Promise { +export async function userSelfAssign(context: Context<"issues.assigned">): Promise { const { payload } = context; const { issue } = payload; const deadline = getDeadline(issue); @@ -41,3 +41,16 @@ export async function userSelfAssign(context: Context): Promise { await addCommentToIssue(context, `${users} the deadline is at ${deadline}`); return { status: HttpStatusCode.OK }; } + +export async function userPullRequest(context: Context<"pull_request.opened">): Promise { + const { payload } = context; + const { pull_request } = payload; + const deadline = getDeadline(pull_request); + + if (!deadline) { + context.logger.debug("Skipping deadline posting message because no deadline has been set."); + return { status: HttpStatusCode.NOT_MODIFIED }; + } + context.logger.debug("Pull request", pull_request); + return { status: HttpStatusCode.NOT_MODIFIED }; +} diff --git a/src/plugin.ts b/src/plugin.ts index 8476325..cfad87f 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -2,7 +2,7 @@ import { Octokit } from "@octokit/rest"; import { createClient } from "@supabase/supabase-js"; import { LogReturn, Logs } from "@ubiquity-dao/ubiquibot-logger"; import { createAdapters } from "./adapters"; -import { userSelfAssign, userStartStop } from "./handlers/user-start-stop"; +import { userPullRequest, userSelfAssign, userStartStop } from "./handlers/user-start-stop"; import { Context, Env, PluginInputs } from "./types"; import { addCommentToIssue } from "./utils/issue"; @@ -27,7 +27,9 @@ export async function startStopTask(inputs: PluginInputs, env: Env) { case "issue_comment.created": return await userStartStop(context); case "issues.assigned": - return await userSelfAssign(context); + return await userSelfAssign(context as Context<"issues.assigned">); + case "pull_request.opened": + return await userPullRequest(context as Context<"pull_request.opened">); default: context.logger.error(`Unsupported event: ${context.eventName}`); } diff --git a/src/types/context.ts b/src/types/context.ts index 85db1d2..4e1734f 100644 --- a/src/types/context.ts +++ b/src/types/context.ts @@ -5,7 +5,7 @@ import { createAdapters } from "../adapters"; import { Env } from "./env"; import { Logs } from "@ubiquity-dao/ubiquibot-logger"; -export type SupportedEventsU = "issue_comment.created" | "issues.assigned"; +export type SupportedEventsU = "issue_comment.created" | "issues.assigned" | "pull_request.opened"; export type SupportedEvents = { [K in SupportedEventsU]: K extends WebhookEventName ? WebhookEvent : never; From b1091515c6523d8efbb1725bf04b6e9c231cb3e6 Mon Sep 17 00:00:00 2001 From: Mentlegen <9807008+gentlementlegen@users.noreply.github.com> Date: Sat, 7 Sep 2024 14:45:42 +0900 Subject: [PATCH 02/18] chore(WIP): pull request reopened --- manifest.json | 2 +- src/handlers/user-start-stop.ts | 2 +- src/plugin.ts | 2 ++ src/types/context.ts | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/manifest.json b/manifest.json index 4c36892..2fa491a 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "name": "Start | Stop", "description": "Assign or un-assign yourself from an issue.", - "ubiquity:listeners": ["issue_comment.created", "issues.assigned", "pull_request.opened"], + "ubiquity:listeners": ["issue_comment.created", "issues.assigned", "pull_request.opened", "pull_request.reopened"], "commands": { "start": { "ubiquity:example": "/start", diff --git a/src/handlers/user-start-stop.ts b/src/handlers/user-start-stop.ts index 875278c..fa7ca21 100644 --- a/src/handlers/user-start-stop.ts +++ b/src/handlers/user-start-stop.ts @@ -42,7 +42,7 @@ export async function userSelfAssign(context: Context<"issues.assigned">): Promi return { status: HttpStatusCode.OK }; } -export async function userPullRequest(context: Context<"pull_request.opened">): Promise { +export async function userPullRequest(context: Context<"pull_request.opened"> | Context<"pull_request.reopened">): Promise { const { payload } = context; const { pull_request } = payload; const deadline = getDeadline(pull_request); diff --git a/src/plugin.ts b/src/plugin.ts index cfad87f..1e6dd2f 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -29,6 +29,8 @@ export async function startStopTask(inputs: PluginInputs, env: Env) { case "issues.assigned": return await userSelfAssign(context as Context<"issues.assigned">); case "pull_request.opened": + return await userPullRequest(context as Context<"pull_request.reopened">); + case "pull_request.reopened": return await userPullRequest(context as Context<"pull_request.opened">); default: context.logger.error(`Unsupported event: ${context.eventName}`); diff --git a/src/types/context.ts b/src/types/context.ts index 4e1734f..c301c76 100644 --- a/src/types/context.ts +++ b/src/types/context.ts @@ -5,7 +5,7 @@ import { createAdapters } from "../adapters"; import { Env } from "./env"; import { Logs } from "@ubiquity-dao/ubiquibot-logger"; -export type SupportedEventsU = "issue_comment.created" | "issues.assigned" | "pull_request.opened"; +export type SupportedEventsU = "issue_comment.created" | "issues.assigned" | "pull_request.opened" | "pull_request.reopened"; export type SupportedEvents = { [K in SupportedEventsU]: K extends WebhookEventName ? WebhookEvent : never; From 3bccb6e9cf2bf1e7174cced1c464a7193de69192 Mon Sep 17 00:00:00 2001 From: Mentlegen <9807008+gentlementlegen@users.noreply.github.com> Date: Sat, 7 Sep 2024 14:50:01 +0900 Subject: [PATCH 03/18] chore(WIP): pull request reopened --- src/handlers/user-start-stop.ts | 1 + src/plugin.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/src/handlers/user-start-stop.ts b/src/handlers/user-start-stop.ts index fa7ca21..8015a3e 100644 --- a/src/handlers/user-start-stop.ts +++ b/src/handlers/user-start-stop.ts @@ -52,5 +52,6 @@ export async function userPullRequest(context: Context<"pull_request.opened"> | return { status: HttpStatusCode.NOT_MODIFIED }; } context.logger.debug("Pull request", pull_request); + console.log(pull_request); return { status: HttpStatusCode.NOT_MODIFIED }; } diff --git a/src/plugin.ts b/src/plugin.ts index 1e6dd2f..bdd6378 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -23,6 +23,7 @@ export async function startStopTask(inputs: PluginInputs, env: Env) { context.adapters = createAdapters(supabase, context); try { + console.log("event", context.eventName); switch (context.eventName) { case "issue_comment.created": return await userStartStop(context); From caac1a480e0b7def2cf6e7fe8ced55094868cae2 Mon Sep 17 00:00:00 2001 From: Mentlegen <9807008+gentlementlegen@users.noreply.github.com> Date: Sat, 7 Sep 2024 14:53:31 +0900 Subject: [PATCH 04/18] chore(WIP): pull request reopened --- src/handlers/user-start-stop.ts | 4 +++- src/plugin.ts | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/handlers/user-start-stop.ts b/src/handlers/user-start-stop.ts index 8015a3e..b26ade0 100644 --- a/src/handlers/user-start-stop.ts +++ b/src/handlers/user-start-stop.ts @@ -45,8 +45,10 @@ export async function userSelfAssign(context: Context<"issues.assigned">): Promi export async function userPullRequest(context: Context<"pull_request.opened"> | Context<"pull_request.reopened">): Promise { const { payload } = context; const { pull_request } = payload; - const deadline = getDeadline(pull_request); + console.log(pull_request); + const deadline = getDeadline(pull_request); + console.log(deadline); if (!deadline) { context.logger.debug("Skipping deadline posting message because no deadline has been set."); return { status: HttpStatusCode.NOT_MODIFIED }; diff --git a/src/plugin.ts b/src/plugin.ts index bdd6378..42106de 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -30,9 +30,9 @@ export async function startStopTask(inputs: PluginInputs, env: Env) { case "issues.assigned": return await userSelfAssign(context as Context<"issues.assigned">); case "pull_request.opened": - return await userPullRequest(context as Context<"pull_request.reopened">); - case "pull_request.reopened": return await userPullRequest(context as Context<"pull_request.opened">); + case "pull_request.reopened": + return await userPullRequest(context as Context<"pull_request.reopened">); default: context.logger.error(`Unsupported event: ${context.eventName}`); } From 7fa4563763460a93059cd4f48b11377f248cc850 Mon Sep 17 00:00:00 2001 From: Mentlegen <9807008+gentlementlegen@users.noreply.github.com> Date: Sat, 7 Sep 2024 15:32:10 +0900 Subject: [PATCH 05/18] chore(WIP): added graphql --- graphql.config.yml | 6 ++++ package.json | 2 ++ src/handlers/user-start-stop.ts | 19 +++++++++-- src/plugin.ts | 5 +-- src/types/context.ts | 3 +- src/utils/get-closing-issue-references.ts | 18 ++++++++++ yarn.lock | 41 ++++++++++++++++++++++- 7 files changed, 88 insertions(+), 6 deletions(-) create mode 100644 graphql.config.yml create mode 100644 src/utils/get-closing-issue-references.ts diff --git a/graphql.config.yml b/graphql.config.yml new file mode 100644 index 0000000..af13179 --- /dev/null +++ b/graphql.config.yml @@ -0,0 +1,6 @@ +schema: + - https://api.github.com/graphql: + headers: + Authorization: Bearer ${GITHUB_TOKEN} +documents: src/**.ts +projects: {} diff --git a/package.json b/package.json index a52f1cf..f71cca2 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,8 @@ "open-source" ], "dependencies": { + "@octokit/graphql-schema": "15.25.0", + "@octokit/plugin-paginate-graphql": "5.2.2", "@octokit/rest": "20.1.1", "@octokit/webhooks": "13.2.7", "@sinclair/typebox": "^0.32.5", diff --git a/src/handlers/user-start-stop.ts b/src/handlers/user-start-stop.ts index b26ade0..2dfff47 100644 --- a/src/handlers/user-start-stop.ts +++ b/src/handlers/user-start-stop.ts @@ -1,5 +1,6 @@ import { Context, isContextCommentCreated } from "../types"; -import { addCommentToIssue } from "../utils/issue"; +import { QUERY_CLOSING_ISSUE_REFERENCES } from "../utils/get-closing-issue-references"; +import { addCommentToIssue, getOwnerRepoFromHtmlUrl } from "../utils/issue"; import { HttpStatusCode, Result } from "./result-types"; import { getDeadline } from "./shared/generate-assignment-comment"; import { start } from "./shared/start"; @@ -53,7 +54,21 @@ export async function userPullRequest(context: Context<"pull_request.opened"> | context.logger.debug("Skipping deadline posting message because no deadline has been set."); return { status: HttpStatusCode.NOT_MODIFIED }; } - context.logger.debug("Pull request", pull_request); + const { owner, repo } = getOwnerRepoFromHtmlUrl(pull_request.html_url); + const linkedIssues = await context.octokit.graphql.paginate(QUERY_CLOSING_ISSUE_REFERENCES, { + owner, + repo, + issue_number: pull_request.id, + }); + console.log( + "Pull request", + { + owner, + repo, + issue_number: pull_request.id, + }, + linkedIssues + ); console.log(pull_request); return { status: HttpStatusCode.NOT_MODIFIED }; } diff --git a/src/plugin.ts b/src/plugin.ts index 42106de..73b071b 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -1,3 +1,4 @@ +import { paginateGraphQL } from "@octokit/plugin-paginate-graphql"; import { Octokit } from "@octokit/rest"; import { createClient } from "@supabase/supabase-js"; import { LogReturn, Logs } from "@ubiquity-dao/ubiquibot-logger"; @@ -7,7 +8,8 @@ import { Context, Env, PluginInputs } from "./types"; import { addCommentToIssue } from "./utils/issue"; export async function startStopTask(inputs: PluginInputs, env: Env) { - const octokit = new Octokit({ auth: inputs.authToken }); + const customOctokit = Octokit.plugin(paginateGraphQL); + const octokit = new customOctokit({ auth: inputs.authToken }); const supabase = createClient(env.SUPABASE_URL, env.SUPABASE_KEY); const context: Context = { @@ -23,7 +25,6 @@ export async function startStopTask(inputs: PluginInputs, env: Env) { context.adapters = createAdapters(supabase, context); try { - console.log("event", context.eventName); switch (context.eventName) { case "issue_comment.created": return await userStartStop(context); diff --git a/src/types/context.ts b/src/types/context.ts index c301c76..1a49209 100644 --- a/src/types/context.ts +++ b/src/types/context.ts @@ -1,3 +1,4 @@ +import { paginateGraphQLInterface } from "@octokit/plugin-paginate-graphql"; import { EmitterWebhookEvent as WebhookEvent, EmitterWebhookEventName as WebhookEventName } from "@octokit/webhooks"; import { Octokit } from "@octokit/rest"; import { StartStopSettings } from "./plugin-input"; @@ -18,7 +19,7 @@ export function isContextCommentCreated(context: Context): context is Context<"i export interface Context { eventName: T; payload: TU["payload"]; - octokit: InstanceType; + octokit: InstanceType & paginateGraphQLInterface; adapters: ReturnType; config: StartStopSettings; env: Env; diff --git a/src/utils/get-closing-issue-references.ts b/src/utils/get-closing-issue-references.ts new file mode 100644 index 0000000..e93ec12 --- /dev/null +++ b/src/utils/get-closing-issue-references.ts @@ -0,0 +1,18 @@ +export const QUERY_CLOSING_ISSUE_REFERENCES = /* GraphQL */ ` + query closingIssueReferences($owner: String!, $repo: String!, $issue_number: Int!, $cursor: String) { + repository(owner: $owner, name: $repo) { + pullRequest(number: $issue_number) { + id + closingIssuesReferences(first: 10, after: $cursor) { + nodes { + id + } + pageInfo { + hasNextPage + endCursor + } + } + } + } + } +`; diff --git a/yarn.lock b/yarn.lock index 4147491..e7c0eb6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2143,6 +2143,16 @@ __metadata: languageName: node linkType: hard +"@octokit/graphql-schema@npm:15.25.0": + version: 15.25.0 + resolution: "@octokit/graphql-schema@npm:15.25.0" + dependencies: + graphql: "npm:^16.0.0" + graphql-tag: "npm:^2.10.3" + checksum: 10c0/c61898d062aa2559a72a2ac3f573410053ca763fe2797f7c4b46ddfa0c435325a7645e8f4613a6d39368d5b66db98b126efdb20ccb370b263eaaab5a67d1e2aa + languageName: node + linkType: hard + "@octokit/graphql@npm:^7.1.0": version: 7.1.0 resolution: "@octokit/graphql@npm:7.1.0" @@ -2168,6 +2178,15 @@ __metadata: languageName: node linkType: hard +"@octokit/plugin-paginate-graphql@npm:5.2.2": + version: 5.2.2 + resolution: "@octokit/plugin-paginate-graphql@npm:5.2.2" + peerDependencies: + "@octokit/core": ">=6" + checksum: 10c0/726944d27459cf5f5682a65b52c01821d023a5b9d7a9ad3078070ebfd839a4819fb1ada64255a04ee0ef15dd43ee3286295a7a071e42d738221104aaa0f235d5 + languageName: node + linkType: hard + "@octokit/plugin-paginate-rest@npm:11.3.1": version: 11.3.1 resolution: "@octokit/plugin-paginate-rest@npm:11.3.1" @@ -2780,6 +2799,8 @@ __metadata: "@eslint/js": "npm:9.5.0" "@jest/globals": "npm:29.7.0" "@mswjs/data": "npm:0.16.1" + "@octokit/graphql-schema": "npm:15.25.0" + "@octokit/plugin-paginate-graphql": "npm:5.2.2" "@octokit/rest": "npm:20.1.1" "@octokit/webhooks": "npm:13.2.7" "@sinclair/typebox": "npm:^0.32.5" @@ -5229,7 +5250,18 @@ __metadata: languageName: node linkType: hard -"graphql@npm:^16.8.1": +"graphql-tag@npm:^2.10.3": + version: 2.12.6 + resolution: "graphql-tag@npm:2.12.6" + dependencies: + tslib: "npm:^2.1.0" + peerDependencies: + graphql: ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + checksum: 10c0/7763a72011bda454ed8ff1a0d82325f43ca6478e4ce4ab8b7910c4c651dd00db553132171c04d80af5d5aebf1ef6a8a9fd53ccfa33b90ddc00aa3d4be6114419 + languageName: node + linkType: hard + +"graphql@npm:^16.0.0, graphql@npm:^16.8.1": version: 16.9.0 resolution: "graphql@npm:16.9.0" checksum: 10c0/a8850f077ff767377237d1f8b1da2ec70aeb7623cdf1dfc9e1c7ae93accc0c8149c85abe68923be9871a2934b1bce5a2496f846d4d56e1cfb03eaaa7ddba9b6a @@ -8743,6 +8775,13 @@ __metadata: languageName: node linkType: hard +"tslib@npm:^2.1.0": + version: 2.7.0 + resolution: "tslib@npm:2.7.0" + checksum: 10c0/469e1d5bf1af585742128827000711efa61010b699cb040ab1800bcd3ccdd37f63ec30642c9e07c4439c1db6e46345582614275daca3e0f4abae29b0083f04a6 + languageName: node + linkType: hard + "tslib@npm:^2.2.0, tslib@npm:^2.6.2": version: 2.6.3 resolution: "tslib@npm:2.6.3" From c6b194f7ff625d64be59e1b2df39e2795fd79fce Mon Sep 17 00:00:00 2001 From: Mentlegen <9807008+gentlementlegen@users.noreply.github.com> Date: Sat, 7 Sep 2024 15:36:07 +0900 Subject: [PATCH 06/18] chore(WIP): check linked pull-requests --- src/handlers/user-start-stop.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/handlers/user-start-stop.ts b/src/handlers/user-start-stop.ts index 2dfff47..9d8ca33 100644 --- a/src/handlers/user-start-stop.ts +++ b/src/handlers/user-start-stop.ts @@ -46,14 +46,6 @@ export async function userSelfAssign(context: Context<"issues.assigned">): Promi export async function userPullRequest(context: Context<"pull_request.opened"> | Context<"pull_request.reopened">): Promise { const { payload } = context; const { pull_request } = payload; - console.log(pull_request); - - const deadline = getDeadline(pull_request); - console.log(deadline); - if (!deadline) { - context.logger.debug("Skipping deadline posting message because no deadline has been set."); - return { status: HttpStatusCode.NOT_MODIFIED }; - } const { owner, repo } = getOwnerRepoFromHtmlUrl(pull_request.html_url); const linkedIssues = await context.octokit.graphql.paginate(QUERY_CLOSING_ISSUE_REFERENCES, { owner, @@ -70,5 +62,13 @@ export async function userPullRequest(context: Context<"pull_request.opened"> | linkedIssues ); console.log(pull_request); + // console.log(pull_request); + // + // const deadline = getDeadline(pull_request); + // console.log(deadline); + // if (!deadline) { + // context.logger.debug("Skipping deadline posting message because no deadline has been set."); + // return { status: HttpStatusCode.NOT_MODIFIED }; + // } return { status: HttpStatusCode.NOT_MODIFIED }; } From 201153fc10075a55e56bc67e93af055aabd73e45 Mon Sep 17 00:00:00 2001 From: Mentlegen <9807008+gentlementlegen@users.noreply.github.com> Date: Sat, 7 Sep 2024 15:38:15 +0900 Subject: [PATCH 07/18] chore(WIP): check linked pull-requests --- src/handlers/user-start-stop.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/handlers/user-start-stop.ts b/src/handlers/user-start-stop.ts index 9d8ca33..95ac133 100644 --- a/src/handlers/user-start-stop.ts +++ b/src/handlers/user-start-stop.ts @@ -50,7 +50,7 @@ export async function userPullRequest(context: Context<"pull_request.opened"> | const linkedIssues = await context.octokit.graphql.paginate(QUERY_CLOSING_ISSUE_REFERENCES, { owner, repo, - issue_number: pull_request.id, + issue_number: pull_request.number, }); console.log( "Pull request", From c5b92dc4b8bc9a702f652b2b8d011af5e8e261cc Mon Sep 17 00:00:00 2001 From: Mentlegen <9807008+gentlementlegen@users.noreply.github.com> Date: Sat, 7 Sep 2024 15:56:45 +0900 Subject: [PATCH 08/18] chore(WIP): check linked pull-requests --- src/handlers/user-start-stop.ts | 25 +++++++++++++---------- src/utils/get-closing-issue-references.ts | 7 +++++++ 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/handlers/user-start-stop.ts b/src/handlers/user-start-stop.ts index 95ac133..20fe610 100644 --- a/src/handlers/user-start-stop.ts +++ b/src/handlers/user-start-stop.ts @@ -1,3 +1,4 @@ +import { Repository } from "@octokit/graphql-schema"; import { Context, isContextCommentCreated } from "../types"; import { QUERY_CLOSING_ISSUE_REFERENCES } from "../utils/get-closing-issue-references"; import { addCommentToIssue, getOwnerRepoFromHtmlUrl } from "../utils/issue"; @@ -47,21 +48,23 @@ export async function userPullRequest(context: Context<"pull_request.opened"> | const { payload } = context; const { pull_request } = payload; const { owner, repo } = getOwnerRepoFromHtmlUrl(pull_request.html_url); - const linkedIssues = await context.octokit.graphql.paginate(QUERY_CLOSING_ISSUE_REFERENCES, { + const linkedIssues = await context.octokit.graphql.paginate(QUERY_CLOSING_ISSUE_REFERENCES, { owner, repo, issue_number: pull_request.number, }); - console.log( - "Pull request", - { - owner, - repo, - issue_number: pull_request.id, - }, - linkedIssues - ); - console.log(pull_request); + console.log(linkedIssues); + const issues = linkedIssues.pullRequest?.closingIssuesReferences?.nodes; + if (!issues) { + context.logger.info("No linked issues were found, nothing to do."); + return { status: HttpStatusCode.NOT_MODIFIED }; + } + for (const issue of issues) { + console.log(issue, pull_request.user); + if (!issue?.assignees.nodes?.includes((node) => node.login === pull_request.user?.login)) { + console.log("assigning!"); + } + } // console.log(pull_request); // // const deadline = getDeadline(pull_request); diff --git a/src/utils/get-closing-issue-references.ts b/src/utils/get-closing-issue-references.ts index e93ec12..7645813 100644 --- a/src/utils/get-closing-issue-references.ts +++ b/src/utils/get-closing-issue-references.ts @@ -6,6 +6,13 @@ export const QUERY_CLOSING_ISSUE_REFERENCES = /* GraphQL */ ` closingIssuesReferences(first: 10, after: $cursor) { nodes { id + url + assignees(first: 100) { + nodes { + id + login + } + } } pageInfo { hasNextPage From b90a5601f8569c4c808fa1a753cd3f08b937b54a Mon Sep 17 00:00:00 2001 From: Mentlegen <9807008+gentlementlegen@users.noreply.github.com> Date: Sat, 7 Sep 2024 16:00:28 +0900 Subject: [PATCH 09/18] chore(WIP): check linked pull-requests --- src/handlers/user-start-stop.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/handlers/user-start-stop.ts b/src/handlers/user-start-stop.ts index 20fe610..2df463d 100644 --- a/src/handlers/user-start-stop.ts +++ b/src/handlers/user-start-stop.ts @@ -48,13 +48,13 @@ export async function userPullRequest(context: Context<"pull_request.opened"> | const { payload } = context; const { pull_request } = payload; const { owner, repo } = getOwnerRepoFromHtmlUrl(pull_request.html_url); - const linkedIssues = await context.octokit.graphql.paginate(QUERY_CLOSING_ISSUE_REFERENCES, { + const linkedIssues = await context.octokit.graphql.paginate<{ repository: Repository }>(QUERY_CLOSING_ISSUE_REFERENCES, { owner, repo, issue_number: pull_request.number, }); console.log(linkedIssues); - const issues = linkedIssues.pullRequest?.closingIssuesReferences?.nodes; + const issues = linkedIssues.repository.pullRequest?.closingIssuesReferences?.nodes; if (!issues) { context.logger.info("No linked issues were found, nothing to do."); return { status: HttpStatusCode.NOT_MODIFIED }; From 6c0fd74cf4e18eb0b99b6dc85027bad09e99f287 Mon Sep 17 00:00:00 2001 From: Mentlegen <9807008+gentlementlegen@users.noreply.github.com> Date: Sat, 7 Sep 2024 16:09:19 +0900 Subject: [PATCH 10/18] chore(WIP): check linked pull-requests --- src/handlers/user-start-stop.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/handlers/user-start-stop.ts b/src/handlers/user-start-stop.ts index 2df463d..79ca7c8 100644 --- a/src/handlers/user-start-stop.ts +++ b/src/handlers/user-start-stop.ts @@ -61,17 +61,17 @@ export async function userPullRequest(context: Context<"pull_request.opened"> | } for (const issue of issues) { console.log(issue, pull_request.user); - if (!issue?.assignees.nodes?.includes((node) => node.login === pull_request.user?.login)) { - console.log("assigning!"); + if (!issue?.assignees.nodes?.includes((node) => node.id === pull_request.user?.id)) { + const deadline = getDeadline(issue); + console.log(deadline); + if (!deadline) { + context.logger.debug("Skipping deadline posting message because no deadline has been set."); + return { status: HttpStatusCode.NOT_MODIFIED }; + } else { + console.log("assigning!"); + return await start(context, issue, payload.sender, []); + } } } - // console.log(pull_request); - // - // const deadline = getDeadline(pull_request); - // console.log(deadline); - // if (!deadline) { - // context.logger.debug("Skipping deadline posting message because no deadline has been set."); - // return { status: HttpStatusCode.NOT_MODIFIED }; - // } return { status: HttpStatusCode.NOT_MODIFIED }; } From b3f8c363b60ea673ccdb046256addf8bb0b84f84 Mon Sep 17 00:00:00 2001 From: Mentlegen <9807008+gentlementlegen@users.noreply.github.com> Date: Sat, 7 Sep 2024 16:16:50 +0900 Subject: [PATCH 11/18] chore(WIP): check linked pull-requests --- src/handlers/user-start-stop.ts | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/handlers/user-start-stop.ts b/src/handlers/user-start-stop.ts index 79ca7c8..830c067 100644 --- a/src/handlers/user-start-stop.ts +++ b/src/handlers/user-start-stop.ts @@ -62,14 +62,18 @@ export async function userPullRequest(context: Context<"pull_request.opened"> | for (const issue of issues) { console.log(issue, pull_request.user); if (!issue?.assignees.nodes?.includes((node) => node.id === pull_request.user?.id)) { - const deadline = getDeadline(issue); - console.log(deadline); - if (!deadline) { - context.logger.debug("Skipping deadline posting message because no deadline has been set."); - return { status: HttpStatusCode.NOT_MODIFIED }; - } else { - console.log("assigning!"); - return await start(context, issue, payload.sender, []); + try { + const deadline = getDeadline(issue); + console.log(deadline); + if (!deadline) { + context.logger.debug("Skipping deadline posting message because no deadline has been set."); + return { status: HttpStatusCode.NOT_MODIFIED }; + } else { + console.log("assigning!"); + return await start(context, issue, payload.sender, []); + } + } catch (e) { + context.logger.error("Failed to assign the user to the issue.", { e }); } } } From 944c7a4add4c43e921b8c14bf7bc0c76f3b07e7e Mon Sep 17 00:00:00 2001 From: Mentlegen <9807008+gentlementlegen@users.noreply.github.com> Date: Sat, 7 Sep 2024 16:20:32 +0900 Subject: [PATCH 12/18] chore(WIP): check linked pull-requests --- src/utils/get-closing-issue-references.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/utils/get-closing-issue-references.ts b/src/utils/get-closing-issue-references.ts index 7645813..a181df9 100644 --- a/src/utils/get-closing-issue-references.ts +++ b/src/utils/get-closing-issue-references.ts @@ -7,6 +7,12 @@ export const QUERY_CLOSING_ISSUE_REFERENCES = /* GraphQL */ ` nodes { id url + labels(first: 100) { + nodes { + id + name + } + } assignees(first: 100) { nodes { id From 910b15ea442f04ffe23f7c954d55b3e0518dcdb1 Mon Sep 17 00:00:00 2001 From: gentlementlegen Date: Mon, 9 Sep 2024 17:28:04 +0900 Subject: [PATCH 13/18] chore: user is properly assigned on PR opened --- .../shared/generate-assignment-comment.ts | 6 ++--- src/handlers/shared/start.ts | 9 +++++-- src/handlers/user-start-stop.ts | 27 ++++++++----------- src/utils/get-closing-issue-references.ts | 1 + src/utils/shared.ts | 8 +++--- 5 files changed, 26 insertions(+), 25 deletions(-) diff --git a/src/handlers/shared/generate-assignment-comment.ts b/src/handlers/shared/generate-assignment-comment.ts index c8c3317..fa03e8a 100644 --- a/src/handlers/shared/generate-assignment-comment.ts +++ b/src/handlers/shared/generate-assignment-comment.ts @@ -11,12 +11,12 @@ export const options: Intl.DateTimeFormatOptions = { timeZoneName: "short", }; -export function getDeadline(issue: Context["payload"]["issue"]): string | null { - if (!issue?.labels) { +export function getDeadline(labels: Context<"issue_comment.created">["payload"]["issue"]["labels"] | undefined | null): string | null { + if (!labels?.length) { throw new Error("No labels are set."); } const startTime = new Date().getTime(); - const duration: number = calculateDurations(issue.labels).shift() ?? 0; + const duration: number = calculateDurations(labels).shift() ?? 0; if (!duration) return null; const endTime = new Date(startTime + duration * 1000); return endTime.toLocaleString("en-US", options); diff --git a/src/handlers/shared/start.ts b/src/handlers/shared/start.ts index 19a6649..fdfbcd3 100644 --- a/src/handlers/shared/start.ts +++ b/src/handlers/shared/start.ts @@ -7,7 +7,12 @@ import { generateAssignmentComment, getDeadline } from "./generate-assignment-co import structuredMetadata from "./structured-metadata"; import { assignTableComment } from "./table"; -export async function start(context: Context, issue: Context["payload"]["issue"], sender: Context["payload"]["sender"], teammates: string[]): Promise { +export async function start( + context: Context, + issue: Context<"issue_comment.created">["payload"]["issue"], + sender: Context["payload"]["sender"], + teammates: string[] +): Promise { const { logger, config } = context; const { maxConcurrentTasks, taskStaleTimeoutDuration } = config; @@ -82,7 +87,7 @@ export async function start(context: Context, issue: Context["payload"]["issue"] throw new Error(logger.error("No price label is set to calculate the duration", { issueNumber: issue.number }).logMessage.raw); } - const deadline = getDeadline(issue); + const deadline = getDeadline(labels); const toAssignIds = await fetchUserIds(context, toAssign); const assignmentComment = await generateAssignmentComment(context, issue.created_at, issue.number, sender.id, deadline); diff --git a/src/handlers/user-start-stop.ts b/src/handlers/user-start-stop.ts index 830c067..d65fcdf 100644 --- a/src/handlers/user-start-stop.ts +++ b/src/handlers/user-start-stop.ts @@ -31,7 +31,7 @@ export async function userStartStop(context: Context): Promise { export async function userSelfAssign(context: Context<"issues.assigned">): Promise { const { payload } = context; const { issue } = payload; - const deadline = getDeadline(issue); + const deadline = getDeadline(issue.labels); if (!deadline) { context.logger.debug("Skipping deadline posting message because no deadline has been set."); @@ -53,27 +53,22 @@ export async function userPullRequest(context: Context<"pull_request.opened"> | repo, issue_number: pull_request.number, }); - console.log(linkedIssues); const issues = linkedIssues.repository.pullRequest?.closingIssuesReferences?.nodes; if (!issues) { context.logger.info("No linked issues were found, nothing to do."); return { status: HttpStatusCode.NOT_MODIFIED }; } for (const issue of issues) { - console.log(issue, pull_request.user); - if (!issue?.assignees.nodes?.includes((node) => node.id === pull_request.user?.id)) { - try { - const deadline = getDeadline(issue); - console.log(deadline); - if (!deadline) { - context.logger.debug("Skipping deadline posting message because no deadline has been set."); - return { status: HttpStatusCode.NOT_MODIFIED }; - } else { - console.log("assigning!"); - return await start(context, issue, payload.sender, []); - } - } catch (e) { - context.logger.error("Failed to assign the user to the issue.", { e }); + if (!issue?.assignees.nodes?.some((node) => node?.id.toString() === pull_request.user?.id.toString())) { + const deadline = getDeadline(issue?.labels?.nodes); + if (!deadline) { + context.logger.debug("Skipping deadline posting message because no deadline has been set."); + return { status: HttpStatusCode.NOT_MODIFIED }; + } else { + issue.assignees = issue.assignees.nodes; + issue.labels = issue.labels.nodes; + context.payload.issue = issue; + return await start(context, issue, payload.sender, []); } } } diff --git a/src/utils/get-closing-issue-references.ts b/src/utils/get-closing-issue-references.ts index a181df9..5d840e1 100644 --- a/src/utils/get-closing-issue-references.ts +++ b/src/utils/get-closing-issue-references.ts @@ -7,6 +7,7 @@ export const QUERY_CLOSING_ISSUE_REFERENCES = /* GraphQL */ ` nodes { id url + number labels(first: 100) { nodes { id diff --git a/src/utils/shared.ts b/src/utils/shared.ts index 4b8a415..6895e78 100644 --- a/src/utils/shared.ts +++ b/src/utils/shared.ts @@ -1,12 +1,12 @@ import ms from "ms"; -import { Label } from "../types"; +import { Context } from "../types"; -export function calculateDurations(labels: Label[]): number[] { +export function calculateDurations(labels: Context<"issue_comment.created">["payload"]["issue"]["labels"]): number[] { // from shortest to longest const durations: number[] = []; - labels.forEach((label: Label) => { - const matches = label.name.match(/<(\d+)\s*(\w+)/); + labels.forEach((label) => { + const matches = label?.name.match(/<(\d+)\s*(\w+)/); if (matches && matches.length >= 3) { const number = parseInt(matches[1]); const unit = matches[2]; From 19743ea67faaaea32937791787f000701b8d1cb5 Mon Sep 17 00:00:00 2001 From: gentlementlegen Date: Mon, 9 Sep 2024 18:39:06 +0900 Subject: [PATCH 14/18] chore: not trying to assign if already assigned --- src/handlers/user-start-stop.ts | 30 +++++++++++++++++------ src/utils/get-closing-issue-references.ts | 1 + tests/main.test.ts | 4 +-- 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/handlers/user-start-stop.ts b/src/handlers/user-start-stop.ts index d65fcdf..eb03cd5 100644 --- a/src/handlers/user-start-stop.ts +++ b/src/handlers/user-start-stop.ts @@ -1,5 +1,5 @@ import { Repository } from "@octokit/graphql-schema"; -import { Context, isContextCommentCreated } from "../types"; +import { Context, isContextCommentCreated, Label } from "../types"; import { QUERY_CLOSING_ISSUE_REFERENCES } from "../utils/get-closing-issue-references"; import { addCommentToIssue, getOwnerRepoFromHtmlUrl } from "../utils/issue"; import { HttpStatusCode, Result } from "./result-types"; @@ -59,16 +59,32 @@ export async function userPullRequest(context: Context<"pull_request.opened"> | return { status: HttpStatusCode.NOT_MODIFIED }; } for (const issue of issues) { - if (!issue?.assignees.nodes?.some((node) => node?.id.toString() === pull_request.user?.id.toString())) { - const deadline = getDeadline(issue?.labels?.nodes); + if (issue && !issue.assignees.nodes?.length) { + const labels = + issue.labels?.nodes?.reduce((acc, curr) => { + if (curr) { + acc.push({ + ...curr, + id: Number(curr.id), + node_id: curr.id, + default: true, + description: curr.description ?? null, + }); + } + return acc; + }, []) ?? []; + const deadline = getDeadline(labels); if (!deadline) { context.logger.debug("Skipping deadline posting message because no deadline has been set."); return { status: HttpStatusCode.NOT_MODIFIED }; } else { - issue.assignees = issue.assignees.nodes; - issue.labels = issue.labels.nodes; - context.payload.issue = issue; - return await start(context, issue, payload.sender, []); + const issueWithComment: Context<"issue_comment.created">["payload"]["issue"] = { + ...issue, + assignees: issue.assignees.nodes as Context<"issue_comment.created">["payload"]["issue"]["assignees"], + labels, + } as unknown as Context<"issue_comment.created">["payload"]["issue"]; + context.payload.issue = issueWithComment; + return await start(context, issueWithComment, payload.sender, []); } } } diff --git a/src/utils/get-closing-issue-references.ts b/src/utils/get-closing-issue-references.ts index 5d840e1..931dd80 100644 --- a/src/utils/get-closing-issue-references.ts +++ b/src/utils/get-closing-issue-references.ts @@ -12,6 +12,7 @@ export const QUERY_CLOSING_ISSUE_REFERENCES = /* GraphQL */ ` nodes { id name + description } } assignees(first: 100) { diff --git a/tests/main.test.ts b/tests/main.test.ts index a99e1c4..ba8409f 100644 --- a/tests/main.test.ts +++ b/tests/main.test.ts @@ -12,7 +12,7 @@ import dotenv from "dotenv"; import { Logs, cleanLogString } from "@ubiquity-dao/ubiquibot-logger"; dotenv.config(); -type Issue = Context["payload"]["issue"]; +type Issue = Context<"issue_comment.created">["payload"]["issue"]; type PayloadSender = Context["payload"]["sender"]; const octokit = jest.requireActual("@octokit/rest"); @@ -552,7 +552,7 @@ function createContext( return { adapters: {} as ReturnType, payload: { - issue: issue as unknown as Context["payload"]["issue"], + issue: issue as unknown as Context<"issue_comment.created">["payload"]["issue"], sender: sender as unknown as Context["payload"]["sender"], repository: db.repo.findFirst({ where: { id: { equals: 1 } } }) as unknown as Context["payload"]["repository"], comment: { body } as unknown as Context<"issue_comment.created">["payload"]["comment"], From cccad443ab23e1b4b82cc7b07ee76f13bb122715 Mon Sep 17 00:00:00 2001 From: gentlementlegen Date: Mon, 9 Sep 2024 18:55:19 +0900 Subject: [PATCH 15/18] chore: not assigned if already assigned before --- src/handlers/user-start-stop.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/handlers/user-start-stop.ts b/src/handlers/user-start-stop.ts index eb03cd5..161e012 100644 --- a/src/handlers/user-start-stop.ts +++ b/src/handlers/user-start-stop.ts @@ -82,8 +82,9 @@ export async function userPullRequest(context: Context<"pull_request.opened"> | ...issue, assignees: issue.assignees.nodes as Context<"issue_comment.created">["payload"]["issue"]["assignees"], labels, + html_url: issue.url, } as unknown as Context<"issue_comment.created">["payload"]["issue"]; - context.payload.issue = issueWithComment; + context.payload = Object.assign({ issue: issueWithComment }, context.payload); return await start(context, issueWithComment, payload.sender, []); } } From c8a51805eb48892b1c13693c02a152ac1085e6eb Mon Sep 17 00:00:00 2001 From: gentlementlegen Date: Mon, 9 Sep 2024 19:02:32 +0900 Subject: [PATCH 16/18] chore: fixed compile issue --- src/utils/issue.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/utils/issue.ts b/src/utils/issue.ts index 4133c5d..70d9691 100644 --- a/src/utils/issue.ts +++ b/src/utils/issue.ts @@ -1,6 +1,6 @@ import ms from "ms"; -import { Context } from "../types/context"; -import { Issue, GitHubIssueSearch, Review } from "../types/payload"; +import { Context, isContextCommentCreated } from "../types/context"; +import { GitHubIssueSearch, Issue, Review } from "../types/payload"; import { getLinkedPullRequests, GetLinkedResults } from "./get-linked-prs"; export function isParentIssue(body: string) { @@ -21,12 +21,17 @@ export async function getAssignedIssues(context: Context, username: string): Pro } export async function addCommentToIssue(context: Context, message: string | null) { - const { payload, logger } = context; if (!message) { - logger.error("Message is not defined"); + context.logger.error("Message is not defined"); return; } + if (!isContextCommentCreated(context)) { + context.logger.error("Cannot post without a referenced issue."); + return; + } + const { payload } = context; + try { await context.octokit.rest.issues.createComment({ owner: payload.repository.owner.login, From f500886ed0c48a46014795e69d9acc5a0f792fbd Mon Sep 17 00:00:00 2001 From: gentlementlegen Date: Mon, 9 Sep 2024 19:10:10 +0900 Subject: [PATCH 17/18] chore: fixed compile issue --- src/handlers/shared/check-assignments.ts | 8 +++++++- src/handlers/shared/stop.ts | 7 ++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/handlers/shared/check-assignments.ts b/src/handlers/shared/check-assignments.ts index c969231..8a58955 100644 --- a/src/handlers/shared/check-assignments.ts +++ b/src/handlers/shared/check-assignments.ts @@ -1,7 +1,10 @@ -import { Context } from "../../types"; +import { Context, isContextCommentCreated } from "../../types"; import { getOwnerRepoFromHtmlUrl } from "../../utils/issue"; async function getUserStopComments(context: Context, username: string): Promise { + if (!isContextCommentCreated(context)) { + throw new Error("The context does not contain an issue."); + } const { payload, octokit, logger } = context; const { number, html_url } = payload.issue; const { owner, repo } = getOwnerRepoFromHtmlUrl(html_url); @@ -52,6 +55,9 @@ export async function hasUserBeenUnassigned(context: Context, username: string): } async function getAssignmentEvents(context: Context) { + if (!isContextCommentCreated(context)) { + throw new Error("The context does not contain an issue."); + } const { repository, issue } = context.payload; try { const data = await context.octokit.paginate(context.octokit.issues.listEventsForTimeline, { diff --git a/src/handlers/shared/stop.ts b/src/handlers/shared/stop.ts index 2116151..caafd48 100644 --- a/src/handlers/shared/stop.ts +++ b/src/handlers/shared/stop.ts @@ -2,7 +2,12 @@ import { Assignee, Context, Sender } from "../../types"; import { addCommentToIssue, closePullRequestForAnIssue } from "../../utils/issue"; import { HttpStatusCode, Result } from "../result-types"; -export async function stop(context: Context, issue: Context["payload"]["issue"], sender: Sender, repo: Context["payload"]["repository"]): Promise { +export async function stop( + context: Context, + issue: Context<"issue_comment.created">["payload"]["issue"], + sender: Sender, + repo: Context["payload"]["repository"] +): Promise { const { logger } = context; const issueNumber = issue.number; From 243dc32af6e49694f3d4802d58e31403b6fdf3c5 Mon Sep 17 00:00:00 2001 From: gentlementlegen Date: Mon, 9 Sep 2024 22:36:10 +0900 Subject: [PATCH 18/18] chore: removed reopened event --- manifest.json | 2 +- src/handlers/user-start-stop.ts | 2 +- src/plugin.ts | 2 -- src/types/context.ts | 2 +- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/manifest.json b/manifest.json index 2fa491a..4c36892 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "name": "Start | Stop", "description": "Assign or un-assign yourself from an issue.", - "ubiquity:listeners": ["issue_comment.created", "issues.assigned", "pull_request.opened", "pull_request.reopened"], + "ubiquity:listeners": ["issue_comment.created", "issues.assigned", "pull_request.opened"], "commands": { "start": { "ubiquity:example": "/start", diff --git a/src/handlers/user-start-stop.ts b/src/handlers/user-start-stop.ts index 161e012..1303366 100644 --- a/src/handlers/user-start-stop.ts +++ b/src/handlers/user-start-stop.ts @@ -44,7 +44,7 @@ export async function userSelfAssign(context: Context<"issues.assigned">): Promi return { status: HttpStatusCode.OK }; } -export async function userPullRequest(context: Context<"pull_request.opened"> | Context<"pull_request.reopened">): Promise { +export async function userPullRequest(context: Context<"pull_request.opened">): Promise { const { payload } = context; const { pull_request } = payload; const { owner, repo } = getOwnerRepoFromHtmlUrl(pull_request.html_url); diff --git a/src/plugin.ts b/src/plugin.ts index 73b071b..988e70c 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -32,8 +32,6 @@ export async function startStopTask(inputs: PluginInputs, env: Env) { return await userSelfAssign(context as Context<"issues.assigned">); case "pull_request.opened": return await userPullRequest(context as Context<"pull_request.opened">); - case "pull_request.reopened": - return await userPullRequest(context as Context<"pull_request.reopened">); default: context.logger.error(`Unsupported event: ${context.eventName}`); } diff --git a/src/types/context.ts b/src/types/context.ts index 1a49209..3a5d276 100644 --- a/src/types/context.ts +++ b/src/types/context.ts @@ -6,7 +6,7 @@ import { createAdapters } from "../adapters"; import { Env } from "./env"; import { Logs } from "@ubiquity-dao/ubiquibot-logger"; -export type SupportedEventsU = "issue_comment.created" | "issues.assigned" | "pull_request.opened" | "pull_request.reopened"; +export type SupportedEventsU = "issue_comment.created" | "issues.assigned" | "pull_request.opened"; export type SupportedEvents = { [K in SupportedEventsU]: K extends WebhookEventName ? WebhookEvent : never;