Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,11 @@ function makeRow(
group: 'passing',
checkTone: 'success',
title: 'Review title',
reviewNumber: 12,
reviewLabel: '#12',
reviewUrl: 'https://example.test/pr/12',
reviewState: 'open',
reviewStatus: 'success',
provider: 'github',
summary: 'Checks passing',
detailNames: [],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import type { HostedReviewInfo } from '../../../../shared/hosted-review'
import type { PRInfo, Repo, Worktree } from '../../../../shared/types'
import {
LOCAL_EXECUTION_HOST_ID,
normalizeExecutionHostId
} from '../../../../shared/execution-host'
import { isCachedMergedBranchPRCurrentForWorktree } from '@/components/sidebar/worktree-card-pr-display'
import type { AppState } from '@/store/types'
import { getGitHubPRCacheKey, getLegacyGitHubPRCacheKey } from '@/store/slices/github-cache-key'
import type { ParentPrChecksCacheEntry } from './parent-pr-checks-row-types'

export function canUseParentPrChecksGitHubPRCacheEntry(
worktree: Worktree,
prEntry: ParentPrChecksCacheEntry<PRInfo> | undefined,
hostedReviewEntry: ParentPrChecksCacheEntry<HostedReviewInfo> | undefined
): prEntry is ParentPrChecksCacheEntry<PRInfo> & {
data: NonNullable<ParentPrChecksCacheEntry<PRInfo>['data']>
} {
const pr = prEntry?.data
if (!pr) {
return false
}
const prFetchedAt = prEntry.fetchedAt
const hasLinkedGitHubPR = worktree.linkedPR !== null
if (hasLinkedGitHubPR && pr.number !== worktree.linkedPR) {
return false
}
if (!hasLinkedGitHubPR && hasNonGitHubLinkedReview(worktree)) {
return false
}
const mergedPrMatchesCurrentHead = isCachedMergedBranchPRCurrentForWorktree(pr, worktree)
if (pr.state === 'merged' && !mergedPrMatchesCurrentHead) {
return false
}
// Why: a newer hosted-review miss should suppress older branch PR cache unless
// a merged PR is proven to still describe the checked-out worktree head.
if (
hostedReviewEntry?.data === null &&
!mergedPrMatchesCurrentHead &&
prFetchedAt <= hostedReviewEntry.fetchedAt
) {
return false
}
Comment thread
brennanb2025 marked this conversation as resolved.
return true
}

export function getParentPrChecksGitHubPRCacheEntry({
prCache,
repo,
branch,
settings
}: {
prCache: Record<string, ParentPrChecksCacheEntry<PRInfo>>
repo: Repo
branch: string
settings: AppState['settings']
}): ParentPrChecksCacheEntry<PRInfo> | undefined {
const currentKey = getGitHubPRCacheKey(
repo.path,
repo.id,
branch,
settings,
repo.connectionId,
repo.executionHostId,
true
)
const executionHostId = normalizeExecutionHostId(repo.executionHostId)
const canUseLegacyPRCache =
!repo.connectionId && (!executionHostId || executionHostId === LOCAL_EXECUTION_HOST_ID)
const legacyRepoKey = canUseLegacyPRCache
? getLegacyGitHubPRCacheKey(repo.path, repo.id, branch)
: ''
const legacyPathKey = canUseLegacyPRCache
? getLegacyGitHubPRCacheKey(repo.path, undefined, branch)
: ''
return (
prCache[currentKey] ??
(legacyRepoKey ? prCache[legacyRepoKey] : undefined) ??
(legacyPathKey ? prCache[legacyPathKey] : undefined)
)
}

function hasNonGitHubLinkedReview(worktree: Worktree): boolean {
return (
worktree.linkedGitLabMR != null ||
worktree.linkedBitbucketPR != null ||
worktree.linkedAzureDevOpsPR != null ||
worktree.linkedGiteaPR != null
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import type { HostedReviewInfo } from '../../../../shared/hosted-review'
import type { PRInfo, Worktree } from '../../../../shared/types'
import {
getWorktreeCardPrDisplay,
isCachedMergedBranchPRCurrentForWorktree
} from '@/components/sidebar/worktree-card-pr-display'
import type { ParentPrChecksCacheEntry } from './parent-pr-checks-row-types'

export function canUseParentPrChecksHostedReviewCacheEntry(
worktree: Worktree,
review: HostedReviewInfo,
entry: ParentPrChecksCacheEntry<HostedReviewInfo>
): boolean {
if (review.state === 'merged' && !mergedReviewMatchesHead(review, worktree)) {
return false
}
const linkedReviewNumber = getLinkedReviewNumberForProvider(worktree, review.provider)
if (hasLinkedReview(worktree)) {
return linkedReviewNumber === review.number
}
if ((entry.linkedReviewHintKey ?? '') !== '') {
return false
}
const display = getWorktreeCardPrDisplay(
review,
worktree.linkedPR,
worktree.linkedGitLabMR ?? null,
worktree.linkedBitbucketPR ?? null,
worktree.linkedAzureDevOpsPR ?? null,
worktree.linkedGiteaPR ?? null,
{ reviewHintKey: entry.linkedReviewHintKey }
)
return display?.provider === review.provider && display.number === review.number
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

function mergedReviewMatchesHead(review: HostedReviewInfo, worktree: Worktree): boolean {
return isCachedMergedBranchPRCurrentForWorktree(
{
number: review.number,
title: review.title,
state: review.state,
url: review.url,
checksStatus: review.status,
updatedAt: review.updatedAt,
mergeable: review.mergeable,
...(review.headSha ? { headSha: review.headSha } : {}),
...(review.confirmedContainedHeadOid
? { confirmedContainedHeadOid: review.confirmedContainedHeadOid }
: {})
} satisfies PRInfo,
worktree
)
}

function getLinkedReviewNumberForProvider(
worktree: Worktree,
provider: HostedReviewInfo['provider']
): number | null {
switch (provider) {
case 'github':
return worktree.linkedPR
case 'gitlab':
return worktree.linkedGitLabMR ?? null
case 'bitbucket':
return worktree.linkedBitbucketPR ?? null
case 'azure-devops':
return worktree.linkedAzureDevOpsPR ?? null
case 'gitea':
return worktree.linkedGiteaPR ?? null
case 'unsupported':
return null
}
}

function hasLinkedReview(worktree: Worktree): boolean {
return (
worktree.linkedPR != null ||
worktree.linkedGitLabMR != null ||
worktree.linkedBitbucketPR != null ||
worktree.linkedAzureDevOpsPR != null ||
worktree.linkedGiteaPR != null
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ describe('parent PR checks refresh', () => {
linkedBitbucketPR: 10,
linkedAzureDevOpsPR: 11,
linkedGiteaPR: 12,
currentHeadOid: 'abc',
staleWhileRevalidate: true
}
])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ type FetchHostedReview = (
linkedBitbucketPR?: number | null
linkedAzureDevOpsPR?: number | null
linkedGiteaPR?: number | null
currentHeadOid?: string | null
}
) => Promise<HostedReviewInfo | null>

Expand Down Expand Up @@ -134,6 +135,7 @@ async function refreshParentPrChecksCandidate(
linkedBitbucketPR: candidate.worktree.linkedBitbucketPR ?? null,
linkedAzureDevOpsPR: candidate.worktree.linkedAzureDevOpsPR ?? null,
linkedGiteaPR: candidate.worktree.linkedGiteaPR ?? null,
currentHeadOid: candidate.worktree.head ?? null,
staleWhileRevalidate: true
})
if (!review) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export type ParentPrChecksCacheEntry<T> = {
data: T | null
fetchedAt: number
headSha?: string
linkedReviewHintKey?: string
}

export type ParentPrChecksRefreshOutcome =
Expand Down Expand Up @@ -52,9 +53,11 @@ export type ParentPrChecksRow = {
group: ParentPrChecksGroupKey
checkTone: CheckStatus
title: string
reviewNumber: number | null
reviewLabel: string | null
reviewUrl: string | null
reviewState: HostedReviewInfo['state'] | null
reviewStatus: HostedReviewInfo['status'] | null
provider: HostedReviewInfo['provider'] | null
summary: string
detailNames: string[]
Expand Down
Loading
Loading