Skip to content

Built for people who want to own their automations. Join the waitlist for an invite.

Package listing

@kentcdodds/github

src/pr/get-checks.ts

67 lines · 2.3 KB · TypeScript
import { request } from '../core.ts'
import type { GitHubAccount } from '../types.ts'
import { normalizePrLocator } from './pr-input.ts'

/**
 * Fetch check-run status for a pull request's head commit.
 *
 * @param params - PR locator plus optional `account` (default `bot`).
 * @returns `{ headSha, state, totalCount, checks }` where `state` is `pending`, `passing`, or `failing`.
 * @example
 * import getPrChecks from 'kody:@kentcdodds/github/pr/get-checks'
 * const checks = await getPrChecks({ owner: 'kentcdodds', repo: 'kody', prNumber: 1 })
 * // => { state: 'passing', totalCount: 3, checks: [...] }
 */
export default async function getPrChecks(params: Record<string, unknown> = {}) {
	const account = (params.account as GitHubAccount | undefined) ?? 'bot'
	const { owner, repo, prNumber } = normalizePrLocator(params)

	const prResponse = await request<Record<string, unknown>>({
		account,
		path: `/repos/${owner}/${repo}/pulls/${prNumber}`,
		throwOnError: true,
	})
	const pr = prResponse.data!
	const head = pr.head as { sha?: string } | undefined
	const headSha = head?.sha
	if (!headSha) {
		throw new Error(`Could not resolve head SHA for ${owner}/${repo}#${prNumber}`)
	}

	const checkRunsResponse = await request<Record<string, unknown>>({
		account,
		path: `/repos/${owner}/${repo}/commits/${headSha}/check-runs`,
		query: { per_page: 100 },
		throwOnError: true,
	})
	const checkRuns = checkRunsResponse.data!
	const checks = ((checkRuns.check_runs ?? []) as Array<Record<string, unknown>>).map(
		(run) => ({
			name: run.name as string,
			status: run.status as 'queued' | 'in_progress' | 'completed',
			conclusion: (run.conclusion ?? null) as string | null,
			detailsUrl: (run.details_url ?? null) as string | null,
			startedAt: (run.started_at ?? null) as string | null,
			completedAt: (run.completed_at ?? null) as string | null,
		}),
	)

	const hasPending = checks.some((check) => check.status !== 'completed')
	const hasFailure = checks.some(
		(check) =>
			check.status === 'completed' &&
			check.conclusion !== 'success' &&
			check.conclusion !== 'neutral' &&
			check.conclusion !== 'skipped',
	)
	const state = hasFailure ? 'failing' : hasPending ? 'pending' : 'passing'

	return {
		repository: `${owner}/${repo}`,
		prNumber,
		headSha,
		state,
		totalCount: checks.length,
		checks,
	}
}