Skip to content

Commit

Permalink
Merge pull request #169 from gentlementlegen/feat/improvements
Browse files Browse the repository at this point in the history
feat: improvements
  • Loading branch information
gentlementlegen authored Nov 6, 2024
2 parents 6986998 + fe99961 commit 393ca5d
Show file tree
Hide file tree
Showing 11 changed files with 102 additions and 32 deletions.
Binary file modified bun.lockb
Binary file not shown.
9 changes: 5 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
"typings": "dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"require": "./dist/index.js",
"import": "./dist/index.mjs"
"types": "./dist/index.d.ts",
"require": "./dist/index.js",
"import": "./dist/index.mjs"
}
},
"files": [
Expand Down Expand Up @@ -53,6 +53,7 @@
"@cfworker/json-schema": "2.0.1",
"@octokit/auth-app": "7.1.0",
"@octokit/core": "6.1.2",
"@octokit/plugin-paginate-graphql": "^5.2.4",
"@octokit/plugin-paginate-rest": "11.3.3",
"@octokit/plugin-rest-endpoint-methods": "13.2.4",
"@octokit/plugin-retry": "7.1.1",
Expand All @@ -61,7 +62,7 @@
"@octokit/types": "^13.5.0",
"@octokit/webhooks": "13.3.0",
"@octokit/webhooks-types": "7.5.1",
"@sinclair/typebox": "0.32.35",
"@sinclair/typebox": "^0.33.17",
"@ubiquity-os/ubiquity-os-logger": "^1.3.2",
"dotenv": "16.4.5",
"hono": "4.4.13",
Expand Down
1 change: 0 additions & 1 deletion revert-pr-108.txt

This file was deleted.

2 changes: 2 additions & 0 deletions src/github/handlers/push-event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ async function checkPluginConfigurations(context: GitHubContext<"push">, config:
value: JSON.stringify(plugin),
type: 0,
schema: configSchema,
errors: [],
});
} else {
const validator = new Validator(manifest.configuration, "7", false);
Expand All @@ -113,6 +114,7 @@ async function checkPluginConfigurations(context: GitHubContext<"push">, config:
value: JSON.stringify(value),
type: 0,
schema: configSchema,
errors: [],
});
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/github/types/plugin-configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export const configSchema = T.Object(
plugins: handlerSchema,
},
{
additionalProperties: false,
additionalProperties: true,
}
);

Expand Down
22 changes: 16 additions & 6 deletions src/sdk/actions.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as core from "@actions/core";
import * as github from "@actions/github";
import { EmitterWebhookEventName as WebhookEventName } from "@octokit/webhooks";
import { Type as T, TAnySchema } from "@sinclair/typebox";
import { TAnySchema, Type as T } from "@sinclair/typebox";
import { Value } from "@sinclair/typebox/value";
import { LOG_LEVEL, LogLevel, LogReturn, Logs } from "@ubiquity-os/ubiquity-os-logger";
import { config } from "dotenv";
Expand Down Expand Up @@ -58,14 +58,24 @@ export async function createActionsPlugin<TConfig = unknown, TEnv = unknown, TSu

let config: TConfig;
if (pluginOptions.settingsSchema) {
config = Value.Decode(pluginOptions.settingsSchema, Value.Default(pluginOptions.settingsSchema, JSON.parse(inputs.settings)));
try {
config = Value.Decode(pluginOptions.settingsSchema, Value.Default(pluginOptions.settingsSchema, JSON.parse(inputs.settings)));
} catch (e) {
console.dir(...Value.Errors(pluginOptions.settingsSchema, JSON.parse(inputs.settings)), { depth: null });
throw e;
}
} else {
config = JSON.parse(inputs.settings) as TConfig;
}

