Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SDK and command interface #27

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/knip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const config: KnipConfig = {
ignore: ["src/types/config.ts", "**/__mocks__/**", "**/__fixtures__/**"],
ignoreExportsUsedInFile: true,
// eslint can also be safely ignored as per the docs: https://knip.dev/guides/handling-issues#eslint--jest
ignoreDependencies: ["eslint-config-prettier", "eslint-plugin-prettier"],
ignoreDependencies: ["eslint-config-prettier", "eslint-plugin-prettier", "ts-node"],
eslint: true,
};

Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/compute.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ on:
description: "Ref"
signature:
description: "The kernel signature"
command:
description: "Command"

jobs:
compute:
Expand Down
10 changes: 0 additions & 10 deletions jest.config.json

This file was deleted.

27 changes: 27 additions & 0 deletions jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import type { Config } from "jest";

const cfg: Config = {
transform: {
"^.+\\.tsx?$": [
"ts-jest",
{
useESM: true,
},
],
},
moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
coveragePathIgnorePatterns: ["node_modules", "mocks"],
collectCoverage: true,
coverageReporters: ["json", "lcov", "text", "clover", "json-summary"],
reporters: ["default", "jest-junit", "jest-md-dashboard"],
coverageDirectory: "coverage",
testTimeout: 20000,
roots: ["<rootDir>", "tests"],
extensionsToTreatAsEsm: [".ts"],
moduleNameMapper: {
"^(\\.{1,2}/.*)\\.js$": "$1",
},
setupFilesAfterEnv: ["dotenv/config"],
};

export default cfg;
44 changes: 21 additions & 23 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "Conditionally merge pull requests after a timeout.",
"author": "Ubiquity DAO",
"license": "MIT",
"main": "src/index.ts",
"main": "src/action.ts",
"engines": {
"node": ">=20.10.0"
},
Expand All @@ -16,8 +16,8 @@
"knip": "knip --config .github/knip.ts",
"knip-ci": "knip --no-exit-code --reporter json --config .github/knip.ts",
"prepare": "husky install",
"start": "tsx src/index.ts",
"test": "jest --setupFiles dotenv/config --coverage"
"start": "tsx src/action.ts",
"test": "cross-env NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\" jest --setupFiles dotenv/config --coverage"
},
"keywords": [
"typescript",
Expand All @@ -27,33 +27,29 @@
"open-source"
],
"dependencies": {
"@actions/core": "1.10.1",
"@actions/github": "6.0.0",
"@octokit/plugin-paginate-rest": "11.3.1",
"@octokit/rest": "20.1.1",
"@octokit/webhooks": "13.2.7",
"@sinclair/typebox": "0.32.33",
"@ubiquity-dao/ubiquibot-logger": "1.3.1",
"@actions/core": "^1.11.1",
"@octokit/plugin-rest-endpoint-methods": "^13.2.6",
"@sinclair/typebox": "0.34.3",
"@ubiquity-os/plugin-sdk": "^1.1.0",
"@ubiquity-os/ubiquity-os-logger": "1.3.2",
"dotenv": "16.4.5",
"ms": "2.1.3",
"reflect-metadata": "0.2.2",
"ts-retry": "4.2.5",
"tsx": "4.16.2",
"typebox-validators": "0.3.5"
"ts-retry": "4.2.5"
},
"devDependencies": {
"@commitlint/cli": "19.3.0",
"@commitlint/config-conventional": "19.2.2",
"@cspell/dict-node": "5.0.1",
"@cspell/dict-software-terms": "3.4.6",
"@cspell/dict-typescript": "3.1.5",
"@eslint/js": "9.5.0",
"@commitlint/cli": "^19.5.0",
"@commitlint/config-conventional": "^19.5.0",
"@cspell/dict-node": "^5.0.5",
"@cspell/dict-software-terms": "^4.1.15",
"@cspell/dict-typescript": "^3.1.2",
"@eslint/js": "9.14.0",
"@jest/globals": "29.7.0",
"@mswjs/data": "0.16.1",
"@types/ms": "0.7.34",
"@types/node": "20.14.5",
"cross-env": "^7.0.3",
"cspell": "8.9.0",
"eslint": "9.5.0",
"eslint": "9.14.0",
"eslint-config-prettier": "9.1.0",
"eslint-plugin-check-file": "2.8.0",
"eslint-plugin-prettier": "5.1.3",
Expand All @@ -68,8 +64,10 @@
"npm-run-all": "4.1.5",
"prettier": "3.3.2",
"ts-jest": "29.1.5",
"ts-node": "10.9.2",
"tsx": "4.16.2",
"typescript": "5.6.2",
"typescript-eslint": "8.8.0"
"typescript-eslint": "8.14.0"
},
"lint-staged": {
"*.ts": [
Expand All @@ -86,4 +84,4 @@
]
},
"packageManager": "yarn@1.22.22"
}
}
55 changes: 17 additions & 38 deletions src/action.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,19 @@
import * as github from "@actions/github";
import { Octokit } from "@octokit/rest";
import { validateAndDecodeSchemas } from "./helpers/validator";
import { createActionsPlugin } from "@ubiquity-os/plugin-sdk";
import { SupportedEvents } from "./types/context";
import { Env, envSchema } from "./types/env";
import { PluginSettings, pluginSettingsSchema } from "./types/plugin-inputs";
import { plugin } from "./plugin";
import { PluginInputs } from "./types";
import { LogLevel } from "@ubiquity-os/ubiquity-os-logger";

