Very interesting solution provided here with PS scripts by MIKAEL DEURELL.
Few issues were there in the sample script provided in MIKAEL DEURELL‘s article the team who requested this specific build note is not happy about.
1. Since generating of build notes, depends on the date of the last good build to get the associated work items for current build, it is not possible to queue a build for a previous changeset, to make it the last good build, and another with latest changeset, will not send the associated work items correctly.
2. It sends all the work items and the team only wanted user story and bug WITs.
3. If a task work item associated it does not send the parent work item.
4. Team wanted the note to be formatted in a more readable format.
To overcome these issues the enhanced version (by me) of the sample script provided in MIKAEL DEURELL‘s article is below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | [void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.TeamFoundation.Client") [void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.TeamFoundation.Common") [void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.TeamFoundation.Build.Client") [void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.TeamFoundation.Build.Common") [void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.TeamFoundation.VersionControl.Client") [void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.TeamFoundation.VersionControl.Client.VersionSpec") [void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.TeamFoundation.WorkItemTracking.Client") $tfsCollectionUrl = "http://tfsserver:8080/tfs/projectcollection" $server = new-object Microsoft.TeamFoundation.Client.TfsTeamProjectCollection(New-Object Uri($tfsCollectionUrl)) $buildServer = $server.GetService([Microsoft.TeamFoundation.Build.Client.IBuildServer]) $buildDetail = $buildServer.QueryBuilds("teamprojectname", "builddefinitionname") | where { $_.BuildDefinition.LastGoodBuildUri -eq $_.Uri } #| select BuildNumber $buildDetail.BuildNumber | Out-File "$PSScriptRoot\tmpBuildNumber.txt" $versionControlServer = $server.GetService([Microsoft.TeamFoundation.VersionControl.Client.VersionControlServer]) $workItemStore = $server.GetService([Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemStore]) $LinkingService = $server.GetService([Microsoft.TeamFoundation.Client.TswaClientHyperlinkService]) $lastGoodBuildStartTime = $buildDetail.StartTime $changesetVersionSpec = New-Object Microsoft.TeamFoundation.VersionControl.Client.ChangesetVersionSpec($buildDetail.SourceGetVersion.Substring(1)) $latest = [Microsoft.TeamFoundation.VersionControl.Client.VersionSpec]::Latest $recursionType = [Microsoft.TeamFoundation.VersionControl.Client.RecursionType]::Full $historySinceLastGoodBuild = $versionControlServer.QueryHistory("$/Orca/Main", $latest, 0, $recursionType, $null, $changesetVersionSpec, $latest,[Int32]::MaxValue, $true, $false) $historySinceLastGoodBuild.Length | Out-File "$PSScriptRoot\tmpHistoryCount.txt" $workItemsSinceLastGoodBuild = $historySinceLastGoodBuild | foreach-object {$_.workitems} $workItemsSinceLastGoodBuild.Length | Out-File "$PSScriptRoot\tmpWorkItemCount.txt" $tabName = "WorkItemTable" #Create Table object $WorkItemTable = New-Object system.Data.DataTable “$tabName” #Define Columns $colWorkItemType1 = New-Object system.Data.DataColumn WorkItemType,([string]) $colId = New-Object system.Data.DataColumn Id,([int]) $colTitle = New-Object system.Data.DataColumn Title,([string]) $colState = New-Object system.Data.DataColumn State,([string]) $colAssignedTo = New-Object system.Data.DataColumn AssignedTo,([string]) $colWILink = New-Object system.Data.DataColumn WILink,([string]) #Add the Columns $WorkItemTable.columns.add($colWorkItemType1) $WorkItemTable.columns.add($colId) $WorkItemTable.columns.add($colTitle) $WorkItemTable.columns.add($colState) $WorkItemTable.columns.add($colAssignedTo) $WorkItemTable.columns.add($colWILink) $workItemsSinceLastGoodBuild | foreach-object { if ($_.Type.Name -eq "Task") { # Get parent work item of task foreach($link in $_.WorkItemLinks) { if ($link.LinkTypeEnd.Name -eq "Parent") { $ParentWorkItem = $workItemStore.GetWorkItem($link.TargetId) #Create a row $row = $WorkItemTable.NewRow() #Enter data in the row $row.WorkItemType = $ParentWorkItem.Type.Name $row.Id = $ParentWorkItem.Id $row.Title = $ParentWorkItem.Title $row.State = $ParentWorkItem.State $row.AssignedTo = $ParentWorkItem.Fields["Assigned To"].Value $row.WILink = $LinkingService.GetArtifactViewerUrl($ParentWorkItem.Uri).AbsoluteUri #Add the row to the table $WorkItemTable.Rows.Add($row) break } } } else { # Adding associated work item other than task #Create a row $row = $WorkItemTable.NewRow() #Enter data in the row $row.WorkItemType = $_.Type.Name $row.Id = $_.Id $row.Title = $_.Title $row.State = $_.State $row.AssignedTo = $_.Fields["Assigned To"].Value $row.WILink = $LinkingService.GetArtifactViewerUrl($_.Uri).AbsoluteUri #Add the row to the table $WorkItemTable.Rows.Add($row) } } $WorkItemTable.Rows.Count | Out-File "$PSScriptRoot\tmpWorkItemTableCount.txt" $style = "<style>BODY{font-family: Arial; font-size: 10pt;}" $style = $style + "TABLE{border: 1px solid black; border-collapse: collapse;}" $style = $style + "TH{border: 1px solid black; background: #dddddd; padding: 5px; }" $style = $style + "TD{border: 1px solid black; padding: 5px; }" $style = $style + "</style>" $messageBody = $WorkItemTable |sort WorkItemType,Id -Unique | Select-Object WorkItemType, Id, State, Title, AssignedTo, WILink | ConvertTo-HTML -head $style | Out-String $Recipients = @("user1@yourOrg.com", "user2@yourOrg.com", "user3@yourOrg.com") Send-MailMessage -From "tfs@yourorg.com" -To $Recipients -SmtpServer "YourSMTPServer" -Body $messageBody -Subject "$Env:TF_BUILD_BUILDNUMBER Build Notes" -BodyAsHtml |
One more enhancement could be made to the script to generate the note in pre build and send it in post build. That is by changing the below script segment to write to a temp file in pre build.
1 2 3 4 5 | $messageBody = $WorkItemTable |sort WorkItemType,Id -Unique | Select-Object WorkItemType, Id, State, Title, AssignedTo, WILink | ConvertTo-HTML -head $style | Out-String $Recipients = @("user1@yourOrg.com", "user2@yourOrg.com", "user3@yourOrg.com") Send-MailMessage -From "tfs@yourorg.com" -To $Recipients -SmtpServer "Ad-ex2003.domainx.local" -Body $messageBody -Subject "$Env:TF_BUILD_BUILDNUMBER Build Notes" -BodyAsHtml |
Above can be changed as shown below so that it writes to a temp file in pre build.
1 | $WorkItemTable |sort WorkItemType,Id -Unique | Select-Object WorkItemType, Id, State, Title, AssignedTo, WILink | ConvertTo-HTML -head $style | Out-File "$PSScriptRoot\..\tmpBuildNote.txt" |
In post build the temp file content can be read like shown below and send the email.
1 2 3 4 5 | $messageBody = Get-Content "$PSScriptRoot\..\tmpBuildNote.txt" | Out-String $Recipients = @("user1@yourOrg.com", "user2@yourOrg.com", "user3@yourOrg.com") Send-MailMessage -From "tfs@yourorg.com" -To $Recipients -SmtpServer "yourSMTPserver" -Body $messageBody -Subject "$Env:TF_BUILD_BUILDNUMBER Build Notes" -BodyAsHtml |
The build scripts should be checked in to TFS as shown below.
If there is multiple script then a run script can be used to call other scripts. For example pre build run script for above shown picture is below.
1 2 | Invoke-Expression "$PSScriptRoot\GenerateBuildNotes.ps1" Invoke-Expression "$PSScriptRoot\ApplyVersionToAssemblies.ps1" |
TFS build definition can be set to call the pre build and post build scripts.
With this build note as shown below can be obtained.
No comments:
Post a Comment