@@ -287,15 +287,19 @@ string Label(string? branch, string def, string stage) =>
287287 // with several PRs and many release stages).
288288 var containsCache = new System . Collections . Concurrent . ConcurrentDictionary < ( string , string , bool ) , Task < bool ? > > ( ) ;
289289 using var gate = new SemaphoreSlim ( ParallelChecks ) ;
290- Task < bool ? > Contains ( string repo , string commit , string target , bool isBranch ) =>
291- containsCache . GetOrAdd ( ( commit , target , isBranch ) , _ => Gated ( ( ) => code . ContainsAsync ( repo , commit , target , isBranch , dt ) ) ) ;
290+ Task < bool ? > Contains ( string repo , string commit , string target , bool isBranch , DateTime ? committed ) =>
291+ containsCache . GetOrAdd ( ( commit , target , isBranch ) , _ => Gated ( ( ) => code . ContainsAsync ( repo , commit , target , isBranch , dt , committed ) ) ) ;
292+ // Code moves forward through the environments (dev -> staging -> master): a change merged into a later one is
293+ // not in an earlier one, so that is not asked.
294+ bool Later ( string ? changeTarget , string ? branch ) =>
295+ changeTarget is not null && branch is not null && envOrder . TryGetValue ( changeTarget , out var a ) && envOrder . TryGetValue ( branch , out var b ) && a > b ;
292296 async Task < bool ? > Gated ( Func < Task < bool ? > > check )
293297 {
294298 await gate . WaitAsync ( dt ) ;
295299 try { return await check ( ) ; }
296300 finally { gate . Release ( ) ; }
297301 }
298- async Task < ( List < CodeChange > In , List < CodeChange > NotYet , bool Known ) > GitStatus ( string repo , string target , bool isBranch , DateTime ? deployedAt )
302+ async Task < ( List < CodeChange > In , List < CodeChange > NotYet , bool Known ) > GitStatus ( string repo , string target , bool isBranch , DateTime ? deployedAt , string ? targetBranch = null )
299303 {
300304 var git = changes . Select ( x => x . Change ) . Where ( x => x . Kind != "changeset" && string . Equals ( x . RepoId , repo , StringComparison . OrdinalIgnoreCase ) ) . ToList ( ) ;
301305 // Only the newest change of each source -> target pair is asked about: the older ones of the pair are in
@@ -308,17 +312,20 @@ string Label(string? branch, string def, string stage) =>
308312 : isBranch && g . Kind == "pr" && string . Equals ( g . TargetBranch , target , StringComparison . OrdinalIgnoreCase ) ? true
309313 // A deployment that finished before a change was merged cannot contain it: no need to ask.
310314 : deployedAt is { } at && g . Date is { } merged && at < merged ? false
315+ : Later ( g . TargetBranch , isBranch ? target : targetBranch ) ? false
311316 : ! newest . Contains ( g ) ? ( bool ? ) null
312- : await Contains ( repo , commit , target , isBranch ) ) ) ;
317+ : await Contains ( repo , commit , target , isBranch , g . Date ) ) ) ;
313318 // The older changes of a pair follow the answer for its newest one.
314319 for ( var i = 0 ; i < git . Count ; i ++ )
315320 if ( answers [ i ] is null && git [ i ] . LandedCommit is not null
316321 && git . Select ( ( g , k ) => ( g , k ) ) . FirstOrDefault ( x => newest . Contains ( x . g ) && x . g . Branch == git [ i ] . Branch && x . g . TargetBranch == git [ i ] . TargetBranch && x . g . Kind == git [ i ] . Kind ) is { g : not null } n )
317322 answers [ i ] = answers [ n . k ] ;
318323 var inside = git . Where ( ( g , i ) => answers [ i ] == true ) . ToList ( ) ;
319324 var known = answers . Any ( a => a is not null ) ;
325+ // Not yet: unless a change from the same source branch is in and no older than a day before it (the same work
326+ // merged into each environment by its own PR).
320327 var notYet = git . Where ( g => ! inside . Contains ( g ) && ! IsPromotion ( g )
321- && ! inside . Any ( i => i . Branch is not null && i . Branch == g . Branch && i . Date >= g . Date ) ) . ToList ( ) ;
328+ && ! inside . Any ( i => i . Branch is not null && i . Branch == g . Branch && i . Date >= g . Date ? . AddDays ( - 1 ) ) ) . ToList ( ) ;
322329 return ( inside , notYet , known ) ;
323330 }
324331 static string Names ( IEnumerable < CodeChange > list ) =>
@@ -351,7 +358,7 @@ static string Names(IEnumerable<CodeChange> list) =>
351358 {
352359 stageTasks . Add ( Task . Run ( async ( ) =>
353360 {
354- var ( inside , notYet , _) = await GitStatus ( repo , deployedCommit , false , d . When ) ;
361+ var ( inside , notYet , _) = await GitStatus ( repo , deployedCommit , false , d . When , Code . Short ( d . Branch ) ) ;
355362 if ( inside . Count > 0 ) anyIn = true ;
356363 return ( string ? ) ( label + ( inside . Count > 0
357364 ? $ "YES - deployed { deployed } : { Names ( inside . Where ( x => ! IsPromotion ( x ) ) . DefaultIfEmpty ( inside [ 0 ] ) ) } " + ( notYet . Count > 0 ? $ "; not yet: { Names ( notYet ) } " : "" )
0 commit comments