/**
* How a GitHub action executes the plugin.
*/
export async function run() {
const payload = github.context.payload.inputs;

payload.env = { ...(payload.env || {}), workflowName: github.context.workflow };
const { decodedSettings, decodedEnv } = validateAndDecodeSchemas(payload.env, JSON.parse(payload.settings));
const inputs: PluginInputs = {
stateId: payload.stateId,
eventName: payload.eventName,
eventPayload: JSON.parse(payload.eventPayload),
settings: decodedSettings,
authToken: payload.authToken,
ref: payload.ref,
};

await plugin(inputs, decodedEnv);

return returnDataToKernel(process.env.GITHUB_TOKEN, inputs.stateId, {});
}

export async function returnDataToKernel(repoToken: string, stateId: string, output: object, eventType = "return-data-to-ubiquity-os-kernel") {
const octokit = new Octokit({ auth: repoToken });
return octokit.repos.createDispatchEvent({
owner: github.context.repo.owner,
repo: github.context.repo.repo,
event_type: eventType,
client_payload: {
state_id: stateId,
output: JSON.stringify(output),
},
});
}
createActionsPlugin<PluginSettings, Env, null, SupportedEvents>(
(context) => {
return plugin(context);
},
{
envSchema: envSchema,
postCommentOnError: true,
settingsSchema: pluginSettingsSchema,
logLevel: (process.env.LOG_LEVEL as LogLevel) ?? "info",
kernelPublicKey: process.env.KERNEL_PUBLIC_KEY,
}
).catch(console.error);
7 changes: 4 additions & 3 deletions src/helpers/update-pull-requests.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { RestEndpointMethodTypes } from "@octokit/rest";
import { RestEndpointMethodTypes } from "@octokit/plugin-rest-endpoint-methods";
import ms from "ms";
import { getAllTimelineEvents } from "../handlers/github-events";
import { generateSummary, ResultInfo } from "../handlers/summary";
Expand Down Expand Up @@ -35,14 +35,15 @@ export async function updatePullRequests(context: Context) {
if (owner) {
logger.info(`No organizations or repo have been specified, will default to the organization owner: ${owner.login}.`);
} else {
return logger.error("Could not set a default organization to watch, skipping.");
throw logger.error("Could not set a default organization to watch, skipping.");
}
}

const pullRequests = await getOpenPullRequests(context, context.config.repos as ReposWatchSettings);

