Introduction
Some of us are still bound to XAML-driven builds in TFS, but with the advent of Pre- and Post-build script capability for these builds, there are new, easier ways to perform activities. This tip shares how to use a post-build PowerShell script to take the output of a build and check those binaries into a shared library in TFS. This is actually a follow-up to a previous article.
Background
We have a very large code base with many shared binary assemblies, and we love our continuous integration. There are a number of solutions in this code base that rely on the shared binaries being built in a certain sequence. Moreover, when the solutions that produce these shared binaries are run, the build process needs to check those binaries into TFS so that the latest binaries are available to subsequent builds. This allows our nightly builds to have all the latest and greatest binaries, and produces a solid build for our QA team to start out their day. With PowerShell and the Microsoft.TeamFoundation.Powershell snap-in, this process is much simpler than what it had been in the past.
However, XAML builds aren't without their issues. If you have to override a policy to check in a file, the build system throws errors, in spite of a successful check-in. This tip will address how to override those errors thrown by the build process with a simple change to the build template.
Using the Code
The PowerShell script itself is relatively simple. There are parameters that need to be passed from the command line in the build definition, and a couple of string
s are defined for use in the script. The workspace as defined in the Source Settings of the build definition needs to be passed in, along with the name of the TFS project collection. The workspace is the target of the binary output, the place where the files are to be checked in. For example, if the mapped workspace is "$(SourceDir)\Share\lib\Baselibs
," then the workspace parameter is simply "\Share\lib\BaseLibs.
" Here's the script in its entirety:
Param(
[Parameter(Mandatory=$true)]
[string]$workSpacePath,
[Parameter(Mandatory=$true)]
[string]$projectCollection,
[string]$WorkingDirectory= $Env:TF_BUILD_BUILDDIRECTORY,
[string]$DF = $Env:TF_BUILD_DROPLOCATION
)
Try
{
Add-PSSnapin Microsoft.TeamFoundation.Powershell
$tfsServerString = ("http://tfs.myserver.com:8080/tfs/" + $projectCollection)
$tfs = get-tfsserver $tfsServerString
Write-Host ("tfs = " + $tfs)
Write-Host ("drop folder = " + $DF)
Write-Host ("workspace path = " + $workSpacePath)
Write-Host ("working directory = " + $WorkingDirectory)
$tfexe = 'C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE\TF.exe'
Write-Host "Get TFS Workspace"
$wsp = $WorkingDirectory + "\src" + $workSpacePath
Write-Host ("wsp = " + $wsp)
Write-Host "Copy the DLLs from the drop folder to workspace folder"
Copy-Item -LiteralPath $DF -Include "*.dll" -Destination $wsp
Write-Host "Add-TfsPendingChange"
Add-TfsPendingChange -Edit -Item $wsp -Recurse
Try
{
Write-Host "tf.exe checkin"
& $tfexe checkin $wsp /comment:"***NO_CI***"
/bypass /noprompt /recursive /override:"Auto-Build: Version Update" | Out-Null
}
Catch [system.exception]
{
Write-Host "caught the exception. Continuing."
}
Write-Host "Binaries checked in."
Write-Host "Done!"
exit 0
}
Catch {
Write-Error $_
exit 1
}
Invocation from the build definition is straightforward: -workSpacePath "\Share\lib\SharedLibs" -projectCollection "DefaultCollection"
The first step in the process is to add the PSSnapin for Microsoft.TeamFoundation.Powershell
, which provides cmdlets that facilitate a couple of important operations, such as determining the TFS server, and adding a Pending Change. It's worth noting that the New-TfsChangeset
cmdlet (as of this writing, at least), doesn't perform as expected, so the option of using the TF.exe command has been taken up instead.
The basic premise is to find the TFS workspace, find the TFS destination folder, and copy the files that already exist in that TFS folder from the build output. By adding the pending change and then using the TF.exe command to execute the checkin, this should always work. In prior implementations, it was necessary to check for pending changes, undo those pending changes, and then check in. That is no longer the case.
Points of Interest
There's a very important caveat here: if there are check-in policies in place, they will need to be overridden. This is easily done in the tf.exe command, using the /bypass
and /override
parameters, as illustrated in the script. However, the build process sees these override operations as errors. Unfortunately, these errors don't bubble up to the PowerShell script, so the build will fail. This is a bad thing. Note that two exit codes have been added to the PS script, a 0 for success, and a 1 for some error that has not yet been encountered. An IF
block has been added to the XAML template in the Compile, Test and Publish
section. Check for the scriptResult
, which is an Int32
variable added to the build template, If the returned value from the script is 0
, then the Properties of the Microsoft.TeamFoundation.build.Workflow.Activities.SetBuildProperties
object in the THEN
block is set to "Build Succeeded
," otherwise, the ELSE
block is set to "Build Failed
."
History
- 9th February, 2016: Initial version