From 7033aacf6d072cbdf133d59ad61610e1ed67cd25 Mon Sep 17 00:00:00 2001 From: gentlementlegen Date: Mon, 17 Jun 2024 16:30:27 +0200 Subject: [PATCH 01/10] feat: help is displayed on /help command --- src/github/handlers/index.ts | 2 + src/github/handlers/issue-comment-created.ts | 22 +++++++++ tests/events.test.ts | 51 ++++++++++++++++++++ 3 files changed, 75 insertions(+) create mode 100644 src/github/handlers/issue-comment-created.ts create mode 100644 tests/events.test.ts diff --git a/src/github/handlers/index.ts b/src/github/handlers/index.ts index ac56ab7..522d6ae 100644 --- a/src/github/handlers/index.ts +++ b/src/github/handlers/index.ts @@ -2,6 +2,7 @@ import { EmitterWebhookEvent } from "@octokit/webhooks"; import { GitHubContext } from "../github-context"; import { GitHubEventHandler } from "../github-event-handler"; import { getConfig } from "../utils/config"; +import issueCommentCreated from "./issue-comment-created"; import { repositoryDispatch } from "./repository-dispatch"; import { dispatchWorker, dispatchWorkflow, getDefaultBranch } from "../utils/workflow-dispatch"; import { PluginInput } from "../types/plugin"; @@ -19,6 +20,7 @@ function tryCatchWrapper(fn: (event: EmitterWebhookEvent) => unknown) { export function bindHandlers(eventHandler: GitHubEventHandler) { eventHandler.on("repository_dispatch", repositoryDispatch); + eventHandler.on("issue_comment.created", issueCommentCreated); eventHandler.onAny(tryCatchWrapper((event) => handleEvent(event, eventHandler))); // onAny should also receive GithubContext but the types in octokit/webhooks are weird } diff --git a/src/github/handlers/issue-comment-created.ts b/src/github/handlers/issue-comment-created.ts new file mode 100644 index 0000000..8ddc231 --- /dev/null +++ b/src/github/handlers/issue-comment-created.ts @@ -0,0 +1,22 @@ +import { GitHubContext } from "../github-context"; +import { getConfig } from "../utils/config"; + +export default async function issueCommentCreated(context: GitHubContext<"issue_comment.created">) { + const body = context.payload.comment.body.trim(); + if (/^\/help$/.test(body)) { + const comments = ["---", "| name | description | command | example |", "---"]; + console.log(JSON.stringify(context, null, 2)); + const configuration = await getConfig(context); + for (const pluginArray of Object.values(configuration.plugins)) { + for (const plugin of pluginArray) { + comments.push(`| ${plugin.name} | ${plugin.description} | ${plugin.command} | ${plugin.example}`); + } + } + await context.octokit.issues.createComment({ + body: comments.join("\n"), + issue_number: 0, + owner: "", + repo: "", + }); + } +} diff --git a/tests/events.test.ts b/tests/events.test.ts new file mode 100644 index 0000000..86e0518 --- /dev/null +++ b/tests/events.test.ts @@ -0,0 +1,51 @@ +import { afterAll, afterEach, beforeAll, describe, it, mock } from "bun:test"; +import { config } from "dotenv"; +import { GitHubContext } from "../src/github/github-context"; +import { GitHubEventHandler } from "../src/github/github-event-handler"; +import issueCommentCreated from "../src/github/handlers/issue-comment-created"; +import { server } from "./__mocks__/node"; +import { WebhooksMocked } from "./__mocks__/webhooks"; + +void mock.module("@octokit/webhooks", () => ({ + Webhooks: WebhooksMocked, +})); + +config({ path: ".dev.vars" }); + +beforeAll(() => { + server.listen(); +}); +afterEach(() => { + server.resetHandlers(); +}); +afterAll(() => { + server.close(); +}); + +describe("Event related tests", () => { + it("Should post the help menu when /help command is invoked", async () => { + await issueCommentCreated({ + id: "", + key: "issue_comment.created", + octokit: { + rest: { + repos: { + getContent() { + return { data: null }; + }, + }, + }, + }, + eventHandler: {} as GitHubEventHandler, + payload: { + repository: { + owner: { login: "ubiquity" }, + name: "ubiquibot-kernel", + }, + comment: { + body: "/help", + }, + } as unknown as GitHubContext<"issue_comment.created">["payload"], + } as unknown as GitHubContext); + }); +}); From b9b852a870eff724de52e412760569f389bd3055 Mon Sep 17 00:00:00 2001 From: gentlementlegen Date: Tue, 18 Jun 2024 11:07:59 +0200 Subject: [PATCH 02/10] chore: added test for help command --- src/github/handlers/issue-comment-created.ts | 3 +- tests/events.test.ts | 35 ++++++++++++++++++-- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/src/github/handlers/issue-comment-created.ts b/src/github/handlers/issue-comment-created.ts index 8ddc231..c9863c5 100644 --- a/src/github/handlers/issue-comment-created.ts +++ b/src/github/handlers/issue-comment-created.ts @@ -5,11 +5,10 @@ export default async function issueCommentCreated(context: GitHubContext<"issue_ const body = context.payload.comment.body.trim(); if (/^\/help$/.test(body)) { const comments = ["---", "| name | description | command | example |", "---"]; - console.log(JSON.stringify(context, null, 2)); const configuration = await getConfig(context); for (const pluginArray of Object.values(configuration.plugins)) { for (const plugin of pluginArray) { - comments.push(`| ${plugin.name} | ${plugin.description} | ${plugin.command} | ${plugin.example}`); + comments.push(`| ${plugin.name} | ${plugin.description} | \`${plugin.command}\` | \`${plugin.example}\` |`); } } await context.octokit.issues.createComment({ diff --git a/tests/events.test.ts b/tests/events.test.ts index 86e0518..415b077 100644 --- a/tests/events.test.ts +++ b/tests/events.test.ts @@ -1,4 +1,5 @@ -import { afterAll, afterEach, beforeAll, describe, it, mock } from "bun:test"; +import { RestEndpointMethodTypes } from "@octokit/plugin-rest-endpoint-methods"; +import { afterAll, afterEach, beforeAll, describe, expect, it, mock, spyOn } from "bun:test"; import { config } from "dotenv"; import { GitHubContext } from "../src/github/github-context"; import { GitHubEventHandler } from "../src/github/github-event-handler"; @@ -24,14 +25,33 @@ afterAll(() => { describe("Event related tests", () => { it("Should post the help menu when /help command is invoked", async () => { + const issues = { + createComment(params?: RestEndpointMethodTypes["issues"]["createComment"]["parameters"]) { + return params; + }, + }; + const spy = spyOn(issues, "createComment"); await issueCommentCreated({ id: "", key: "issue_comment.created", octokit: { + issues, rest: { repos: { getContent() { - return { data: null }; + return { + data: ` + plugins: + issue_comment.created: + - name: "Run on comment created" + description: "Plugin A" + example: /command foobar + command: /command + uses: + - id: plugin-A + plugin: https://plugin-a.internal + `, + }; }, }, }, @@ -47,5 +67,16 @@ describe("Event related tests", () => { }, } as unknown as GitHubContext<"issue_comment.created">["payload"], } as unknown as GitHubContext); + expect(spy).toBeCalledTimes(1); + expect(spy.mock.calls).toEqual([ + [ + { + body: "---\n| name | description | command | example |\n---\n| Run on comment created | Plugin A | `/command` | `/command foobar` |", + issue_number: 0, + owner: "", + repo: "", + }, + ], + ]); }); }); From e4f3155d5600014b263f4ee4808acddb32a47df3 Mon Sep 17 00:00:00 2001 From: gentlementlegen Date: Tue, 18 Jun 2024 11:10:26 +0200 Subject: [PATCH 03/10] chore: using real values to post help comment --- src/github/handlers/issue-comment-created.ts | 6 +++--- tests/events.test.ts | 7 ++++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/github/handlers/issue-comment-created.ts b/src/github/handlers/issue-comment-created.ts index c9863c5..3a1c1e7 100644 --- a/src/github/handlers/issue-comment-created.ts +++ b/src/github/handlers/issue-comment-created.ts @@ -13,9 +13,9 @@ export default async function issueCommentCreated(context: GitHubContext<"issue_ } await context.octokit.issues.createComment({ body: comments.join("\n"), - issue_number: 0, - owner: "", - repo: "", + issue_number: context.payload.issue.number, + owner: context.payload.repository.owner.login, + repo: context.payload.repository.name, }); } } diff --git a/tests/events.test.ts b/tests/events.test.ts index 415b077..4d38b3c 100644 --- a/tests/events.test.ts +++ b/tests/events.test.ts @@ -62,6 +62,7 @@ describe("Event related tests", () => { owner: { login: "ubiquity" }, name: "ubiquibot-kernel", }, + issue: { number: 1 }, comment: { body: "/help", }, @@ -72,9 +73,9 @@ describe("Event related tests", () => { [ { body: "---\n| name | description | command | example |\n---\n| Run on comment created | Plugin A | `/command` | `/command foobar` |", - issue_number: 0, - owner: "", - repo: "", + issue_number: 1, + owner: "ubiquity", + repo: "ubiquibot-kernel", }, ], ]); From dbdde4d0eeba2e3f20eb24fc39ad0c5eae62fec6 Mon Sep 17 00:00:00 2001 From: gentlementlegen Date: Tue, 18 Jun 2024 11:56:18 +0200 Subject: [PATCH 04/10] chore: display empty when no content --- src/github/handlers/issue-comment-created.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/github/handlers/issue-comment-created.ts b/src/github/handlers/issue-comment-created.ts index 3a1c1e7..cdbf2ec 100644 --- a/src/github/handlers/issue-comment-created.ts +++ b/src/github/handlers/issue-comment-created.ts @@ -4,11 +4,13 @@ import { getConfig } from "../utils/config"; export default async function issueCommentCreated(context: GitHubContext<"issue_comment.created">) { const body = context.payload.comment.body.trim(); if (/^\/help$/.test(body)) { - const comments = ["---", "| name | description | command | example |", "---"]; + const comments = ["| Name | Description | Command | Example |", "|---|---|---|---|"]; const configuration = await getConfig(context); for (const pluginArray of Object.values(configuration.plugins)) { for (const plugin of pluginArray) { - comments.push(`| ${plugin.name} | ${plugin.description} | \`${plugin.command}\` | \`${plugin.example}\` |`); + if (plugin.name) { + comments.push(`| ${plugin.name} | ${getContent(plugin.description)} | \`${getContent(plugin.command)}\` | \`${getContent(plugin.example)}\` |`); + } } } await context.octokit.issues.createComment({ @@ -19,3 +21,7 @@ export default async function issueCommentCreated(context: GitHubContext<"issue_ }); } } + +function getContent(content: string | undefined) { + return content || "-"; +} From 7b6d582806e7a959e2c8fbc4cf000b824dbd1868 Mon Sep 17 00:00:00 2001 From: gentlementlegen Date: Tue, 18 Jun 2024 12:01:44 +0200 Subject: [PATCH 05/10] chore: changed display condition for commands --- src/github/handlers/issue-comment-created.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/github/handlers/issue-comment-created.ts b/src/github/handlers/issue-comment-created.ts index cdbf2ec..c95b129 100644 --- a/src/github/handlers/issue-comment-created.ts +++ b/src/github/handlers/issue-comment-created.ts @@ -8,8 +8,11 @@ export default async function issueCommentCreated(context: GitHubContext<"issue_ const configuration = await getConfig(context); for (const pluginArray of Object.values(configuration.plugins)) { for (const plugin of pluginArray) { - if (plugin.name) { - comments.push(`| ${plugin.name} | ${getContent(plugin.description)} | \`${getContent(plugin.command)}\` | \`${getContent(plugin.example)}\` |`); + // Only show plugins that have commands available for the user + if (plugin.command) { + comments.push( + `| ${getContent(plugin.name)} | ${getContent(plugin.description)} | \`${getContent(plugin.command)}\` | \`${getContent(plugin.example)}\` |` + ); } } } From 25629fe0f73880f483cefeb5810f17aa0470d4fb Mon Sep 17 00:00:00 2001 From: gentlementlegen Date: Tue, 18 Jun 2024 12:11:22 +0200 Subject: [PATCH 06/10] chore: added default help command in description --- src/github/handlers/issue-comment-created.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/github/handlers/issue-comment-created.ts b/src/github/handlers/issue-comment-created.ts index c95b129..cffb570 100644 --- a/src/github/handlers/issue-comment-created.ts +++ b/src/github/handlers/issue-comment-created.ts @@ -4,15 +4,13 @@ import { getConfig } from "../utils/config"; export default async function issueCommentCreated(context: GitHubContext<"issue_comment.created">) { const body = context.payload.comment.body.trim(); if (/^\/help$/.test(body)) { - const comments = ["| Name | Description | Command | Example |", "|---|---|---|---|"]; + const comments = ["| Command | Description | Example |", "|---|---|---|---|", "| `/help` | List all available commands. | `/help` |"]; const configuration = await getConfig(context); for (const pluginArray of Object.values(configuration.plugins)) { for (const plugin of pluginArray) { // Only show plugins that have commands available for the user if (plugin.command) { - comments.push( - `| ${getContent(plugin.name)} | ${getContent(plugin.description)} | \`${getContent(plugin.command)}\` | \`${getContent(plugin.example)}\` |` - ); + comments.push(`| \`${getContent(plugin.command)}\` | ${getContent(plugin.description)} | \`${getContent(plugin.example)}\` |`); } } } From 7227fdbd368c4cd62eaa992f1e3ce55f74b1bfc4 Mon Sep 17 00:00:00 2001 From: gentlementlegen Date: Tue, 18 Jun 2024 12:12:13 +0200 Subject: [PATCH 07/10] chore: added title --- src/github/handlers/issue-comment-created.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/github/handlers/issue-comment-created.ts b/src/github/handlers/issue-comment-created.ts index cffb570..715f148 100644 --- a/src/github/handlers/issue-comment-created.ts +++ b/src/github/handlers/issue-comment-created.ts @@ -4,7 +4,12 @@ import { getConfig } from "../utils/config"; export default async function issueCommentCreated(context: GitHubContext<"issue_comment.created">) { const body = context.payload.comment.body.trim(); if (/^\/help$/.test(body)) { - const comments = ["| Command | Description | Example |", "|---|---|---|---|", "| `/help` | List all available commands. | `/help` |"]; + const comments = [ + "### Available Commands\n\n", + "| Command | Description | Example |", + "|---|---|---|---|", + "| `/help` | List all available commands. | `/help` |", + ]; const configuration = await getConfig(context); for (const pluginArray of Object.values(configuration.plugins)) { for (const plugin of pluginArray) { From 1a26d932ab8a2edfea3ad69ab642bf81aad95c89 Mon Sep 17 00:00:00 2001 From: gentlementlegen Date: Tue, 18 Jun 2024 12:12:37 +0200 Subject: [PATCH 08/10] chore: fixed table display --- src/github/handlers/issue-comment-created.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/github/handlers/issue-comment-created.ts b/src/github/handlers/issue-comment-created.ts index 715f148..c999e94 100644 --- a/src/github/handlers/issue-comment-created.ts +++ b/src/github/handlers/issue-comment-created.ts @@ -7,7 +7,7 @@ export default async function issueCommentCreated(context: GitHubContext<"issue_ const comments = [ "### Available Commands\n\n", "| Command | Description | Example |", - "|---|---|---|---|", + "|---|---|---|", "| `/help` | List all available commands. | `/help` |", ]; const configuration = await getConfig(context); From 05c505ceac36c206b0c25145dc123595940fb9a2 Mon Sep 17 00:00:00 2001 From: gentlementlegen Date: Tue, 18 Jun 2024 12:20:51 +0200 Subject: [PATCH 09/10] fix: added MD escape for content --- src/github/handlers/issue-comment-created.ts | 31 +++++++++++++++++++- tests/events.test.ts | 4 +-- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/github/handlers/issue-comment-created.ts b/src/github/handlers/issue-comment-created.ts index c999e94..66b77c2 100644 --- a/src/github/handlers/issue-comment-created.ts +++ b/src/github/handlers/issue-comment-created.ts @@ -28,6 +28,35 @@ export default async function issueCommentCreated(context: GitHubContext<"issue_ } } +/** + * Ensures that passed content does not break MD display within the table. + */ +function escapeMarkdown(text: string) { + const replacements = { + "\\": "\\\\", + "`": "\\`", + "*": "\\*", + _: "\\_", + "{": "\\{", + "}": "\\}", + "[": "\\[", + "]": "\\]", + "(": "\\(", + ")": "\\)", + "#": "\\#", + "+": "\\+", + "-": "\\-", + ".": "\\.", + "!": "\\!", + "|": "\\|", + "~": "\\~", + ">": "\\>", + "<": "\\<", + }; + + return text.replace(/[\\`*_{}[\]()#+\-.!|~><]/g, (match) => replacements[match as keyof typeof replacements]); +} + function getContent(content: string | undefined) { - return content || "-"; + return content ? escapeMarkdown(content) : "-"; } diff --git a/tests/events.test.ts b/tests/events.test.ts index 4d38b3c..4e88660 100644 --- a/tests/events.test.ts +++ b/tests/events.test.ts @@ -45,7 +45,7 @@ describe("Event related tests", () => { issue_comment.created: - name: "Run on comment created" description: "Plugin A" - example: /command foobar + example: /command [foo | bar] command: /command uses: - id: plugin-A @@ -72,7 +72,7 @@ describe("Event related tests", () => { expect(spy.mock.calls).toEqual([ [ { - body: "---\n| name | description | command | example |\n---\n| Run on comment created | Plugin A | `/command` | `/command foobar` |", + body: "### Available Commands\n\n\n| Command | Description | Example |\n|---|---|---|\n| `/help` | List all available commands. | `/help` |\n| `/command` | Plugin A | `/command \\[foo \\| bar\\]` |", issue_number: 1, owner: "ubiquity", repo: "ubiquibot-kernel", From 6e2497385c688c8b459cbd3032a84df77e2941bd Mon Sep 17 00:00:00 2001 From: gentlementlegen Date: Tue, 18 Jun 2024 12:26:27 +0200 Subject: [PATCH 10/10] fix: escape only pipes --- src/github/handlers/issue-comment-created.ts | 28 +------------------- tests/events.test.ts | 4 ++- 2 files changed, 4 insertions(+), 28 deletions(-) diff --git a/src/github/handlers/issue-comment-created.ts b/src/github/handlers/issue-comment-created.ts index 66b77c2..45d4a52 100644 --- a/src/github/handlers/issue-comment-created.ts +++ b/src/github/handlers/issue-comment-created.ts @@ -31,32 +31,6 @@ export default async function issueCommentCreated(context: GitHubContext<"issue_ /** * Ensures that passed content does not break MD display within the table. */ -function escapeMarkdown(text: string) { - const replacements = { - "\\": "\\\\", - "`": "\\`", - "*": "\\*", - _: "\\_", - "{": "\\{", - "}": "\\}", - "[": "\\[", - "]": "\\]", - "(": "\\(", - ")": "\\)", - "#": "\\#", - "+": "\\+", - "-": "\\-", - ".": "\\.", - "!": "\\!", - "|": "\\|", - "~": "\\~", - ">": "\\>", - "<": "\\<", - }; - - return text.replace(/[\\`*_{}[\]()#+\-.!|~><]/g, (match) => replacements[match as keyof typeof replacements]); -} - function getContent(content: string | undefined) { - return content ? escapeMarkdown(content) : "-"; + return content ? content.replace("|", "\\|") : "-"; } diff --git a/tests/events.test.ts b/tests/events.test.ts index 4e88660..5180559 100644 --- a/tests/events.test.ts +++ b/tests/events.test.ts @@ -72,7 +72,9 @@ describe("Event related tests", () => { expect(spy.mock.calls).toEqual([ [ { - body: "### Available Commands\n\n\n| Command | Description | Example |\n|---|---|---|\n| `/help` | List all available commands. | `/help` |\n| `/command` | Plugin A | `/command \\[foo \\| bar\\]` |", + body: + "### Available Commands\n\n\n| Command | Description | Example |\n|---|---|---|\n| `/help` | List" + + " all available commands. | `/help` |\n| `/command` | Plugin A | `/command [foo \\| bar]` |", issue_number: 1, owner: "ubiquity", repo: "ubiquibot-kernel",