diff --git a/src/sdk/actions.ts b/src/sdk/actions.ts index fdd53b9..2a5b02b 100644 --- a/src/sdk/actions.ts +++ b/src/sdk/actions.ts @@ -80,6 +80,8 @@ export async function createActionsPlugin = { eventName: inputs.eventName as TSupportedEvents, payload: JSON.parse(inputs.eventPayload), @@ -87,6 +89,7 @@ export async function createActionsPlugin`, + body: `${error.logMessage.diff}\n`, }); } else { context.logger.info("Cannot post error comment because issue is not found in the payload"); diff --git a/src/sdk/comment.ts b/src/sdk/comment.ts index cf87399..f925601 100644 --- a/src/sdk/comment.ts +++ b/src/sdk/comment.ts @@ -1,35 +1,84 @@ import { Context } from "./context"; -import { LogReturn } from "@ubiquity-os/ubiquity-os-logger"; +import { LogReturn, Metadata } from "@ubiquity-os/ubiquity-os-logger"; import { sanitizeMetadata } from "./util"; +import { CloudflareEnvBindings } from "./server"; 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"), - }); +export async function postComment(context: Context, message: LogReturn | Error | null, honoEnv: CloudflareEnvBindings) { + if (!message) { + return; + } + + let issueNumber + + if ("issue" in context.payload) { + issueNumber = context.payload.issue.number; + } else if ("pull_request" in context.payload) { + issueNumber = context.payload.pull_request.number; + } else if ("discussion" in context.payload) { + issueNumber = context.payload.discussion.number; } else { context.logger.info("Cannot post comment because issue is not found in the payload"); + return; } + + await context.octokit.rest.issues.createComment({ + owner: context.payload.repository.owner.login, + repo: context.payload.repository.name, + issue_number: issueNumber, + body: await createStructuredMetadataErrorComment(message, context, honoEnv), + }); } -function createStructuredMetadata(className: string | undefined, logReturn: LogReturn) { - const logMessage = logReturn.logMessage; - const metadata = logReturn.metadata; +async function createStructuredMetadataErrorComment(message: LogReturn | Error, context: Context, honoEnv: CloudflareEnvBindings) { + let metadata: Metadata = {}; + let logMessage, logTier, callingFnName, headerName; + + if (message instanceof Error) { + metadata = { + message: message.message, + name: message.name, + stack: message.stack, + }; + + callingFnName = message.stack?.split("\n")[2]?.match(/at (\S+)/)?.[1]; + + } else if (message instanceof LogReturn && message.metadata) { + logMessage = message.logMessage; + logTier = message.logMessage.level; // LogLevel + metadata = message.metadata; + + if (metadata.stack || metadata.error) { + metadata.stack = metadata.stack || metadata.error?.stack; + metadata.caller = metadata.caller || metadata.error?.stack?.split("\n")[2]?.match(/at (\S+)/)?.[1]; + } + + callingFnName = metadata.caller; + } else { + metadata = { ...message }; + } + + if ("organization" in context.payload) { + headerName = context.payload.organization?.login; + } else if ("repository" in context.payload) { + headerName = context.payload.repository?.owner?.login; + } else if ("installation" in context.payload && "account" in context.payload.installation!) { + // could use their ID here instead as ID is in all installation payloads + headerName = context.payload.installation?.account?.name; + } else { + headerName = context.payload.sender?.login || HEADER_NAME; + } + + const workerDetails = await getWorkerDeploymentHash(context, honoEnv); + const workerLogUrl = await getWorkerErrorLogUrl(context, honoEnv); + metadata.worker = { ...workerDetails, logUrl: workerLogUrl }; 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 = `