TFS Build vNext – A Preview

Currently, Microsoft is working hard on a complete rewrite of the TFS Build system. They announced this, among other things, at the Connect event back in November and did a short demo of it. It is not yet available, but as part of the MVP program, a few of us has now been fortunate enough to get access to an early preview of the new build system.

Note: This version is in pre-alpha state, meaning that a lot of things can and will change until the final version!

Microsoft will enable a beta version to a broader public on Visual Studio Online later on this year, and it will RTM in TFS 2015. I will write a series of blog posts about Build vNext as it evolves. In this first post I will take a quick look at how to create and customize build definitions and running them in the new Web UI.

Key Principals

The key principals of TFS Build vNext are:

  • Customization should be dead simple. Users should not have to learn a new language (Windows Workflow anyone?) in order to just run some tool or script as part of the build process
  • Provide a simple Web UI for creating and customizing build definitions. No need for Visual Studio to customize builds anymore.
  • Support cross platform builds (Linux, MacOS…)  As in most other areas right now, Microsoft is serious about supporting other platforms than Windows, and TFS Build won’t be an exception.
  • Sharing build infrastructure. The concept of a build controller tied to a collection is gone, instead we will create agent pools at the deployment level and connect agents to those.
  • Don’t mess with the build output and keep my logs clean. One of the most frustrating things about the current version of TFS Build is that by default it doesn’t preserve the output structure of the compiled projects, but instead places them beneath a common Binaries directory. This causes all sort of problems, such as post build events that are dependent on relative path etc.

    CI build == Dev build !

  • Work side by side with the existing build system. All your existing XAML build definitions will continue to work just like before, and you will be able to create new ones. But don’t expect anything to be added in terms of functionality to the XAML builds

 

Creating a build definition in vNext

In this post, I will show how easy it is to create a  new build definition in Build vNext, and customize it to update the version info in all the assembly files using a PowerShell task with a custom script.

Note: There will probably be a versioning task out of the box in the final release, but currently there isn’t.

Let’s get started:

  1. From the dropdown, you can select from a list of build definition templates. These templates are created by saving an existing build definition as a template. Currently, they are scoped to the team project level:

    image

    Here I’m going to select the Visual Studio definition template.

  2. This results in a build definition with two tasks, one that compiles the solution and one that runs all the unit tests using the Visual Studio test runner task

    image 

  3. Selecting a task shows the properties of that task to the right, the properties of the Visual Studio Build task is shown above. Some of these properties are typed, meaning that for example the Solution property let’s you browse the current repository to select a solution. By default, it will locate all solution files and compile them (using the ***.sln pattern)

    You can also see that the Platform and the Configuration property uses the variables $(platform) and $(config). We will come back to them shortly.

  4. The first thing we want to do typically is specifying which repository that the build will download the source from. To do this, select the Repository tab:

    image

    Looking at the Repository type field, since this is a Git team project it will let me choose either Git (which means a git repo in the current team project) or GitHub, which actually lets me choose any Git repo. In that case I will need to enter credentials as well. When selecting the Git repo type, i can then select the repo (Visual Studio ALM in this case) from the current team project, and then I can select the default branch in a drop down.

  5. If you want to add more tasks to the definition, go back to the Build tab and click on the Add new task link. This will display a list of all available tasks, shown below:

    image

    The list of tasks here are of course not complete, but as you can see there are a lot of cross platform tasks there already. To add a task to the build definition,  select it and press Add.
    Here, I will select the PowerShell task that I will use to version all my assemblies:

    image 

    When adding a new task it will end up at bottom of the task list, but you can easily reorder the tasks by using drag and drop. Since I must stamp the version information before i compile my solution, I have dragged the PowerShell task to the top. This task then requires me to select a PowerShell file to execute, and I also specified a working folder since the script I am using assumes this. All paths here are relative to the repository.

    Note: Although currently not possible, it will be possible to rename the build tasks

  6. On the Triggers tab, the only option available right now is to trigger the build on every check-in (or push in the case of Git). I can also select to batch the changes, which is the same thing as the Rolling build trigger in the existing version of TFS build.
    This means that all changes that are checked in during a running build will be batched up and processed in the next build as soon as the current build has completed.

    In addition we can specify one or more branches that should trigger this build.

     image

    Tip: The filter strings can include * as a filter, which lets me for example specify refs/heads/feature* as a filter that will trigger on any change on any branch that starts with the string feature.

  7. On the Variables tab, we can specify and add new variables for this build definition. Here you can see the config and platform variables that were referenced in the Build tab:

    image

  8. Last, let’s take a look at the Options tab. The MultiConfiguration option enables us to build the selected solution(s) multiple times, each time for every combination of the variables that are specified here. By default, it will list the config and platform variables here. So, this means that the build will first build my solution using Debug | Any CPU, and the it will compile it using Release | Any CPU.

    If I check the Parallel checkbox, it will run these combinations in parallel, if there are multiple agents available of course.

    image

    Also, we can enable copy to Staging and Drop location here. The build agent will copy everything that matches the Search pattern and place it in the staging folder

  9. Finally, let’s save the new build definition. Note that you can also add a comment every time you save a build definition:

    image

    These comments are visible on the History tab, where you can view the history of all the changes to the build definition and view the changes between any of the changes. Nice!

 