if (!pullRequests?.length) {
return logger.info("Nothing to do.");
logger.info("Nothing to do.");
return;
}

for (const { html_url } of pullRequests) {
Expand Down
35 changes: 0 additions & 35 deletions src/helpers/validator.ts

This file was deleted.

12 changes: 0 additions & 12 deletions src/index.ts

This file was deleted.

19 changes: 2 additions & 17 deletions src/plugin.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,10 @@
import { paginateRest } from "@octokit/plugin-paginate-rest";
import { Octokit } from "@octokit/rest";
import { Logs } from "@ubiquity-dao/ubiquibot-logger";
import { updatePullRequests } from "./helpers/update-pull-requests";
import { Context, Env, PluginInputs } from "./types";
import { Context } from "./types";

/**
* How a worker executes the plugin.
*/
export async function plugin(inputs: PluginInputs, env: Env) {
const octokitWithPlugin = Octokit.plugin(paginateRest);
const octokit = new octokitWithPlugin({ auth: inputs.authToken });

const context: Context = {
eventName: inputs.eventName,
payload: inputs.eventPayload,
config: inputs.settings,
octokit,
env,
logger: new Logs("debug"),
};

export async function plugin(context: Context) {
context.logger.info("Will check the following repos", { ...context.config.repos });
return await updatePullRequests(context);
}
19 changes: 3 additions & 16 deletions src/types/context.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,7 @@
import { Octokit } from "@octokit/rest";
import { EmitterWebhookEvent as WebhookEvent, EmitterWebhookEventName as WebhookEventName } from "@octokit/webhooks";
import { Logs } from "@ubiquity-dao/ubiquibot-logger";
import { Context as PluginContext } from "@ubiquity-os/plugin-sdk";
import { Env } from "./env";
import { PluginSettings } from "./plugin-inputs";

export type SupportedEventsU = "push" | "issue_comment.created";
export type SupportedEvents = "push" | "issue_comment.created";

export type SupportedEvents = {
[K in SupportedEventsU]: K extends WebhookEventName ? WebhookEvent<K> : never;
};

export interface Context<T extends SupportedEventsU = SupportedEventsU, TU extends SupportedEvents[T] = SupportedEvents[T]> {
eventName: T;
payload: TU["payload"];
octokit: InstanceType<typeof Octokit>;
config: PluginSettings;
env: Env;
logger: Logs;
}
export type Context<TEvents extends SupportedEvents = SupportedEvents> = PluginContext<PluginSettings, Env, null, TEvents>;
3 changes: 0 additions & 3 deletions src/types/env.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
import { Type as T } from "@sinclair/typebox";
import { StaticDecode } from "@sinclair/typebox";
import "dotenv/config";
import { StandardValidator } from "typebox-validators";

export const envSchema = T.Object({
workflowName: T.String(),
});

export const envValidator = new StandardValidator(envSchema);

export type Env = StaticDecode<typeof envSchema>;
13 changes: 0 additions & 13 deletions src/types/plugin-inputs.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,4 @@
import { SupportedEvents, SupportedEventsU } from "./context";
import { StaticDecode, Type as T } from "@sinclair/typebox";
import { StandardValidator } from "typebox-validators";

export interface PluginInputs<T extends SupportedEventsU = SupportedEventsU, TU extends SupportedEvents[T] = SupportedEvents[T]> {
stateId: string;
eventName: T;
eventPayload: TU["payload"];
settings: PluginSettings;
authToken: string;
ref: string;
}

export const approvalsRequiredSchema = T.Object(
{
Expand Down Expand Up @@ -71,7 +60,5 @@ export const pluginSettingsSchema = T.Object({
),
});

export const pluginSettingsValidator = new StandardValidator(pluginSettingsSchema);

export type PluginSettings = StaticDecode<typeof pluginSettingsSchema>;
export type ReposWatchSettings = StaticDecode<typeof reposSchema>;
Loading