@@ -55,7 +55,7 @@ string GenerateCodeFlowPRTitle(
5555 /// <summary>
5656 /// Generate the description for a code flow PR.
5757 /// </summary>
58- string GenerateCodeFlowPRDescription (
58+ Task < string > GenerateCodeFlowPRDescription (
5959 SubscriptionUpdateWorkItem update ,
6060 BuildDTO build ,
6161 string ? previousSourceCommit ,
@@ -181,7 +181,7 @@ public async Task<string> CalculatePRDescriptionAndCommitUpdatesAsync(
181181 itemsToUpdate ,
182182 message . ToString ( ) ) ;
183183
184- AppendBuildDescription ( description , ref startingReferenceId , update , deps , committedFiles , build ) ;
184+ startingReferenceId = await AppendBuildDescriptionAsync ( description , startingReferenceId , update , deps , committedFiles , build ) ;
185185 }
186186
187187 // If the coherency update wasn't combined, then
@@ -224,7 +224,7 @@ public string GenerateCodeFlowPRTitle(
224224 return GeneratePRTitle ( $ "[{ targetBranch } ] Source code updates from", repoNames ) ;
225225 }
226226
227- public string GenerateCodeFlowPRDescription (
227+ public async Task < string > GenerateCodeFlowPRDescription (
228228 SubscriptionUpdateWorkItem update ,
229229 BuildDTO build ,
230230 string ? previousSourceCommit ,
@@ -233,7 +233,7 @@ public string GenerateCodeFlowPRDescription(
233233 string ? currentDescription ,
234234 bool isForwardFlow )
235235 {
236- string description = GenerateCodeFlowPRDescriptionInternal (
236+ string description = await GenerateCodeFlowPRDescriptionInternal (
237237 update ,
238238 build ,
239239 previousSourceCommit ,
@@ -246,7 +246,7 @@ public string GenerateCodeFlowPRDescription(
246246 return AddOrUpdateFooterInDescription ( description , upstreamRepoDiffs ) ;
247247 }
248248
249- private static string GenerateCodeFlowPRDescriptionInternal (
249+ private async Task < string > GenerateCodeFlowPRDescriptionInternal (
250250 SubscriptionUpdateWorkItem update ,
251251 BuildDTO build ,
252252 string ? previousSourceCommit ,
@@ -263,7 +263,7 @@ private static string GenerateCodeFlowPRDescriptionInternal(
263263 > This is a codeflow update. It may contain both source code changes from [{ ( isForwardFlow ? "the source repo" : "the VMR" ) } ]({ update . SourceRepo } ) as well as dependency updates. Learn more [here]({ CodeFlowPrFaqUri } ).
264264
265265 This pull request brings the following source code changes
266- { GenerateCodeFlowDescriptionForSubscription ( update . SubscriptionId , previousSourceCommit , build , update . SourceRepo , dependencyUpdates ) }
266+ { await GenerateCodeFlowDescriptionForSubscription ( update . SubscriptionId , previousSourceCommit , build , update . SourceRepo , dependencyUpdates ) }
267267 """ ;
268268 }
269269 else
@@ -279,15 +279,16 @@ This pull request brings the following source code changes
279279 currentDescription . Length :
280280 endIndex + GetEndMarker ( update . SubscriptionId ) . Length ;
281281
282- return string . Concat (
283- currentDescription . AsSpan ( 0 , startCutoff ) ,
284- GenerateCodeFlowDescriptionForSubscription (
285- update . SubscriptionId ,
286- previousSourceCommit ,
287- build ,
288- update . SourceRepo ,
289- dependencyUpdates ) ,
290- currentDescription . AsSpan ( endCutoff , currentDescription . Length - endCutoff ) ) ;
282+ var beforeSpan = currentDescription . Substring ( 0 , startCutoff ) ;
283+ var afterSpan = currentDescription . Substring ( endCutoff , currentDescription . Length - endCutoff ) ;
284+ var generatedDescription = await GenerateCodeFlowDescriptionForSubscription (
285+ update . SubscriptionId ,
286+ previousSourceCommit ,
287+ build ,
288+ update . SourceRepo ,
289+ dependencyUpdates ) ;
290+
291+ return string . Concat ( beforeSpan , generatedDescription , afterSpan ) ;
291292 }
292293 }
293294
@@ -336,15 +337,14 @@ private static string GenerateUpstreamRepoDiffs(IReadOnlyCollection<UpstreamRepo
336337 return sb . ToString ( ) ;
337338 }
338339
339- private static string GenerateCodeFlowDescriptionForSubscription (
340+ private async Task < string > GenerateCodeFlowDescriptionForSubscription (
340341 Guid subscriptionId ,
341342 string ? previousSourceCommit ,
342343 BuildDTO build ,
343344 string repoUri ,
344345 List < DependencyUpdateSummary > dependencyUpdates )
345346 {
346347 string sourceDiffText = CreateSourceDiffLink ( build , previousSourceCommit ) ;
347-
348348 string dependencyUpdateBlock = CreateDependencyUpdateBlock ( dependencyUpdates , repoUri ) ;
349349 return
350350 $ """
@@ -353,7 +353,7 @@ private static string GenerateCodeFlowDescriptionForSubscription(
353353
354354 ## From { build . GetRepository ( ) }
355355 - **Subscription**: { GetSubscriptionLink ( subscriptionId ) }
356- - **Build**: [ { build . AzureDevOpsBuildNumber } ]( { build . GetBuildLink ( ) } )
356+ - **Build**: { await GetBuildLinkAsync ( build , subscriptionId ) }
357357 - **Date Produced**: { build . DateProduced . ToUniversalTime ( ) : MMMM d, yyyy h:mm:ss tt UTC}
358358 - **Commit**: [{ build . Commit } ]({ GitRepoUrlUtils . GetCommitUri ( build . GetRepository ( ) , build . Commit ) } )
359359 - **Commit Diff**: { sourceDiffText }
@@ -557,9 +557,9 @@ private static string CompressRepeatedLinksInDescription(string description)
557557 /// Because PRs tend to be live for short periods of time, we can put more information
558558 /// in the description than the commit message without worrying that links will go stale.
559559 /// </remarks>
560- private void AppendBuildDescription (
560+ private async Task < int > AppendBuildDescriptionAsync (
561561 StringBuilder description ,
562- ref int startingReferenceId ,
562+ int startingReferenceId ,
563563 SubscriptionUpdateWorkItem update ,
564564 List < DependencyUpdate > deps ,
565565 List < GitFile > ? committedFiles ,
@@ -577,7 +577,7 @@ private void AppendBuildDescription(
577577 . AppendLine ( sectionStartMarker )
578578 . AppendLine ( $ "## From { sourceRepository } ")
579579 . AppendLine ( $ "- **Subscription**: { GetSubscriptionLink ( updateSubscriptionId ) } ")
580- . AppendLine ( $ "- **Build**: [ { build . AzureDevOpsBuildNumber } ]( { build . GetBuildLink ( ) } ) ")
580+ . AppendLine ( $ "- **Build**: { await GetBuildLinkAsync ( build , update . SubscriptionId ) } ")
581581 . AppendLine ( $ "- **Date Produced**: { build . DateProduced . ToUniversalTime ( ) : MMMM d, yyyy h:mm:ss tt UTC} ")
582582 // This is duplicated from the files changed, but is easier to read here.
583583 . AppendLine ( $ "- **Commit**: [{ build . Commit } ]({ GitRepoUrlUtils . GetCommitUri ( build . GetRepository ( ) , build . Commit ) } )") ;
@@ -658,6 +658,7 @@ private void AppendBuildDescription(
658658 description . AppendLine ( ) ;
659659
660660 startingReferenceId += changesLinks . Count ;
661+ return startingReferenceId ;
661662 }
662663
663664 /// <summary>
@@ -844,4 +845,98 @@ private static string GetEndMarker(Guid subscriptionId)
844845
845846 private static string GetSubscriptionLink ( Guid subscriptionId )
846847 => $ "[{ subscriptionId } ](https://maestro.dot.net/subscriptions?search={ subscriptionId } )";
848+
849+ /// <summary>
850+ /// Generates a build link that to the Azure DevOps build and tries to add a BAR build link.
851+ /// </summary>
852+ /// <param name="build">The build object containing build information</param>
853+ /// <param name="subscriptionId">The subscription ID to get channel information</param>
854+ /// <returns>Enhanced build link string with BAR build details</returns>
855+ private async Task < string > GetBuildLinkAsync ( BuildDTO build , Guid subscriptionId )
856+ {
857+ var originalBuildLink = $ "[{ build . AzureDevOpsBuildNumber } ]({ build . GetBuildLink ( ) } )";
858+
859+ try
860+ {
861+ int ? channelId ;
862+ if ( build . Channels . Count == 1 )
863+ {
864+ channelId = build . Channels [ 0 ] . Id ;
865+ }
866+ else
867+ {
868+
869+ // Get the subscription to retrieve the channel ID
870+ var subscription = await _context . Subscriptions
871+ . Where ( s => s . Id == subscriptionId )
872+ . Select ( s => new { s . ChannelId } )
873+ . FirstOrDefaultAsync ( ) ;
874+
875+ if ( subscription == null )
876+ {
877+ // If the subscription is not found, return the original link
878+ return originalBuildLink ;
879+ }
880+
881+ channelId = subscription . ChannelId ;
882+ }
883+
884+ // Generate repository slug for BarViz URL
885+ var repoSlug = ConvertRepoUrlToSlug ( build . GetRepository ( ) ) ;
886+ if ( string . IsNullOrEmpty ( repoSlug ) )
887+ {
888+ return originalBuildLink ;
889+ }
890+
891+ // Create the BarViz link
892+ var barBuildLink = $ "https://maestro.dot.net/channel/{ channelId } /{ repoSlug } /build/{ build . Id } ";
893+ return $ "{ originalBuildLink } ([{ build . Id } ]({ barBuildLink } ))";
894+ }
895+ catch ( Exception ex )
896+ {
897+ _logger . LogWarning ( ex , "Failed to generate enhanced build link for build {BuildId} and subscription {SubscriptionId}" , build . Id , subscriptionId ) ;
898+ return originalBuildLink ;
899+ }
900+ }
901+
902+ /// <summary>
903+ /// Converts a repository URL to a slug format used by BarViz.
904+ /// </summary>
905+ /// <param name="repoUrl">The repository URL</param>
906+ /// <returns>Repository slug in format github:org:repo or azdo:org:project:repo</returns>
907+ private static string ? ConvertRepoUrlToSlug ( string ? repoUrl )
908+ {
909+ if ( repoUrl == null )
910+ {
911+ return null ;
912+ }
913+
914+ var repoType = GitRepoUrlUtils . ParseTypeFromUri ( repoUrl ) ;
915+
916+ switch ( repoType )
917+ {
918+ case GitRepoType . GitHub :
919+ var ( repoName , org ) = GitRepoUrlUtils . GetRepoNameAndOwner ( repoUrl ) ;
920+ return $ "github:{ org } :{ repoName } ";
921+
922+ case GitRepoType . AzureDevOps :
923+ // For Azure DevOps, we need to extract the project name from the URL
924+ // Format: https://dev.azure.com/{org}/{project}/_git/{repo}
925+ const string azureDevOpsPrefix = "https://dev.azure.com/" ;
926+ if ( repoUrl . StartsWith ( azureDevOpsPrefix ) )
927+ {
928+ string [ ] urlParts = repoUrl . Substring ( azureDevOpsPrefix . Length ) . Split ( '/' ) ;
929+ if ( urlParts . Length >= 4 && urlParts [ 2 ] == "_git" )
930+ {
931+ string orgName = urlParts [ 0 ] ;
932+ string projectName = urlParts [ 1 ] ;
933+ string repoNamePart = urlParts [ 3 ] . Split ( '?' ) [ 0 ] ; // Remove query parameters
934+ return $ "azdo:{ orgName } :{ projectName } :{ repoNamePart } ";
935+ }
936+ }
937+ break ;
938+ }
939+
940+ return null ;
941+ }
847942}
0 commit comments