Running a Build

The build definition is complete, let’s kick off a build. Click on the Queue build.. button at the top to do this. This will show the live output of the build, both in a console output window and in an aggregated view where the status of each task is shown:

image

 

Here you can note several nice features of build vNext:

  • The console output shoes the exact output from each task, just like it would if you ran the same command on your local machine
  • The aggregated view to the left shows the status and progress of every task which makes it very clear how far in the process the build is
  • This view also show parallel builds, in this case I have enabled the MultiConfiguration option and have two build agents configured, meaning that both Debug|AnyCPU and Release|AnyCPU are being processed in parallel on separate agents.
  • If you select one of the tasks to the left, you will see the build output for this particular task

 
When the build completes, you will see a build summary and you can also view a timeline of the build where the duration of each task is shown:

 

image

Trigger Visual Studio Release Management vNext from TeamCity

The last couple of updates to Visual Studio has included a lot of new functionality for Visual Studio Release Management. The biggest one is the introduction of so called vNext releases, that leverages Powershell DSC for carrying out the provisioning and deployments of environments and applications.

Also included in Visual Studio 2013 Update 3 was the introduction of a REST API that allows us to both trigger new releases and read back information about them. This API is only available for vNext release templates, and will probably not be implemented for the “old” agentbased deployments.

This REST API opens up a lot of integration possibilities, for example we don’t have to use TFS Build to automatically trigger a release. In this post, I will show how we can use the popular TeamCity build server from JetBrains to trigger a release as part of the build.

 
First of all, we need to setup a release template in Visual Studio Release Management. I won’t go through all the details here, the most important once is that the components that we define must use the UNC Path as the source parameter:

image

Here I have specified a shared folder, \localhostbuildoutput. Beneath this path, VSRM will look for a folder that typically correspond to a build number that we will pass in using the API, as you will see later on.

Next, we create our release template. To be able to trigger a build using the API, make sure that you tick the “Can trigger a release from a Build?” checkbox:

 image

 

Then we define our deployment sequence, in this case I just have one action for deploying my web site using a PowerShell script:

SNAGHTML5c6e9ead

There is nothing really nothing special here, I refere to the DeployWebSite.ps1 which is a PowerShell DSC script that installs my web site. I have also a DestinationPath variable that is referenced in the DSC script and specifies where the web site should be installed on the server.

 

Now, to trigger a release of this release template we are going to use a PowerShell script, that we can execute using TeamCity. I have used a sample from Microsoft and modified it to use parameters.

Here is the full script:

 

