import ingestMissing from './ingest-missing.ts' import { getRepoSettings } from './storage.ts' type WebhookInput = { request?: { headers?: Record json?: unknown body?: string } } type GithubPushPayload = { ref?: string after?: string repository?: { full_name?: string } commits?: Array<{ id?: string }> } /** * Ingest new main-branch commits from a GitHub push webhook. * Platform HMAC verification happens before this export runs. * * @param input - Kody webhook envelope with the GitHub push JSON body * @returns Whether the delivery was ignored or which SHAs were ingested * * @example * import handleGithubWebhook from 'kody:@kentcdodds/lineage/handle-github-webhook' * * const result = await handleGithubWebhook({ * request: { json: { ref: 'refs/heads/main', commits: [{ id: 'abc' }] } }, * }) */ export default async function handleGithubWebhook(input: WebhookInput = {}) { const payload = (input.request?.json ?? {}) as GithubPushPayload const headers = input.request?.headers ?? {} const event = headers['x-github-event'] || headers['X-GitHub-Event'] if (event && event !== 'push') { return { ok: true, ignored: true, reason: `event:${event}` } } const repo = await getRepoSettings() const expectedRef = `refs/heads/${repo.branch}` if (payload.ref && payload.ref !== expectedRef) { return { ok: true, ignored: true, reason: `ref:${payload.ref}` } } const remoteName = payload.repository?.full_name const expectedName = `${repo.owner}/${repo.repo}` if (remoteName && remoteName.toLowerCase() !== expectedName.toLowerCase()) { return { ok: true, ignored: true, reason: `repo:${remoteName}` } } const shas = [ ...new Set( [ ...(payload.commits ?? []).map((commit) => commit.id), payload.after, ].filter((sha): sha is string => Boolean(sha) && sha !== '0'.repeat(40)), ), ] if (shas.length === 0) { return ingestMissing({ limit: 10, maxPages: 1 }) } return ingestMissing({ shas }) }