mirror of
https://github.com/stablyai/orca.git
synced 2026-11-02 01:57:57 +00:00
Fix GitHub PR auto-merge eligibility (#32075)
* Fix GitHub PR auto-merge eligibility handling * Preserve GraphQL auto-merge state over REST fallback
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import {
|
||||
canEnableGitHubPRAutoMerge,
|
||||
canShowGitHubPRAutoMergeControl,
|
||||
type GitHubPRAutoMergeAvailabilityInput
|
||||
} from './pull-request-auto-merge-availability'
|
||||
@@ -18,34 +17,55 @@ function pr(
|
||||
}
|
||||
|
||||
describe('github PR auto-merge availability', () => {
|
||||
it.each([undefined, null, 'UNKNOWN', 'FUTURE_STATUS'])(
|
||||
'does not infer an auto-merge requirement from unresolved status %s',
|
||||
(mergeStateStatus) => {
|
||||
const item = pr({ mergeable: 'UNKNOWN', mergeStateStatus })
|
||||
expect(canShowGitHubPRAutoMergeControl(item)).toBe(false)
|
||||
}
|
||||
)
|
||||
|
||||
it('does not offer auto-merge for directly mergeable PRs with only optional checks pending', () => {
|
||||
expect(canEnableGitHubPRAutoMerge(pr())).toBe(false)
|
||||
expect(canShowGitHubPRAutoMergeControl(pr())).toBe(false)
|
||||
})
|
||||
|
||||
it('offers auto-merge for requirement-blocked PRs', () => {
|
||||
expect(
|
||||
canEnableGitHubPRAutoMerge(
|
||||
canShowGitHubPRAutoMergeControl(
|
||||
pr({ mergeable: 'UNKNOWN', mergeStateStatus: 'BLOCKED', reviewDecision: 'REVIEW_REQUIRED' })
|
||||
)
|
||||
).toBe(true)
|
||||
expect(canEnableGitHubPRAutoMerge(pr({ mergeStateStatus: 'BLOCKED' }))).toBe(true)
|
||||
expect(canShowGitHubPRAutoMergeControl(pr({ mergeStateStatus: 'BLOCKED' }))).toBe(true)
|
||||
expect(canShowGitHubPRAutoMergeControl(pr({ mergeStateStatus: 'BEHIND' }))).toBe(true)
|
||||
expect(
|
||||
canShowGitHubPRAutoMergeControl(pr({ mergeable: 'UNKNOWN', mergeStateStatus: 'BLOCKED' }))
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
it('keeps merge-queue branches available for merge-when-ready', () => {
|
||||
expect(canEnableGitHubPRAutoMerge(pr({ mergeQueueRequired: true }))).toBe(false)
|
||||
expect(canShowGitHubPRAutoMergeControl(pr({ mergeQueueRequired: true }))).toBe(true)
|
||||
expect(
|
||||
canShowGitHubPRAutoMergeControl(pr({ autoMergeAllowed: false, mergeQueueRequired: true }))
|
||||
).toBe(true)
|
||||
expect(
|
||||
canShowGitHubPRAutoMergeControl(
|
||||
pr({ mergeStateStatus: 'UNSTABLE', mergeQueueRequired: true })
|
||||
)
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
it('keeps enabled auto-merge visible so users can disable it', () => {
|
||||
expect(canEnableGitHubPRAutoMerge(pr({ autoMergeEnabled: true }))).toBe(false)
|
||||
expect(canShowGitHubPRAutoMergeControl(pr({ autoMergeEnabled: true }))).toBe(true)
|
||||
expect(
|
||||
canShowGitHubPRAutoMergeControl(
|
||||
pr({
|
||||
autoMergeEnabled: true,
|
||||
autoMergeAllowed: false,
|
||||
mergeable: 'CONFLICTING',
|
||||
mergeStateStatus: 'DIRTY'
|
||||
})
|
||||
)
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
it('suppresses closed, draft, disallowed, conflicting, and unstable PRs', () => {
|
||||
@@ -63,9 +83,6 @@ describe('github PR auto-merge availability', () => {
|
||||
expect(
|
||||
canShowGitHubPRAutoMergeControl(pr({ mergeable: 'CONFLICTING', mergeStateStatus: 'DIRTY' }))
|
||||
).toBe(false)
|
||||
expect(
|
||||
canEnableGitHubPRAutoMerge(pr({ mergeable: 'UNKNOWN', mergeStateStatus: 'UNSTABLE' }))
|
||||
).toBe(false)
|
||||
expect(
|
||||
canShowGitHubPRAutoMergeControl(pr({ mergeable: 'UNKNOWN', mergeStateStatus: 'UNSTABLE' }))
|
||||
).toBe(false)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { PRMergeableState, PRReviewDecision, PRState } from './pull-request-types'
|
||||
|
||||
export type GitHubPRAutoMergeAvailabilityInput = {
|
||||
state: PRState | 'open' | 'closed' | 'merged' | 'draft'
|
||||
state: PRState
|
||||
mergeable?: PRMergeableState
|
||||
mergeStateStatus?: string | null
|
||||
reviewDecision?: PRReviewDecision | null
|
||||
@@ -10,51 +10,26 @@ export type GitHubPRAutoMergeAvailabilityInput = {
|
||||
mergeQueueRequired?: boolean | null
|
||||
}
|
||||
|
||||
function isOpenPR(item: GitHubPRAutoMergeAvailabilityInput): boolean {
|
||||
return item.state === 'open'
|
||||
}
|
||||
|
||||
function isConflicting(item: GitHubPRAutoMergeAvailabilityInput): boolean {
|
||||
return item.mergeable === 'CONFLICTING' || item.mergeStateStatus === 'DIRTY'
|
||||
}
|
||||
|
||||
function isUnstable(item: GitHubPRAutoMergeAvailabilityInput): boolean {
|
||||
return item.mergeStateStatus === 'UNSTABLE'
|
||||
}
|
||||
|
||||
function hasReviewRequirement(item: GitHubPRAutoMergeAvailabilityInput): boolean {
|
||||
return item.reviewDecision === 'REVIEW_REQUIRED' || item.reviewDecision === 'CHANGES_REQUESTED'
|
||||
}
|
||||
|
||||
function canMergeImmediately(item: GitHubPRAutoMergeAvailabilityInput): boolean {
|
||||
if (item.mergeStateStatus === 'BLOCKED' || item.mergeStateStatus === 'BEHIND') {
|
||||
export function canShowGitHubPRAutoMergeControl(item: GitHubPRAutoMergeAvailabilityInput): boolean {
|
||||
if (item.state !== 'open') {
|
||||
return false
|
||||
}
|
||||
return item.mergeable === 'MERGEABLE' || item.mergeStateStatus === 'CLEAN'
|
||||
}
|
||||
|
||||
function canRequestWhenReady(item: GitHubPRAutoMergeAvailabilityInput): boolean {
|
||||
// Why: GitHub auto-merge waits on unmet requirements; UNSTABLE is rejected
|
||||
// by the mutation rather than becoming a waitable auto-merge request.
|
||||
if (!isOpenPR(item) || isConflicting(item) || isUnstable(item)) {
|
||||
if (item.autoMergeEnabled === true) {
|
||||
return true
|
||||
}
|
||||
if (item.mergeable === 'CONFLICTING' || item.mergeStateStatus === 'DIRTY') {
|
||||
return false
|
||||
}
|
||||
if (item.mergeQueueRequired === true) {
|
||||
return true
|
||||
}
|
||||
// Missing merge metadata is not evidence of an unmet branch requirement.
|
||||
return (
|
||||
item.autoMergeAllowed !== false && (hasReviewRequirement(item) || !canMergeImmediately(item))
|
||||
item.mergeStateStatus !== 'UNSTABLE' &&
|
||||
item.autoMergeAllowed !== false &&
|
||||
(item.reviewDecision === 'REVIEW_REQUIRED' ||
|
||||
item.reviewDecision === 'CHANGES_REQUESTED' ||
|
||||
item.mergeStateStatus === 'BLOCKED' ||
|
||||
item.mergeStateStatus === 'BEHIND')
|
||||
)
|
||||
}
|
||||
|
||||
export function canEnableGitHubPRAutoMerge(item: GitHubPRAutoMergeAvailabilityInput): boolean {
|
||||
return (
|
||||
item.autoMergeEnabled !== true && item.mergeQueueRequired !== true && canRequestWhenReady(item)
|
||||
)
|
||||
}
|
||||
|
||||
export function canShowGitHubPRAutoMergeControl(item: GitHubPRAutoMergeAvailabilityInput): boolean {
|
||||
// Why: GitHub auto-merge waits for branch requirements, not arbitrary optional CI.
|
||||
// Keep already-enabled PRs visible so users can disable the setting.
|
||||
return isOpenPR(item) && (item.autoMergeEnabled === true || canRequestWhenReady(item))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user