param( [string]$rmServer, [string]$rmUser, [string]$rmPassword, [string]$rmDomain, [string]$releaseDefinition, [string]$deploymentPropertyBag ) $deploymentPropertyBag = $propertyBag = [System.Uri]::EscapeDataString($deploymentPropertyBag) $exitCode = 0 trap { $e = $error[0].Exception $e.Message $e.StackTrace if ($exitCode -eq 0) { $exitCode = 1 } } $scriptName = $MyInvocation.MyCommand.Name $scriptPath = Split-Path -Parent (Get-Variable MyInvocation -Scope Script).Value.MyCommand.Path Push-Location $scriptPath $orchestratorService = "http://$rmServer/account/releaseManagementService/_apis/releaseManagement/OrchestratorService" $status = @{ "2" = "InProgress"; "3" = "Released"; "4" = "Stopped"; "5" = "Rejected"; "6" = "Abandoned"; } #For Update3 use api-version=2.0 for Update4 use api-version=3.0. $uri = "$orchestratorService/InitiateRelease?releaseTemplateName=" + $releaseDefinition + "&deploymentPropertyBag=" + $propertyBag + "&api-version=3.0" $wc = New-Object System.Net.WebClient #$wc.UseDefaultCredentials = $true # rmuser should be part rm users list and he should have permission to trigger the release. $wc.Credentials = new-object System.Net.NetworkCredential("$rmUser", "$rmPassword", "$rmDomain") try { $releaseId = $wc.UploadString($uri,"") $url = "$orchestratorService/ReleaseStatus?releaseId=$releaseId" $releaseStatus = $wc.DownloadString($url) Write-Host -NoNewline "`nReleasing ..." while($status[$releaseStatus] -eq "InProgress") { Start-Sleep -s 5 $releaseStatus = $wc.DownloadString($url) Write-Host -NoNewline "." } " done.`n`nRelease completed with {0} status." -f $status[$releaseStatus] } catch [System.Exception] { if ($exitCode -eq 0) { $exitCode = 1 } Write-Host "`n$_`n" -ForegroundColor Red } if ($exitCode -eq 0) { "`nThe script completed successfully.`n" } else { $err = "Exiting with error: " + $exitCode + "`n" Write-Host $err -ForegroundColor Red } Pop-Location exit $exitCode

Basically, this script calls the following REST API endpoint:

http://RMSERVER/account/releaseManagementService/_apis/releaseManagement/OrchestratorService/InitiateRelease?releaseTemplateName=RELEASEDEFINITION&deploymentPropertyBag=PROPERTYBAG&api-version=3.0 

 

This REST endpoint returns a release id, which we can use to read the status of the release. The script loops until the status is not “In Progress” and the quits and returns an exit code depending on how the release went.

 

The parameters of the above endpoint are:

  • RMSERVER

    The URL including port to the release management server, typically somthing like contoso.com:1000 

  • RELEASEDEFINITION

    The name of the release template that we want to trigger.

  • PROPERTYBAG

    Now this one is a bit special. This is an array of key/value which is not yet documented but it typically looks like this:

  • { "Component1:Build" : "Component1Build_20140814.1", "Component2:Build" : "Component2Build_20140815.1", "ReleaseName" : "$releaseName" }

    The first two lines references two different components in RM. Here I have just called them Component1 and Component2. The :Build is a keyword and must be there. The value part is the build number, which RM will append to the UNC path that we defined earlier. Note that we can use different build numbers per component here if we want to.

    The last line is a predefined keyword that allows us to specify the name of the release, something that we actually can’t do using the RM client or the standard RM TFS Build release template so this is a nice feature.

 

So, to execute this script in TeamCity, add the above script to repository and then add a PowerShell build step at the end of your build in TeamCity that looks something like this:

image 

 

The Script Arguments property is the tricky part, since we have to escape the quotes for the propertyBag parameter, and we will also use the %env:BUILD_NUMBER% variable from TeamCity for the build number. Here is the full string as an example:

-rmServer SERVER:1000 -rmUser USER -rmPassword PASSWORD -rmDomain DOMAIN -releaseDefinition VSGallery -deploymentPropertyBag “{“VSGallery Web Application:Build” : “%env.BUILD_NUMBER%”,”ReleaseName” : “%env.BUILD_NUMBER%”}”

 

Running this build in TeamCity will now trigger a release in Release Management:

image