diff --git a/.gitignore b/.gitignore index 48cd346..dcab865 100644 --- a/.gitignore +++ b/.gitignore @@ -114,8 +114,8 @@ node_modules .yarn .pnp.cjs .pnp.loader.mjs -.env static/dist keys -coverage junit.xml + +*.pem diff --git a/bun.lockb b/bun.lockb index c8d024d..c0d11df 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 7d7cd3e..5f6cbd6 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ }, "scripts": { "dev": "run-p worker proxy", - "predev": "lsof -i tcp:8787 | grep LISTEN | awk '{print $2}' | (if [ -n \"$(awk '{print $2}')\" ]; then xargs kill -9; fi)", + "predev": "tsx predev.ts", "format": "run-s format:lint format:prettier format:cspell", "format:lint": "eslint --fix .", "format:prettier": "prettier --write .", @@ -41,6 +41,7 @@ "@octokit/types": "^12.6.0", "@octokit/webhooks": "^12.0.10", "@sinclair/typebox": "^0.32.5", + "@ubiquibot/configuration": "2.1.0", "dotenv": "^16.4.4", "smee-client": "^2.0.0", "yaml": "^2.4.1" @@ -85,5 +86,5 @@ "@commitlint/config-conventional" ] }, - "packageManager": "bun@1.0.23" + "packageManager": "bun@1.1.0" } diff --git a/predev.ts b/predev.ts new file mode 100644 index 0000000..419c52e --- /dev/null +++ b/predev.ts @@ -0,0 +1,30 @@ +import { exec } from "child_process"; + +const port = "8787"; + +const isWindows = process.platform === "win32"; + +const command = isWindows ? `netstat -ano | findstr LISTENING | findstr :${port}` : `lsof -i tcp:${port} | grep LISTEN | awk '{print $2}'`; + +exec(command, (error, stdout) => { + if (error) { + // The command will also fail on Windows if the process doesn't exist which is expected + console.error(`Error executing command: ${error.message}`); + return; + } + + const pid = isWindows ? stdout.trim().split(/\s+/)[4] : stdout.trim(); + + if (pid) { + const killCommand = isWindows ? `taskkill /F /PID ${pid}` : `kill -9 ${pid}`; + exec(killCommand, (error) => { + if (error) { + console.error(`Error killing process: ${error.message}`); + return; + } + console.log(`Process ${pid} killed successfully.`); + }); + } else { + console.log("No process found listening on port 8787."); + } +}); diff --git a/src/github/types/config.ts b/src/github/types/plugin-configuration.ts similarity index 87% rename from src/github/types/config.ts rename to src/github/types/plugin-configuration.ts index 58e153d..b750f26 100644 --- a/src/github/types/config.ts +++ b/src/github/types/plugin-configuration.ts @@ -2,7 +2,7 @@ import { Type as T } from "@sinclair/typebox"; import { StaticDecode } from "@sinclair/typebox"; import { githubWebhookEvents } from "./webhook-events"; -const pluginNameRegex = new RegExp("^([0-9a-zA-Z-._]+)/([0-9a-zA-Z-._]+)(?::([0-9a-zA-Z-._]+))?(?:@([0-9a-zA-Z-._]+))?$"); +const pluginNameRegex = new RegExp("^([0-9a-zA-Z-._]+)\\/([0-9a-zA-Z-._]+)(?::([0-9a-zA-Z-._]+))?(?:@([0-9a-zA-Z-._]+(?:\\/[0-9a-zA-Z-._]+)?))?$"); type GithubPlugin = { owner: string; @@ -58,4 +58,4 @@ export const configSchema = T.Object({ plugins: T.Record(T.Enum(githubWebhookEvents), handlerSchema, { default: {} }), }); -export type Config = StaticDecode; +export type PluginConfiguration = StaticDecode; diff --git a/src/github/types/plugin.ts b/src/github/types/plugin.ts index f9c4ec2..316e0ea 100644 --- a/src/github/types/plugin.ts +++ b/src/github/types/plugin.ts @@ -1,6 +1,6 @@ import { EmitterWebhookEvent, EmitterWebhookEventName } from "@octokit/webhooks"; -import { PluginChain } from "./config"; import { StaticDecode, Type } from "@sinclair/typebox"; +import { PluginChain } from "./plugin-configuration"; export const expressionRegex = /^\s*\${{\s*(\S+)\s*}}\s*$/; diff --git a/src/github/utils/config.ts b/src/github/utils/config.ts index ea7ad77..f1d0584 100644 --- a/src/github/utils/config.ts +++ b/src/github/utils/config.ts @@ -1,15 +1,20 @@ import { Value } from "@sinclair/typebox/value"; import { GitHubContext } from "../github-context"; import YAML from "yaml"; -import { Config, configSchema } from "../types/config"; import { expressionRegex } from "../types/plugin"; +import { configSchema, PluginConfiguration } from "../types/plugin-configuration"; import { eventNames } from "../types/webhook-events"; +import { BotConfig, generateConfiguration } from "@ubiquibot/configuration"; -const UBIQUIBOT_CONFIG_FULL_PATH = ".github/ubiquibot-config.yml"; +const UBIQUIBOT_CONFIG_FULL_PATH = ".github/.ubiquibot-config.yml"; -export async function getConfig(context: GitHubContext): Promise { +export async function getConfig(context: GitHubContext): Promise { const payload = context.payload; - if (!("repository" in payload) || !payload.repository) throw new Error("Repository is not defined"); + const defaultConfiguration = generateConfiguration(); + if (!("repository" in payload) || !payload.repository) { + console.warn("Repository is not defined"); + return defaultConfiguration; + } const _repoConfig = parseYaml( await download({ @@ -18,9 +23,9 @@ export async function getConfig(context: GitHubContext): Promise owner: payload.repository.owner.login, }) ); - if (!_repoConfig) return null; + if (!_repoConfig) return defaultConfiguration; - let config: Config; + let config: PluginConfiguration; try { config = Value.Decode(configSchema, Value.Default(configSchema, _repoConfig)); } catch (error) { @@ -30,10 +35,10 @@ export async function getConfig(context: GitHubContext): Promise checkPluginChains(config); - return config; + return generateConfiguration(config as BotConfig); } -function checkPluginChains(config: Config) { +function checkPluginChains(config: PluginConfiguration) { for (const eventName of eventNames) { const plugins = config.plugins[eventName]; for (const plugin of plugins) { @@ -43,7 +48,7 @@ function checkPluginChains(config: Config) { } } -function checkPluginChainUniqueIds(plugin: Config["plugins"]["*"][0]) { +function checkPluginChainUniqueIds(plugin: PluginConfiguration["plugins"]["*"][0]) { const allIds = new Set(); for (const use of plugin.uses) { if (!use.id) continue; @@ -56,7 +61,7 @@ function checkPluginChainUniqueIds(plugin: Config["plugins"]["*"][0]) { return allIds; } -function checkPluginChainExpressions(plugin: Config["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; @@ -101,6 +106,7 @@ async function download({ context, repository, owner }: { context: GitHubContext }); return data as unknown as string; // this will be a string if media format is raw } catch (err) { + console.error(err); return null; } }