let env: TEnv;
if (pluginOptions.envSchema) {
env = Value.Decode(pluginOptions.envSchema, Value.Default(pluginOptions.envSchema, process.env));
try {
env = Value.Decode(pluginOptions.envSchema, Value.Default(pluginOptions.envSchema, process.env));
} catch (e) {
console.dir(...Value.Errors(pluginOptions.envSchema, process.env), { depth: null });
throw e;
}
} else {
env = process.env as TEnv;
}
Expand Down Expand Up @@ -99,12 +109,12 @@ export async function createActionsPlugin<TConfig = unknown, TEnv = unknown, TSu
}

if (pluginOptions.postCommentOnError && loggerError) {
await postComment(context, loggerError);
await postErrorComment(context, loggerError);
}
}
}

async function postComment(context: Context, error: LogReturn) {
async function postErrorComment(context: Context, error: LogReturn) {
if ("issue" in context.payload && context.payload.repository?.owner?.login) {
await context.octokit.rest.issues.createComment({
owner: context.payload.repository.owner.login,
Expand All @@ -113,7 +123,7 @@ async function postComment(context: Context, error: LogReturn) {
body: `${error.logMessage.diff}\n<!--\n${getGithubWorkflowRunUrl()}\n${sanitizeMetadata(error.metadata)}\n-->`,
});
} else {
context.logger.info("Cannot post comment because issue is not found in the payload");
context.logger.info("Cannot post error comment because issue is not found in the payload");
}
}

Expand Down
48 changes: 48 additions & 0 deletions src/sdk/comment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { Context } from "./context";
import { LogReturn } from "@ubiquity-os/ubiquity-os-logger";
import { sanitizeMetadata } from "./util";

const HEADER_NAME = "Ubiquity";

/**
* Posts a comment on a GitHub issue if the issue exists in the context payload, embedding structured metadata to it.
*/
export async function postComment(context: Context, message: LogReturn) {
if ("issue" in context.payload && context.payload.repository?.owner?.login) {
const metadata = createStructuredMetadata(message.metadata?.name, message);
await context.octokit.rest.issues.createComment({
owner: context.payload.repository.owner.login,
repo: context.payload.repository.name,
issue_number: context.payload.issue.number,
body: [message.logMessage.diff, metadata].join("\n"),
});
} else {
context.logger.info("Cannot post comment because issue is not found in the payload");
}
}

function createStructuredMetadata(className: string | undefined, logReturn: LogReturn) {
const logMessage = logReturn.logMessage;
const metadata = logReturn.metadata;

const jsonPretty = sanitizeMetadata(metadata);
const stack = logReturn.metadata?.stack;
const stackLine = (Array.isArray(stack) ? stack.join("\n") : stack)?.split("\n")[2] ?? "";
const caller = stackLine.match(/at (\S+)/)?.[1] ?? "";
const ubiquityMetadataHeader = `<!-- ${HEADER_NAME} - ${className} - ${caller} - ${metadata?.revision}`;

let metadataSerialized: string;
const metadataSerializedVisible = ["```json", jsonPretty, "```"].join("\n");
const metadataSerializedHidden = [ubiquityMetadataHeader, jsonPretty, "-->"].join("\n");

if (logMessage?.type === "fatal") {
// if the log message is fatal, then we want to show the metadata
metadataSerialized = [metadataSerializedVisible, metadataSerializedHidden].join("\n");
} else {
// otherwise we want to hide it
metadataSerialized = metadataSerializedHidden;
}

// Add carriage returns to avoid any formatting issue
return `\n${metadataSerialized}\n`;
}
2 changes: 2 additions & 0 deletions src/sdk/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
export { createPlugin } from "./server";
export { createActionsPlugin } from "./actions";
export { postComment } from "./comment";
export type { Context } from "./context";
export * from "./constants";
export type { Manifest } from "../types/manifest";
3 changes: 2 additions & 1 deletion src/sdk/octokit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { paginateRest } from "@octokit/plugin-paginate-rest";
import { restEndpointMethods } from "@octokit/plugin-rest-endpoint-methods";
import { retry } from "@octokit/plugin-retry";
import { throttling } from "@octokit/plugin-throttling";
import { paginateGraphQL } from "@octokit/plugin-paginate-graphql";

const defaultOptions = {
throttle: {
Expand All @@ -22,6 +23,6 @@ const defaultOptions = {
},
};

export const customOctokit = Octokit.plugin(throttling, retry, paginateRest, restEndpointMethods).defaults((instanceOptions: object) => {
export const customOctokit = Octokit.plugin(throttling, retry, paginateRest, restEndpointMethods, paginateGraphQL).defaults((instanceOptions: object) => {
return Object.assign({}, defaultOptions, instanceOptions);
});
39 changes: 22 additions & 17 deletions src/sdk/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import { KERNEL_PUBLIC_KEY } from "./constants";
import { Context } from "./context";
import { customOctokit } from "./octokit";
import { verifySignature } from "./signature";
import { sanitizeMetadata } from "./util";
import { env as honoEnv } from "hono/adapter";
import { postComment } from "./comment";
import { Type as T } from "@sinclair/typebox";

interface Options {
Expand Down Expand Up @@ -54,22 +55,39 @@ export function createPlugin<TConfig = unknown, TEnv = unknown, TSupportedEvents
throw new HTTPException(400, { message: "Content-Type must be application/json" });
}

const inputs = Value.Decode(inputSchema, await ctx.req.json());
const body = await ctx.req.json();
const inputSchemaErrors = [...Value.Errors(inputSchema, body)];
if (inputSchemaErrors.length) {
console.dir(inputSchemaErrors, { depth: null });
throw new HTTPException(400, { message: "Invalid body" });
}
const inputs = Value.Decode(inputSchema, body);
const signature = inputs.signature;
if (!(await verifySignature(pluginOptions.kernelPublicKey, inputs, signature))) {
throw new HTTPException(400, { message: "Invalid signature" });
}

let config: TConfig;
if (pluginOptions.settingsSchema) {
config = Value.Decode(pluginOptions.settingsSchema, Value.Default(pluginOptions.settingsSchema, inputs.settings));
try {
config = Value.Decode(pluginOptions.settingsSchema, Value.Default(pluginOptions.settingsSchema, inputs.settings));
} catch (e) {
console.dir(...Value.Errors(pluginOptions.settingsSchema, inputs.settings), { depth: null });
throw e;
}
} else {
config = inputs.settings as TConfig;
}

let env: TEnv;
const honoEnvironment = honoEnv(ctx);
if (pluginOptions.envSchema) {
env = Value.Decode(pluginOptions.envSchema, Value.Default(pluginOptions.envSchema, ctx.env));
try {
env = Value.Decode(pluginOptions.envSchema, Value.Default(pluginOptions.envSchema, honoEnvironment));
} catch (e) {
console.dir(...Value.Errors(pluginOptions.envSchema, honoEnvironment), { depth: null });
throw e;
}
} else {
env = ctx.env as TEnv;
}
Expand Down Expand Up @@ -108,16 +126,3 @@ export function createPlugin<TConfig = unknown, TEnv = unknown, TSupportedEvents

return app;
}

async function postComment(context: Context, error: LogReturn) {
if ("issue" in context.payload && context.payload.repository?.owner?.login) {
await context.octokit.rest.issues.createComment({
owner: context.payload.repository.owner.login,
repo: context.payload.repository.name,
issue_number: context.payload.issue.number,
body: `${error.logMessage.diff}\n<!--\n${sanitizeMetadata(error.metadata)}\n-->`,
});
} else {
context.logger.info("Cannot post comment because issue is not found in the payload");
}
}
6 changes: 4 additions & 2 deletions tests/sdk.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,11 +146,13 @@ describe("SDK worker tests", () => {
body: `\`\`\`diff
! test error
\`\`\`
<!--
<!-- Ubiquity - undefined - - undefined
{
"caller": "error"
}
-->`,
-->
`,
});
});
it("Should accept correct request", async () => {
Expand Down

0 comments on commit 393ca5d

Please sign in to comment.