@@ -7,6 +7,7 @@ const exec = promisify(execFile);
77
88const TICK_MS = 30_000 ;
99const SETTLED_REFRESH_MS = 5 * 60_000 ;
10+ const POST_PUSH_REFRESH_GRACE_MS = 2 * 60_000 ;
1011const GH_TIMEOUT_MS = 15_000 ;
1112const GH_MAX_BUFFER = 4 * 1024 * 1024 ;
1213
@@ -44,6 +45,8 @@ interface TaskEntry {
4445 lastNotifiedSha : string | null ;
4546 /** Outcome at the last notification, so the same (sha, outcome) doesn't refire. */
4647 lastNotifiedOutcome : Exclude < PrChecksOverall , 'pending' | 'none' > | null ;
48+ /** After a local push, ignore stale old-SHA check data while GitHub catches up. */
49+ postPushRefreshUntil : number | null ;
4750}
4851
4952let win : BrowserWindow | null = null ;
@@ -114,6 +117,7 @@ export function startPrChecksWatcher(args: {
114117 lastRefreshedAt : 0 ,
115118 lastNotifiedSha : null ,
116119 lastNotifiedOutcome : null ,
120+ postPushRefreshUntil : null ,
117121 }
118122 : { ...existing , taskName : args . taskName } ;
119123 tasks . set ( args . taskId , next ) ;
@@ -128,6 +132,21 @@ export function stopPrChecksWatcher(taskId: string): void {
128132 if ( tasks . size === 0 ) clearTickInterval ( ) ;
129133}
130134
135+ export function refreshPrChecksWatcher ( taskId : string ) : void {
136+ if ( disabled ) return ;
137+ const entry = tasks . get ( taskId ) ;
138+ if ( ! entry ) return ;
139+ entry . postPushRefreshUntil = Date . now ( ) + POST_PUSH_REFRESH_GRACE_MS ;
140+ entry . overall = 'pending' ;
141+ entry . passing = 0 ;
142+ entry . pending = Math . max ( 1 , entry . checks . length ) ;
143+ entry . failing = 0 ;
144+ entry . checks = [ ] ;
145+ entry . lastRefreshedAt = Date . now ( ) ;
146+ sendUpdate ( entry ) ;
147+ ensureInterval ( ) ;
148+ }
149+
131150/** True if the window currently exists and is visible. */
132151function windowIsVisible ( ) : boolean {
133152 return ! ! win && ! win . isDestroyed ( ) && win . isVisible ( ) ;
@@ -209,6 +228,12 @@ async function refreshOne(taskId: string): Promise<void> {
209228 const prevSha = entry . headRefOid ;
210229 const shaChanged = prevSha !== null && prevSha !== view . headRefOid ;
211230 const firstRefresh = entry . lastRefreshedAt === 0 ;
231+ const waitingForPostPushHead =
232+ entry . postPushRefreshUntil !== null && ! shaChanged && Date . now ( ) < entry . postPushRefreshUntil ;
233+ if ( waitingForPostPushHead ) {
234+ entry . lastRefreshedAt = Date . now ( ) ;
235+ return ;
236+ }
212237 const nothingChanged =
213238 ! firstRefresh &&
214239 entry . overall === overall &&
@@ -221,6 +246,9 @@ async function refreshOne(taskId: string): Promise<void> {
221246 if ( shaChanged ) {
222247 entry . lastNotifiedSha = null ;
223248 entry . lastNotifiedOutcome = null ;
249+ entry . postPushRefreshUntil = null ;
250+ } else if ( entry . postPushRefreshUntil !== null && Date . now ( ) >= entry . postPushRefreshUntil ) {
251+ entry . postPushRefreshUntil = null ;
224252 }
225253
226254 entry . overall = overall ;
@@ -356,16 +384,37 @@ export async function detectPrUrlForBranch(
356384 worktreePath : string ,
357385 branchName : string ,
358386) : Promise < string | null > {
387+ const { stdout : headStdout } = await exec ( 'git' , [ 'rev-parse' , branchName ] , {
388+ cwd : worktreePath ,
389+ timeout : GH_TIMEOUT_MS ,
390+ maxBuffer : GH_MAX_BUFFER ,
391+ } ) ;
392+ const localHead = headStdout . trim ( ) ;
359393 const { stdout } = await exec (
360394 'gh' ,
361- [ 'pr' , 'list' , '--state' , 'open' , '--head' , branchName , '--json' , 'url' , '--limit' , '1' ] ,
395+ [
396+ 'pr' ,
397+ 'list' ,
398+ '--state' ,
399+ 'open' ,
400+ '--head' ,
401+ branchName ,
402+ '--json' ,
403+ 'url,headRefName,headRefOid' ,
404+ '--limit' ,
405+ '20' ,
406+ ] ,
362407 { cwd : worktreePath , timeout : GH_TIMEOUT_MS , maxBuffer : GH_MAX_BUFFER } ,
363408 ) ;
364409 const parsed : unknown = JSON . parse ( stdout ) ;
365410 if ( ! Array . isArray ( parsed ) ) return null ;
366- const first = parsed [ 0 ] ;
367- if ( ! first || typeof first !== 'object' ) return null ;
368- const url = ( first as Record < string , unknown > ) [ 'url' ] ;
411+ const match = parsed . find ( ( item ) => {
412+ if ( ! item || typeof item !== 'object' ) return false ;
413+ const pr = item as Record < string , unknown > ;
414+ return pr [ 'headRefName' ] === branchName && pr [ 'headRefOid' ] === localHead ;
415+ } ) ;
416+ if ( ! match || typeof match !== 'object' ) return null ;
417+ const url = ( match as Record < string , unknown > ) [ 'url' ] ;
369418 return typeof url === 'string' && isPrUrl ( url ) ? url : null ;
370419}
371420
0 commit comments