diff --git a/bun.lockb b/bun.lockb index 6b04d52..7ed8c7b 100644 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index d8e556a..84199e4 100644 --- a/package.json +++ b/package.json @@ -37,12 +37,12 @@ "dependencies": { "@octokit/auth-app": "7.1.0", "@octokit/core": "6.1.2", - "@octokit/plugin-paginate-rest": "11.3.0", - "@octokit/plugin-rest-endpoint-methods": "13.2.1", + "@octokit/plugin-paginate-rest": "11.3.3", + "@octokit/plugin-rest-endpoint-methods": "13.2.4", "@octokit/plugin-retry": "7.1.1", "@octokit/plugin-throttling": "9.3.0", "@octokit/types": "13.5.0", - "@octokit/webhooks": "13.2.7", + "@octokit/webhooks": "13.2.8", "@octokit/webhooks-types": "7.5.1", "@sinclair/typebox": "0.32.33", "dotenv": "16.4.5", diff --git a/src/github/handlers/help-command.ts b/src/github/handlers/help-command.ts index 561b892..8d36ec9 100644 --- a/src/github/handlers/help-command.ts +++ b/src/github/handlers/help-command.ts @@ -1,12 +1,13 @@ import { getConfig } from "../utils/config"; -import { GithubPlugin, isGithubPlugin } from "../types/plugin-configuration"; +import { GithubPlugin } from "../types/plugin-configuration"; import { GitHubContext } from "../github-context"; -import { Manifest, manifestSchema, manifestValidator } from "../../types/manifest"; +import { manifestSchema, manifestValidator } from "../../types/manifest"; import { Value } from "@sinclair/typebox/value"; +import { getManifest } from "../utils/plugins"; async function parseCommandsFromManifest(context: GitHubContext<"issue_comment.created">, plugin: string | GithubPlugin) { const commands: string[] = []; - const manifest = await (isGithubPlugin(plugin) ? fetchActionManifest(context, plugin) : fetchWorkerManifest(plugin)); + const manifest = await getManifest(context, plugin); if (manifest) { Value.Default(manifestSchema, manifest); const errors = manifestValidator.testReturningErrors(manifest); @@ -18,7 +19,7 @@ async function parseCommandsFromManifest(context: GitHubContext<"issue_comment.c } else { if (manifest?.commands) { for (const [key, value] of Object.entries(manifest.commands)) { - commands.push(`| \`/${getContent(key)}\` | ${getContent(value.description)} | \`${getContent(value["ubiquity:example"])}\` |`); + commands.push(`| \`/${getContent(key)}\` | ${getContent(value.description)} | \`${getContent(value["ubiquibot:example"])}\` |`); } } } @@ -55,31 +56,3 @@ export async function postHelpCommand(context: GitHubContext<"issue_comment.crea function getContent(content: string | undefined) { return content ? content.replace("|", "\\|") : "-"; } - -async function fetchActionManifest(context: GitHubContext<"issue_comment.created">, { owner, repo }: GithubPlugin): Promise { - try { - const { data } = await context.octokit.repos.getContent({ - owner, - repo, - path: "manifest.json", - }); - if ("content" in data) { - const content = Buffer.from(data.content, "base64").toString(); - return JSON.parse(content); - } - } catch (e) { - console.warn(`Could not find a manifest for ${owner}/${repo}: ${e}`); - } - return null; -} - -async function fetchWorkerManifest(url: string): Promise { - const manifestUrl = `${url}/manifest.json`; - try { - const result = await fetch(manifestUrl); - return await result.json(); - } catch (e) { - console.warn(`Could not find a manifest for ${manifestUrl}: ${e}`); - } - return null; -} diff --git a/src/github/handlers/index.ts b/src/github/handlers/index.ts index 44835fa..3cd3635 100644 --- a/src/github/handlers/index.ts +++ b/src/github/handlers/index.ts @@ -7,6 +7,7 @@ import { repositoryDispatch } from "./repository-dispatch"; import { dispatchWorker, dispatchWorkflow, getDefaultBranch } from "../utils/workflow-dispatch"; import { PluginInput } from "../types/plugin"; import { isGithubPlugin, PluginConfiguration } from "../types/plugin-configuration"; +import { getPluginsForEvent } from "../utils/plugins"; function tryCatchWrapper(fn: (event: EmitterWebhookEvent) => unknown) { return async (event: EmitterWebhookEvent) => { @@ -57,13 +58,7 @@ async function handleEvent(event: EmitterWebhookEvent, eventHandler: InstanceTyp return; } - const pluginChains = config.plugins.filter((plugin) => { - console.log("Plugin runs on", plugin.name, plugin.runsOn); - if (plugin.runsOn) { - return plugin.runsOn.includes(event.name); - } - return false; - }); + const pluginChains = getPluginsForEvent(config.plugins, event.key); if (pluginChains.length === 0) { console.log(`No handler found for event ${event.name}`); diff --git a/src/github/types/plugin-configuration.ts b/src/github/types/plugin-configuration.ts index 31c80ad..98f4e5d 100644 --- a/src/github/types/plugin-configuration.ts +++ b/src/github/types/plugin-configuration.ts @@ -51,6 +51,7 @@ const pluginChainSchema = T.Array( id: T.Optional(T.String()), plugin: githubPluginType(), with: T.Record(T.String(), T.Unknown(), { default: {} }), + runsOn: T.Optional(T.Array(runEvent, { default: [] })), }), { minItems: 1, default: [] } ); @@ -65,7 +66,6 @@ const handlerSchema = T.Array( example: T.Optional(T.String()), uses: pluginChainSchema, skipBotEvents: T.Boolean({ default: true }), - runsOn: T.Optional(T.Array(runEvent, { default: [] })), }), { default: [] } ); diff --git a/src/github/utils/config.ts b/src/github/utils/config.ts index c3a563a..214b1b6 100644 --- a/src/github/utils/config.ts +++ b/src/github/utils/config.ts @@ -4,6 +4,7 @@ import { GitHubContext } from "../github-context"; import { expressionRegex } from "../types/plugin"; import { configSchema, configSchemaValidator, PluginConfiguration } from "../types/plugin-configuration"; import { eventNames } from "../types/webhook-events"; +import { getManifest, getPluginsForEvent } from "./plugins"; const UBIQUIBOT_CONFIG_FULL_PATH = ".github/.ubiquibot-config.yml"; const UBIQUIBOT_CONFIG_ORG_REPO = "ubiquibot-config"; @@ -39,11 +40,8 @@ async function getConfigurationFromRepo(context: GitHubContext, repository: stri */ function mergeConfigurations(configuration1: PluginConfiguration, configuration2: PluginConfiguration): PluginConfiguration { const mergedConfiguration = { ...configuration1 }; - for (const key of Object.keys(configuration2.plugins)) { - const pluginKey = key as keyof PluginConfiguration["plugins"]; - if (configuration2.plugins[pluginKey]?.length) { - mergedConfiguration.plugins[pluginKey] = configuration2.plugins[pluginKey]; - } + if (configuration2.plugins?.length) { + mergedConfiguration.plugins = configuration2.plugins; } return mergedConfiguration; } @@ -75,12 +73,20 @@ export async function getConfig(context: GitHubContext): Promise(); for (const use of plugin.uses) { if (!use.id) continue; @@ -101,7 +107,7 @@ function checkPluginChainUniqueIds(plugin: PluginConfiguration["plugins"]["*"][0 return allIds; } -function checkPluginChainExpressions(plugin: PluginConfiguration["plugins"]["*"][0], allIds: Set) { +function checkPluginChainExpressions(plugin: PluginConfiguration["plugins"][0], allIds: Set) { const calledIds = new Set(); for (const use of plugin.uses) { if (!use.id) continue; diff --git a/src/github/utils/plugins.ts b/src/github/utils/plugins.ts new file mode 100644 index 0000000..e0ba200 --- /dev/null +++ b/src/github/utils/plugins.ts @@ -0,0 +1,55 @@ +import { GithubPlugin, isGithubPlugin, PluginConfiguration } from "../types/plugin-configuration"; +import { EmitterWebhookEventName } from "@octokit/webhooks"; +import { GitHubContext } from "../github-context"; +import { Manifest } from "../../types/manifest"; + +const _manifestCache: Record = {}; + +export function getPluginsForEvent(plugins: PluginConfiguration["plugins"], event: EmitterWebhookEventName) { + return plugins.filter((plugin) => { + return plugin.uses?.[0].runsOn?.includes(event); + }); +} + +export function getManifest(context: GitHubContext, plugin: string | GithubPlugin) { + return isGithubPlugin(plugin) ? fetchActionManifest(context, plugin) : fetchWorkerManifest(plugin); +} + +async function fetchActionManifest(context: GitHubContext<"issue_comment.created">, { owner, repo }: GithubPlugin): Promise { + const manifestKey = `${owner}:${repo}`; + if (_manifestCache[manifestKey]) { + return _manifestCache[manifestKey]; + } + try { + const { data } = await context.octokit.repos.getContent({ + owner, + repo, + path: "manifest.json", + }); + if ("content" in data) { + const content = Buffer.from(data.content, "base64").toString(); + const manifest = JSON.parse(content) as Manifest; + _manifestCache[manifestKey] = manifest; + return manifest; + } + } catch (e) { + console.warn(`Could not find a manifest for ${owner}/${repo}: ${e}`); + } + return null; +} + +async function fetchWorkerManifest(url: string): Promise { + if (_manifestCache[url]) { + return _manifestCache[url]; + } + const manifestUrl = `${url}/manifest.json`; + try { + const result = await fetch(manifestUrl); + const manifest = (await result.json()) as Manifest; + _manifestCache[url] = manifest; + return manifest; + } catch (e) { + console.warn(`Could not find a manifest for ${manifestUrl}: ${e}`); + } + return null; +} diff --git a/src/types/manifest.ts b/src/types/manifest.ts index 49a896c..2c9aa19 100644 --- a/src/types/manifest.ts +++ b/src/types/manifest.ts @@ -1,19 +1,19 @@ import { type Static, Type as T } from "@sinclair/typebox"; import { StandardValidator } from "typebox-validators"; -import { emitterEventNames } from "@octokit/webhooks/dist-types/generated/webhook-names"; +import { emitterEventNames } from "@octokit/webhooks"; export const runEvent = T.Union(emitterEventNames.map((o) => T.Literal(o))); export const commandSchema = T.Object({ description: T.String({ minLength: 1 }), - "ubiquity:example": T.String({ minLength: 1 }), + "ubiquibot:example": T.String({ minLength: 1 }), }); export const manifestSchema = T.Object({ name: T.String({ minLength: 1 }), description: T.String({ minLength: 1 }), commands: T.Record(T.String(), commandSchema), - "ubiquity:runsOn": T.Optional(T.Array(runEvent, { default: [] })), + "ubiquibot:listeners": T.Optional(T.Array(runEvent, { default: [] })), }); export const manifestValidator = new StandardValidator(manifestSchema);