TFS Automatic Deployment and Email Script
SteveGTR
Posts: 91
Hopefully someone will find this useful. This script is called by a custom TFS process script after our website has been built and the package has been created on the NuGet feed. The xaml file is based on the code posted by Justin at http://thefutureofdeployment.com/callin ... r-exe-tfs/. Instead of executing DeploymentManager.exe from the xaml, my version executes the following powershell script that performs the following tasks:
1) Executes DeploymentManager.exe
2) Archives old NuGet packages
3) Sends out an email message with annotated changes
With a little modification, this script might be useful for your project.
1) Executes DeploymentManager.exe
2) Archives old NuGet packages
3) Sends out an email message with annotated changes
With a little modification, this script might be useful for your project.
# To run via -File parameter do the following: # Set-ExecutionPolicy Unrestricted # Get the packages that are going to be deployed $Feed = "\\servername\DeploymentFeed"; $Archive = "\\servername\DeploymentFeedArchive"; $WebPagePackages = $Feed + "\WebPage*.nupkg"; $MergePageDatabasePackages = $Feed + "\MergePage*.nupkg"; $CurrentWebPackage = Get-ChildItem $WebPagePackages | sort LastWriteTime | select -last 1; $CurrentMergePagePackage = Get-ChildItem $MergePageDatabasePackages | sort LastWriteTime | select -last 1; # Execute the deployment manager &"C:\Program Files\Red Gate\DeploymentManager.EXE" create-release --server=http://servername:8080/ --apiKey=YourKey --project=ProjectName --deployto=Development --releasenotes="Automatically generated from TFS website build." --waitfordeployment --httpConnectTimeout=00:05:00 | Write-Host; if ($LastExitCode -ne 0) { Write-Host ("Deployment manager returned error: " + $LastExitCode); exit $LastExitCode; } # Move all files, but the latest to archive if ($CurrentWebPackage -ne $null) { Write-Host ("Current WebPage package: " + $CurrentWebPackage.Name); $Parts = $CurrentWebPackage.Name.Split("."); if ($Parts[4] -ne $null) { $WebPageBuildNumber = $Parts[4]; $Version = $Parts[1] + "." + $Parts[2] + "." + $Parts[3] + "." + $Parts[4]; Write-Host ("Current WebPage Build Number: " + $WebPageBuildNumber); Write-Host ("Current Deployment Version: " + $Version); } $ArchiveFiles = Get-ChildItem -Path $WebPagePackages -Exclude $CurrentWebPackage.Name; if ($ArchiveFiles -ne $null) { foreach ($File in $ArchiveFiles) { Write-Host ("Archiving: " + $File.Name); Move-Item $File $Archive | Write-Host; } } } if ($CurrentMergePagePackage -ne $null) { Write-Host ("Current MergePage Database package: " + $CurrentMergePagePackage.Name); $Parts = $CurrentMergePagePackage.Name.Split("."); if ($Parts[3] -ne $null) { $MergePageBuildNumber = $Parts[0] + "_" + $Parts[2] + "." + $Parts[3]; Write-Host ("Current MergePage Database Build Number: " + $MergePageBuildNumber); } $ArchiveFiles = Get-ChildItem -Path $MergePageDatabasePackages -Exclude $CurrentMergePagePackage.Name; if ($ArchiveFiles -ne $null) { foreach ($File in $ArchiveFiles) { Write-Host ("Archiving: " + $File.Name); Move-Item $File $Archive | Write-Host; } } } if ($WebPageBuildNumber -ne $null -and $MergePageBuildNumber -ne $null) { #---- Start of code to enumerate build details [void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.TeamFoundation.Client"); [void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.TeamFoundation.Build.Client"); [void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.TeamFoundation.Build.Common"); if ((Get-PSSnapin -Name Microsoft.TeamFoundation.PowerShell -ErrorAction SilentlyContinue) -eq $null) { Add-PSSnapin Microsoft.TeamFoundation.PowerShell; } $html = "<table>"; $Packarray = @( [pscustomobject]@{PackageName="Website package: $CurrentWebPackage";ProjectName="MergePage";BuildName="MergePage";LocationToSearch="$/MergePage";BuildNumber=$WebPageBuildNumber}, [pscustomobject]@{PackageName="Database package: $CurrentMergePagePackage";ProjectName="PageDatabases";BuildName="MergePage";LocationToSearch="$/PageDatabases/MergePage";BuildNumber=$MergePageBuildNumber} ); foreach ($p in $Packarray) { $html += "<tr>" + "<td colspan=4>" + $p.PackageName + "</td>" + "</tr>"; $projectName = $p.ProjectName; $buildName = $p.BuildName; $locationToSearch = $p.LocationToSearch; $buildNumber = $p.BuildNumber; $tfsCollectionUrl = "http://servername:8080/tfs/MergePageCollection"; $server = new-object Microsoft.TeamFoundation.Client.TfsTeamProjectCollection(New-Object Uri($tfsCollectionUrl)); $tfs = get-tfsserver $tfsCollectionUrl; $buildServer = $server.GetService([Microsoft.TeamFoundation.Build.Client.IBuildServer]); $buildDetail = $buildServer.QueryBuilds($projectName, $buildName) | where {($_.BuildNumber -eq $buildNumber -or ($_.BuildFinished -eq $true -and $_.Status -eq "Succeeded")) -and $_.LabelName -and $_.LabelName -ne "" } | sort -desc StartTime | select BuildNumber, StartTime, FinishTime; $vcs = $server.GetService([Microsoft.TeamFoundation.VersionControl.Client.VersionControlServer]); $i1 = 0; $i2 = $i1 + 1; # Construct the date range using label dates $LabelDetail = $vcs.QueryLabels($BuildDetail[$i2].BuildNumber, $locationToSearch, $null, $false) | select LastModifiedDate; $dateRange = "D" + (Get-Date($LabelDetail.LastModifiedDate) -format s) + "~"; $LabelDetail = $vcs.QueryLabels($BuildDetail[$i1].BuildNumber, $locationToSearch, $null, $false) | select LastModifiedDate; $dateRange += "D" + (Get-Date($LabelDetail.LastModifiedDate) -format s); $changeDetail = Get-TfsItemHistory $locationToSearch -Server $tfs -Version $dateRange -Recurse -IncludeItems | sort -desc ChangesetId; $changeFound = $false; # Because the Webpage building process is still active and hasn't finished, use the current date on the build # agent. This is the best I could do. if ($BuildDetail[$i1].FinishTime -eq $null -or $BuildDetail[$i1].FinishTime -lt $BuildDetail[$i1].StartTime) { $FinishTime = Get-Date; } else { $FinishTime = $BuildDetail[$i1].FinishTime; } $html += "<tr>" + "<td colspan=2 nowrap>" + "Build: " + $BuildDetail[$i1].BuildNumber + "</td>" + "<td nowrap>" + "Started: " + (Get-Date($BuildDetail[$i1].StartTime) -format g) + "</td>" + "<td nowrap>" + "Duration: {0:N1} minutes" -f ($FinishTime - $BuildDetail[$i1].StartTime).TotalMinutes + "</td>" + "</tr>"; foreach ($changeSet in ($changeDetail | select ChangesetId, Committer, CreationDate, Comment)) { $changeFound = $true; $html += "<tr>" + "<td>" + " " + "</td>" + "<td nowrap>" + "Changeset: " + $changeSet.ChangesetID + "</td>" + "<td nowrap>" + "Date: " + (Get-Date($changeSet.CreationDate) -format g) + "</td>" + "<td nowrap>" + "User: " + $changeSet.Committer + "</td>" + "</tr>"; if ($changeSet.Comment -and $changeSet.Comment -ne "") { $html += "<tr>" + "<td>" + " " + "</td>" + "<td colspan=3>" + "Comments: " + $changeSet.Comment + "</td>" + "</tr>"; } else { $html += "<tr>" + "<td>" + " " + "</td>" + "<td colspan=3>" + "Comments: None" + "</td>" + "</tr>"; } foreach ($typeSet in ($changeDetail | where { $_.ChangesetId -eq $changeSet.ChangesetID } | Select-Object -Expand "Changes")) { $html += "<tr>" + "<td>" + " " + "</td>" + "<td>" + " " + "</td>" + "<td colspan=2 nowrap>" + "Action: " + $typeSet.ChangeType + "</td>" + "</tr>"; foreach ($itemSet in ($typeSet | Select-Object -Expand "Item")) { $html += "<tr>" + "<td>" + " " + "</td>" + "<td>" + " " + "</td>" + "<td>" + " " + "</td>" + "<td>" + "Item: " + $itemSet.ServerItem + "</td>" + "</tr>"; } } } if ($changeFound -eq $false) { $html += "<tr>" + "<td>" + " " + "</td>" + "<td colspan=3>" + "Nothing new included in build" + "</td>" + "</tr>"; } } $html += "</table>"; } else { $html = "Could not determine version information, check the log file."; } #---- End of code # Email notification messages $EmailFrom = "fromemail"; $EmailTo = "toemails"; $Subject = "Version: " + $Version + " deployed to Development"; $Body = $html; $SMTPServer = "smtpserver"; $SMTPClient = New-Object Net.Mail.SmtpClient($SmtpServer, 587); $SMTPClient.EnableSsl = $true; $SMTPClient.Credentials = New-Object System.Net.NetworkCredential("validemail", "password"); $Message = New-Object Net.Mail.MailMessage($EmailFrom, $EmailTo, $Subject, $Body); $Message.IsBodyHtml = $true; $SMTPClient.Send($Message); exit 0;
Comments
Developer
Redgate Software Ltd