diff --git a/src/events/match.ts b/src/events/match.ts index 20dcf2b..4eee966 100644 --- a/src/events/match.ts +++ b/src/events/match.ts @@ -48,6 +48,14 @@ function extractBranch(event: WebhookEvent): string | undefined { const suite = event.payload.check_suite as { head_branch?: string } | undefined; return suite?.head_branch; } + if (event.event === "workflow_job") { + const job = event.payload.workflow_job as { head_branch?: string } | undefined; + return job?.head_branch; + } + if (event.event === "deployment") { + const deployment = event.payload.deployment as { ref?: string } | undefined; + return deployment?.ref?.replace("refs/heads/", ""); + } if (event.event === "commit_comment") { const comment = event.payload.comment as { position?: number | null } | undefined; if (comment?.position != null) { diff --git a/src/formatters/check.ts b/src/formatters/check.ts index 64ac7c6..bf5121d 100644 --- a/src/formatters/check.ts +++ b/src/formatters/check.ts @@ -81,6 +81,77 @@ export function formatCheckSuite( ); } +export function formatStatus( + payload: Record, + repo: string | undefined, + author: NeutralAuthor, + t: T, + showEmoji: boolean, +): NeutralMessage { + const state = (payload.state as string) ?? "pending"; + const context = (payload.context as string) ?? ""; + const description = payload.description as string | undefined; + const targetUrl = payload.target_url as string | undefined; + const sha = payload.sha as string | undefined; + + const emoji = state === "success" ? "✅" : state === "failure" || state === "error" ? "❌" : "⏳"; + const em = (e: string): string => emojiPrefix(e, showEmoji); + const colorKey = + state === "success" + ? "check_run_success" + : state === "failure" || state === "error" + ? "check_run_failure" + : "check_run_other"; + + const fields: Array<{ name: string; value: string; inline?: boolean }> = []; + + fields.push({ + name: t("fields.status"), + value: `${em(emoji)}${state}`, + inline: true, + }); + + if (context) { + fields.push({ + name: t("fields.context"), + value: context, + inline: true, + }); + } + + if (sha) { + fields.push({ + name: t("fields.commit"), + value: `\`${sha.slice(0, 7)}\``, + inline: true, + }); + } + + if (description) { + fields.push({ + name: t("fields.description"), + value: description, + inline: false, + }); + } + + return buildMessage( + { + author, + title: t("events.status.title", { + repo: repo ?? t("common.repository"), + context: context || t("common.unknown"), + state, + }), + url: targetUrl, + color: GITHUB_COLORS[colorKey], + fields, + }, + t, + repo, + ); +} + export function formatCheckRun( payload: Record, repo: string | undefined, diff --git a/src/formatters/deployment.ts b/src/formatters/deployment.ts index 3e1a7a0..529925f 100644 --- a/src/formatters/deployment.ts +++ b/src/formatters/deployment.ts @@ -2,6 +2,77 @@ import type { NeutralMessage, NeutralAuthor } from "../types"; import { GITHUB_COLORS } from "./colors"; import { emojiPrefix, type T, buildMessage } from "./helpers"; +export function formatDeployment( + payload: Record, + repo: string | undefined, + author: NeutralAuthor, + t: T, + showEmoji: boolean, +): NeutralMessage { + const deployment = payload.deployment as { + environment?: string; + ref?: string; + sha?: string; + description?: string; + html_url?: string; + statuses_url?: string; + }; + + const emoji = "🚀"; + const em = (e: string): string => emojiPrefix(e, showEmoji); + const env = deployment.environment ?? t("common.unknown"); + const shortSha = deployment.sha?.slice(0, 7) ?? "???????"; + const fields: Array<{ name: string; value: string; inline?: boolean }> = []; + + fields.push({ + name: t("fields.status"), + value: `${em(emoji)}created`, + inline: true, + }); + + fields.push({ + name: t("fields.environment"), + value: env, + inline: true, + }); + + if (deployment.ref) { + fields.push({ + name: t("fields.branch_tag"), + value: `\`${deployment.ref}\``, + inline: true, + }); + } + + if (deployment.sha) { + fields.push({ + name: t("fields.commit"), + value: `\`${shortSha}\``, + inline: true, + }); + } + + if (deployment.description) { + fields.push({ + name: t("fields.description"), + value: deployment.description, + inline: false, + }); + } + + return buildMessage( + { + author, + title: t("events.deployment.title", { repo: repo ?? t("common.repository"), env, state: "created" }), + url: deployment.html_url ?? deployment.statuses_url, + color: GITHUB_COLORS.deployment_pending, + fields, + }, + t, + repo, + ); +} + export function formatDeploymentStatus( payload: Record, repo: string | undefined, diff --git a/src/formatters/index.ts b/src/formatters/index.ts index 9020997..c6635a3 100644 --- a/src/formatters/index.ts +++ b/src/formatters/index.ts @@ -6,13 +6,13 @@ import { formatPullRequest } from "./pull-request"; import { formatPullRequestReview, formatPullRequestReviewComment } from "./review"; import { formatIssues } from "./issues"; import { formatIssueComment } from "./comments"; -import { formatWorkflowRun } from "./workflow"; +import { formatWorkflowRun, formatWorkflowJob } from "./workflow"; import { formatRelease } from "./release"; import { formatCreate, formatDelete } from "./create"; import { formatStar, formatFork } from "./repo"; -import { formatCheckRun, formatCheckSuite } from "./check"; +import { formatCheckRun, formatCheckSuite, formatStatus } from "./check"; import { formatCommitComment } from "./commit-comment"; -import { formatDeploymentStatus } from "./deployment"; +import { formatDeployment, formatDeploymentStatus } from "./deployment"; import { formatMember } from "./member"; import { formatLabel } from "./label"; import { formatMilestone } from "./milestone"; @@ -20,6 +20,7 @@ import { formatDiscussion, formatDiscussionComment } from "./discussion"; import { formatRepository } from "./repository"; import { formatCodeScanningAlert, formatDependabotAlert } from "./security"; import { formatGeneric } from "./generic"; +import { formatPing } from "./ping"; export type { T } from "./helpers"; export { formatPush } from "./push"; @@ -27,13 +28,13 @@ export { formatPullRequest } from "./pull-request"; export { formatPullRequestReview, formatPullRequestReviewComment } from "./review"; export { formatIssues } from "./issues"; export { formatIssueComment } from "./comments"; -export { formatWorkflowRun } from "./workflow"; +export { formatWorkflowRun, formatWorkflowJob } from "./workflow"; export { formatRelease } from "./release"; export { formatCreate, formatDelete } from "./create"; export { formatStar, formatFork } from "./repo"; -export { formatCheckRun, formatCheckSuite } from "./check"; +export { formatCheckRun, formatCheckSuite, formatStatus } from "./check"; export { formatCommitComment } from "./commit-comment"; -export { formatDeploymentStatus } from "./deployment"; +export { formatDeployment, formatDeploymentStatus } from "./deployment"; export { formatMember } from "./member"; export { formatLabel } from "./label"; export { formatMilestone } from "./milestone"; @@ -41,6 +42,7 @@ export { formatDiscussion, formatDiscussionComment } from "./discussion"; export { formatRepository } from "./repository"; export { formatCodeScanningAlert, formatDependabotAlert } from "./security"; export { formatGeneric } from "./generic"; +export { formatPing } from "./ping"; export function formatEvent( route: Route, @@ -77,6 +79,14 @@ export function formatEvent( return formatIssueComment(payload, repo, author, t, showEmoji); case "workflow_run": return formatWorkflowRun(payload, repo, author, t, showEmoji); + case "workflow_job": + return formatWorkflowJob(payload, repo, author, t, showEmoji); + case "status": + return formatStatus(payload, repo, author, t, showEmoji); + case "deployment": + return formatDeployment(payload, repo, author, t, showEmoji); + case "ping": + return formatPing(payload, repo, author, t, showEmoji); case "release": return formatRelease(payload, repo, author, t, showEmoji); case "create": diff --git a/src/formatters/ping.ts b/src/formatters/ping.ts new file mode 100644 index 0000000..403585b --- /dev/null +++ b/src/formatters/ping.ts @@ -0,0 +1,36 @@ +import type { NeutralMessage, NeutralAuthor } from "../types"; +import { GITHUB_COLORS } from "./colors"; +import { type T, buildMessage } from "./helpers"; + +export function formatPing( + payload: Record, + repo: string | undefined, + author: NeutralAuthor, + t: T, + _showEmoji: boolean, +): NeutralMessage { + const zen = payload.zen as string | undefined; + const hookId = payload.hook_id as number | undefined; + + const fields: Array<{ name: string; value: string; inline?: boolean }> = []; + + if (hookId != null) { + fields.push({ + name: t("fields.details"), + value: String(hookId), + inline: true, + }); + } + + return buildMessage( + { + author, + title: t("events.ping.title", { repo: repo ?? t("common.repository") }), + color: GITHUB_COLORS.default, + description: zen, + fields, + }, + t, + repo, + ); +} diff --git a/src/formatters/workflow.ts b/src/formatters/workflow.ts index bcf8ca9..d6aba3d 100644 --- a/src/formatters/workflow.ts +++ b/src/formatters/workflow.ts @@ -2,6 +2,96 @@ import type { NeutralMessage, NeutralAuthor } from "../types"; import { GITHUB_COLORS, WORKFLOW_CONCLUSION_EMOJI } from "./colors"; import { emojiPrefix, type T, buildMessage } from "./helpers"; +export function formatWorkflowJob( + payload: Record, + repo: string | undefined, + author: NeutralAuthor, + t: T, + showEmoji: boolean, +): NeutralMessage { + const job = payload.workflow_job as { + name?: string; + status?: string; + conclusion?: string | null; + head_branch?: string; + head_sha?: string; + html_url?: string; + workflow_name?: string; + run_id?: number; + }; + + const status = + job.status === "queued" + ? "queued" + : job.status === "in_progress" + ? "running" + : (job.conclusion ?? "pending"); + const emoji = WORKFLOW_CONCLUSION_EMOJI[status] ?? "⏳"; + const em = (e: string): string => emojiPrefix(e, showEmoji); + const colorKey = + status === "success" + ? "workflow_run_success" + : status === "failure" + ? "workflow_run_failure" + : "workflow_run_other"; + + const fields: Array<{ name: string; value: string; inline?: boolean }> = []; + + fields.push({ + name: t("fields.status"), + value: `${em(emoji)}${status}`, + inline: true, + }); + + if (job.name) { + fields.push({ + name: t("fields.job"), + value: job.name, + inline: true, + }); + } + + if (job.workflow_name) { + fields.push({ + name: t("fields.workflow"), + value: job.workflow_name, + inline: true, + }); + } + + if (job.head_branch) { + fields.push({ + name: t("fields.branch"), + value: `\`${job.head_branch}\``, + inline: true, + }); + } + + if (job.head_sha) { + fields.push({ + name: t("fields.commit"), + value: `\`${job.head_sha.slice(0, 7)}\``, + inline: true, + }); + } + + return buildMessage( + { + author, + title: t("events.workflow_job.title", { + repo: repo ?? t("common.repository"), + name: job.name ?? "Job", + conclusion: status, + }), + url: job.html_url, + color: GITHUB_COLORS[colorKey], + fields, + }, + t, + repo, + ); +} + export function formatWorkflowRun( payload: Record, repo: string | undefined,