mirror of
https://github.com/stablyai/orca.git
synced 2026-11-14 01:47:24 +00:00
105 lines
2.9 KiB
TypeScript
105 lines
2.9 KiB
TypeScript
import type { GitHubPRStack, GitHubPRStackEntry } from '../pull-request-types'
|
|
|
|
export type GitHubPRStackMergeScope = {
|
|
count: number
|
|
complete: boolean
|
|
entries: GitHubPRStackEntry[]
|
|
}
|
|
|
|
export type GitHubPRStackMergeBlocker = {
|
|
reason:
|
|
| 'draft'
|
|
| 'closed'
|
|
| 'conflicts'
|
|
| 'changes_requested'
|
|
| 'review_required'
|
|
| 'behind'
|
|
| 'blocked'
|
|
prNumber: number
|
|
}
|
|
|
|
export function isGitHubPRStackMergeQueueRequired(
|
|
reviewMergeQueueRequired: boolean | null | undefined,
|
|
stackMergeQueueRequired: boolean | null | undefined
|
|
): boolean {
|
|
return reviewMergeQueueRequired === true || stackMergeQueueRequired === true
|
|
}
|
|
|
|
export function getGitHubPRStackMergeScope(stack: GitHubPRStack): GitHubPRStackMergeScope {
|
|
const entries = [...(stack.entries ?? [])]
|
|
.filter((entry) => entry.position <= stack.position)
|
|
.sort((a, b) => a.position - b.position)
|
|
const count = stack.position
|
|
const complete =
|
|
entries.length === count && entries.every((entry, index) => entry.position === index + 1)
|
|
return { count, complete, entries }
|
|
}
|
|
|
|
function stackEntryBlockerReason(
|
|
entry: GitHubPRStackEntry
|
|
): GitHubPRStackMergeBlocker['reason'] | null {
|
|
if (entry.state === 'draft') {
|
|
return 'draft'
|
|
}
|
|
if (entry.state === 'closed') {
|
|
return 'closed'
|
|
}
|
|
if (entry.mergeable === 'CONFLICTING' || entry.mergeStateStatus === 'DIRTY') {
|
|
return 'conflicts'
|
|
}
|
|
if (entry.reviewDecision === 'CHANGES_REQUESTED') {
|
|
return 'changes_requested'
|
|
}
|
|
if (entry.reviewDecision === 'REVIEW_REQUIRED') {
|
|
return 'review_required'
|
|
}
|
|
if (entry.mergeStateStatus === 'BEHIND') {
|
|
return 'behind'
|
|
}
|
|
if (entry.mergeStateStatus === 'BLOCKED') {
|
|
return 'blocked'
|
|
}
|
|
return null
|
|
}
|
|
|
|
export function getGitHubPRStackMergeBlocker(
|
|
scope: GitHubPRStackMergeScope
|
|
): GitHubPRStackMergeBlocker | null {
|
|
for (const entry of scope.entries) {
|
|
const reason = stackEntryBlockerReason(entry)
|
|
if (reason) {
|
|
return { reason, prNumber: entry.number }
|
|
}
|
|
}
|
|
return null
|
|
}
|
|
|
|
export type GitHubPRStackMergeDecision = {
|
|
scope: GitHubPRStackMergeScope
|
|
blocker: GitHubPRStackMergeBlocker | null
|
|
usesMergeQueue: boolean
|
|
directMergeAvailable: boolean
|
|
}
|
|
|
|
// A stack merge lands every PR below this one, so the stack's readiness replaces this PR's merge state.
|
|
export function resolveGitHubPRStackMergeDecision(args: {
|
|
stack: GitHubPRStack
|
|
reviewMergeQueueRequired: boolean | null | undefined
|
|
stackMergeQueueRequired: boolean | null | undefined
|
|
directMergeAvailable: boolean
|
|
}): GitHubPRStackMergeDecision {
|
|
const scope = getGitHubPRStackMergeScope(args.stack)
|
|
const blocker = getGitHubPRStackMergeBlocker(scope)
|
|
const usesMergeQueue = isGitHubPRStackMergeQueueRequired(
|
|
args.reviewMergeQueueRequired,
|
|
args.stackMergeQueueRequired
|
|
)
|
|
return {
|
|
scope,
|
|
blocker,
|
|
usesMergeQueue,
|
|
directMergeAvailable:
|
|
!blocker && (scope.complete || args.directMergeAvailable || usesMergeQueue)
|
|
}
|
|
}
|