Get started with some basic TFBuild scripts

Get started with some basic TFBuild scripts

Rate This
  • Comments 26

You want to run some of your business logic during your TFBuild process. Maybe before, or maybe after Team Foundation Build (TFBuild) compiles or tests your code. For example, before you compile, you want to set the version of your assembly files. Or after you compile, you want to control the build outputs that get dropped to your staging location.

Until recently, you would have to customize your build process. Plenty has been written about how to develop a custom build process. I recently released a topic that I hope makes getting started on this kind of effort a bit easier: Create a custom build process. Regardless, developing and using custom build process activities of any complexity can be tricky and requires a learning curve.

What if you just want to run a simple batch (.bat) file? Or maybe you need a bit more power and you want to run a PowerShell script, perhaps to leverage even a bit of .Net code. Well, I'm here to share some good news: with Visual Studio and Team Foundation Server 2013 you can do it! In fact, to help you kick off your adventure in TFBuild scripting, following are a few examples you can literally copy, paste, and check in to your team project and use today.

As you can see, the following build definition is based on the default template; no customizations up my sleeve:

Default build process that runs basic scripts

Before you begin using these scripts, a few bits about how they work:

  • ApplyVersionToAssemblies
    • The Build number format is set to provide the script with the version data it needs in 0.0.0.0 form, as shown above.
    • The script applies the version data to your AssemblyInfo files so that it is stamped on the binaries when they are compiled.
  • GatherItemsForDrop
    • Output location is set to AsConfigured so that the script controls what output files are dropped later in the build process.
    • After the code is compiled, the script copies binaries from the source folder to the binaries folder so that these files can later be dropped to your staging location.

To use these scripts, simply copy the code below into .ps1 files, check them into your codebase, and then reference them from your build process as shown above. Also make sure to include the folder that contains your script on the build definition Workspace tab.

Run a script before building: version your assemblies

While there are many tasks you might want to perform before MSBuild compiles your code, one of the most common of these might be to apply a version to your assemblies. Jim Lamb wrote a canonical blog post on build process customization: Create a Custom WF Activity to Sync Version and Build Numbers. I patterned the following script using his approach to assembly versioning.

You can get this script here: https://tfsbuildextensions.codeplex.com/SourceControl/latest#Scripts/ApplyVersionToAssemblies.ps1

Run a script after building: prepare your binaries to be staged

When you set Output location to AsConfigured, TFBuild yields control of which binaries are dropped to you. The binaries are compiled and left in the source directory, organized essentially the same way as if you used Visual Studio to compile them locally on your dev machine. The following script gathers some of the typical binary types from the typical locations and copies them to the folder from which TFBuild copies and drops to your staging location.

You can get this script here: https://tfsbuildextensions.codeplex.com/SourceControl/latest#Scripts/GatherItemsForDrop.ps1

A few final bits

You'll notice that these scripts leverage some environment variables to get the key bits of data they need to perform their functions. I've cataloged these and all the other environment variables that are available to your scripts: Run your scripts in a default build process. (Note: there is a known bug with the TF_BUILD_TESTRESULTSDIRECTORY not being correctly handled by TFS 2013 Preview. This bug will be fixed in the next release.)

To debug the script while logged on to your dev machine or on the build server, you can manually set the TF_BUILD environment variables in your PowerShell console session. In fact, you'll notice the scripts automatically prompts you to do so. Use the -verbose parameter if you want to get details about what the script is doing and view them from the diagnostic log. Specify -disable if you want to disable any changes the script might make while leaving it referenced by your build process.

I invite you to post your comments here. For questions, I encourage you to post them on our forums: http://social.msdn.microsoft.com/forums/en-us/home.

