Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / DevOps / TFS

TFS Post-Build Check-in Using PowerShell

5.00/5 (2 votes)
9 Feb 2016CPOL3 min read 24K  
Use PowerShell Post-build scripts to check in binaries to TFS for continuous integration during the build process.

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 strings 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:

PowerShell
Param( 
  [Parameter(Mandatory=$true)]
  [string]$workSpacePath, 
  [Parameter(Mandatory=$true)]
  [string]$projectCollection,
  [string]$WorkingDirectory= $Env:TF_BUILD_BUILDDIRECTORY,
  [string]$DF = $Env:TF_BUILD_DROPLOCATION
)
Try
{
#
# Get the TeamFoundation PowerShell cmdlets 
#
    Add-PSSnapin Microsoft.TeamFoundation.Powershell
# Identify the server (might be a better way for this)
    $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)
 
# The TF.exe command, since the New_TfsChangeset doesn't work properly
    $tfexe = 'C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE\TF.exe'
    
    # Construct the full workspace from the working directory + "\src" and the work space path.
    # In TFS 2010, "src" was "source". 
    # Now it's "src". That's pretty important.
	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 
 
    # Create the Pending Change, and use the -Edit option 
    # - we're replacing existing binaries, nothing else.
    Write-Host "Add-TfsPendingChange"
    Add-TfsPendingChange -Edit -Item $wsp -Recurse
 
    # Check in the Pending Change. New-TfsChangeset doesn't work, so we're dropping down to TF.exe.
    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."

Image 1

History

  • 9th February, 2016: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)