← Public packages
@kody/github
Call GitHub REST, GraphQL, and pull requests with a GitHub OAuth App or a personal access token.
src/pr/merge.ts
85 lines · 2.7 KB · TypeScriptimport { isConfirmed, isDryRun, pickAuthInput, request } from '../core.ts'
import { normalizePrLocator } from './pr-input.ts'
type MergeMethod = 'merge' | 'squash' | 'rebase'
const MERGE_METHODS: Array<MergeMethod> = ['merge', 'squash', 'rebase']
/**
* Merge a pull request.
*
* Pass `dryRun: true` to preview the merge request without calling GitHub.
* Live merges require `confirm: true`.
*
* @param params - PR locator plus `mergeMethod`, optional `commitTitle`,
* `commitMessage`, `sha`, `integrationName` or `secretName`, `dryRun`, and `confirm`.
* @returns `{ merged, sha, message }` from GitHub, or a dry-run preview.
* @example
* import mergePr from 'kody:@kody/github/pr/merge'
* const preview = await mergePr({ owner: 'example-org', repo: 'example', prNumber: 1, mergeMethod: 'squash', dryRun: true })
* // => { dryRun: true, wouldMerge: true, repository: 'example-org/example', prNumber: 1, mergeMethod: 'squash' }
*/
export default async function mergePr(params: Record<string, unknown> = {}) {
const auth = pickAuthInput(params)
const { owner, repo, prNumber } = normalizePrLocator(params)
const mergeMethod = readMergeMethod(params)
const body: Record<string, unknown> = { merge_method: mergeMethod }
if (typeof params.commitTitle === 'string' && params.commitTitle.trim()) {
body.commit_title = params.commitTitle.trim()
}
if (typeof params.commitMessage === 'string' && params.commitMessage.trim()) {
body.commit_message = params.commitMessage.trim()
}
if (typeof params.sha === 'string' && params.sha.trim()) {
body.sha = params.sha.trim()
}
if (isDryRun(params)) {
return {
dryRun: true,
wouldMerge: true,
repository: `${owner}/${repo}`,
prNumber,
mergeMethod,
body,
auth,
}
}
if (!isConfirmed(params)) {
throw new Error(
`Merging ${owner}/${repo}#${prNumber} requires dryRun: true (preview) or confirm: true (live merge).`,
)
}
const response = await request<Record<string, unknown>>({
...auth,
method: 'PUT',
path: `/repos/${owner}/${repo}/pulls/${prNumber}/merge`,
body,
throwOnError: true,
confirm: true,
})
const result = response.data!
return {
merged: result.merged as boolean,
sha: result.sha as string,
message: result.message as string,
repository: `${owner}/${repo}`,
prNumber,
mergeMethod,
auth: response.auth,
}
}
function readMergeMethod(params: Record<string, unknown>): MergeMethod {
const raw = params.mergeMethod ?? params.merge_method
if (raw === undefined) return 'squash'
if (typeof raw === 'string' && MERGE_METHODS.includes(raw as MergeMethod)) {
return raw as MergeMethod
}
throw new Error(
`mergeMethod must be one of ${MERGE_METHODS.map((method) => `"${method}"`).join(', ')}`,
)
}