Leave a Comment
  • Please add 2 and 1 and type the answer here:
  • Post
  • Curious why you used "$PSBoundParameters.ContainsKey('Disable')" instead of $Disable.IsPresent ?

    -dave

  • Would be great to integrate a checkout/checkin for the assembly Version script.

    Regards

    Thorsten

  • @David Gardiner: Thanks for the tip!

    @Thorsten: Agreed. I thought some about how to provide that option. In the spirit of agile, I decided to ship these in their current form now.

  • @Andy, can we expect to get complete build script examples with check-in, check-out of the of the assembly version file? Agile doesn't mean you can ship incomplete forever...and I worry that this post will be mothballed for all eternity here on MSDN without completed scripts.

  • @Kevin T, @Thorsten, and all others who want to check in the assembly files: I've talked this angle over with a few folks on the product team. I've got a fix in hand to do this, but before I update this post, please share some information about your scenarios. Specifically:

    1. Why do you want to check in the change to the assembly files? I ask because in this scenario, the data that supplies the version stamp is automatically generated. So please help me understand: What benefit do you get from hard coding it each time into your assembly files?

    2. Do you also want to associate the changeset generated by the assembly file check-in with the build? I need to know because doing this is tricky and likely would require you to customize the default build process the way it exists in its current form today.

    The answers to these questions will not only help me formulate the next version of these scripts, but also may help the product team make some decisions about future enhancements.

  • @Andy - please post the updated scripts.

    To answer your questions:

    1) We checkin the assembly files because we don't trust labels (slip the label, etc). We need to have the assembly version in source control so when we re-gen the build (turning off increment) we have the exact same binaries / version numbers.

    2) Yes we also want the associated changeset for the build to include the fact that the TFS Service account versioned the files. We need this to have a BOM that includes ALL changes that went into the build.

    Thanks for the dialogue and the script examples!

  • @Andy -For those of us who are using MTM we depend on the build number to be consisently stamped into the version info of the builds we are installing in our lab environment. We also have the build number stamped into our WIX installer, deployed SQL Database instance name, and we update our testplan description in MTM with the build number out of version control.

    We also would want the changesets in the build to include the fact that the build process itself touched the file and incremented the number so the build report would accurately ALL of the changes.

  • Taking a look at 2013, can you please post the complete scripts with checkin of the assembly?

  • Thanks to all who have given me feedback on the assembly version scenario! These scripts are a work in progress. I expect I will release them next in MSDN Library for our RTM release.

    Meanwhile, you can adapt the scripts to check out and check in as you see fit. Here's how:

    # to check out instead of manually setting a file to read-only,

    # replace this:

    #

    # attrib $file -r

    #

    # with this:

    & ($env:VS120COMNTOOLS + "..\..\common7\ide\tf.exe") edit $file

    # to check in:

    & ($env:VS120COMNTOOLS + "..\..\common7\ide\tf.exe") checkin $file /i

    You can use the beginning of this pattern to run any version control command such as label (msdn.microsoft.com/.../9ew32kd1.aspx).

    I've been sharing your feedback with the product team. So far as I can tell at the moment, associating the check-in with the build for this scenario cannot be done only with a script given the current design of the product.

  • @Andy - thanks for posting the command. The more examples you can provide the better!

  • Is possible to pass $(BuildDefinitionname) between 2013 build definitions using new script feature? ex. With success CI build, CI build script call into stage build script, with success stage build, stage build script call into QA build script? Deployment of SQL via script example would be very useful.

  • @Gary Carty: Hear you loud and clear.

    @Qiang Nuo: TFBuild does not have this capability.

  • @AndyLewis - There is no stage pipeline in TFSBuild and that makes build process very manual and more difficult to manage.

    We work 90% of the time with SQL code changes via database projects...deployment of SQL changes via DACPAC's with MSBuild parameters is hard, would new buildscripts make easier? Example SQL deploys with teambuild scripts would be most helpful.

  • I really dislike the use of $env to pass parameters to the script (i.e. $Env:TF_BUILD_SOURCESDIRECTORY)

    Why are these not explicit parameters that are passed to the script like this:

    param( $Binaries, $Sources, $DropLocation, $Configuration, $LogLocation, $BuildUri)

    "***AfterCompileBeforeTest.ps1***" | write-host

    "Binaries: $Binaries" | write-host

    "Sources: $Sources" | write-host

    "DropLocation: $DropLocation" | write-host

    "Configuration: $Configuration" | write-host

    "LogLocation: $LogLocation" | write-host

    "BuildUri: $BuildUri" | write-host

    Adding three PS script hooks into the 2010/2012 DefaultTEmplate.xaml is the first thing I do for any project.  I always pass them like this:

    **InvokeProcess**

    Filename: powershell.exe

    Arguments:

    String.Format("-File ""{0}"" -binaries ""{1}"" -sources ""{2}"" -droplocation ""{3}"" -configuration ""{4}"" -loglocation ""{5}"" -builduri ""{6}""", BuildHookPs1, BinariesDirectory, SourcesDirectory, BuildDetail.DropLocation, platformConfiguration.Configuration, BuildDetail.LogLocation, BuildDetail.Uri.ToString())

  • Very helpful post. Great sample ++ Please provide many more.

Page 1 of 2 (26 items) 12