Run Nant script on TFS Build server
I believe that all the tasks that nant does can be done by TFS build server as well, but TFS build tasks can only be performed on a TFS server, due to this reason we might end up defining the automated build process in two formats: TFS build on server, nant in a local development environment.
So question will be how about running nant script on TFS build server?
Here I want to demonstrate how to customize TFS build definition and build template to run nant script.
TFS server I am using is 2012.
I assume that you know how to create a build in visual studio in Team Explorer. Right click the build you can queue a build or edit the build, it turns out that just editing the build is far from enough to achieve what is supposed to be a very simple task: run a script.
It turns out that the build you created is based on a build template, it is the build template where we should customize.
You can create a new template by copying from an existing one, basically the default template DefaultTEemplate.11.1.xaml. Fortunately you do not have to work with template on text basis, when the template is opened using visual studio, you will have a UI to work with the template instead of its xml content, even better is that all the components of build template are organized into the toolbox, so that you can drag and drop.
The template edit screen in visual studio will be something like this
At the bottom on the status bar are three tabs: Variables, Arguments and Imports.
Trick one: lower the system log level for build
This change is not in template, but in build definition
EditBuildDefinition:Basic, LoggingVerbosity= Detailed
Displaying the details of what is going on of every build step is very helpful for monitoring and diagnosis, especially in the template development stage.
Trick two: create a variable or argument
On the bottom bar when you click either of these two tabs, there will be a list of existing variables or arguments , you can simply click the last row to create one. My understanding is variables are used by your subroutines, variables have scope, if scope is sequence then it is only accessible in a sequence, agent wise, then it is global.
While arguments are those that can be exposed by the build definition UI, especially when the direction is set to ‘IN’
Here I added an IN argument “RunNantScript” Boolean, and default value is false, to control the build process to run nant script only when this is set to true.
Trick three: find the place to add our script run
What we want to edit this template for is to add an action after the build process finishes normal build activities like check out code, build solution etc. The whole template is a big document, a big sequence, under the root sequence, there is a sequence called: Run On Agent, the last sequence of Run On Agent is Try Compile, Test, and Associate Changesets and Work Items, after this sequence is where I want to insert my nant script execution.
It starts from adding an if control flow, when it is dropped on the right place, right click it and choose properties to start to define it.
DisplayName is the what you will see on the xaml view
Condition put RunNantScript = True, so our extra action will be executed only RunNantScript is turned on.
Even though we only want to run script, still there are a few more steps to perform, so I added a sequence (Control Flow)into the then section of if block.
Trick four: InvokeProcess
Here is the key, to execute another program, you need to use InvokeProcess in Team Foundation Build Activities tab under toolbox, in its properties view you will find
FileName, this should be the file name for program you want to invoke, here is our nant.exe
Result , is the exit code of the program that has been run
Arguments, where you set what parameters to be passed to nant.exe we need script file and other params like which target to start.
WorkingDirectory, this is important too, as we run nant script under a particular folder, otherwise the working directory will be using nant.exe folder.
Then it came the problem of how to find nant.exe and script file. No matter how I set those properties, I just cannot get the build tool to find my file.
Trick five: use ConvertWorkspaceItem to get the file into a variable
When accessing file, always use ConvertWorkspaceItem in Team Foundation Build Activities
Result: a variable I created to hold the file place
Trick six: what is a workspace?
Workspace is a source control folder, in edit build definition window, there is menu called ‘Source Settings’ where you can add the source control folder. In the previous trick, how to decide input? The input must be full path of a file or a folder that are under a source control folder which is defined in Source settings in build definition window.
So if you have an active working folder $/DEV, the input of ConvertWorkspaceItem can be any time under $/DEV, but input should be the full path.
More on InvokeProcess:
When we run nant script we would like to see the details or echoes of nant script to make sure that nant has gone through the steps we defined in the script, this can be achieved by two tools provided by TFS, one is WriteBuildMessage which takes the variable stdOutput from the InvokeProcess as Message, this will output the nant message in TFS build log, in WriteBuildMessage, there is also Importance field to set, this will decide level of logging for these messages, when it is set to normal, then only when LoggingVerbosity= Detailed or higher can you see these message in the log of TFS build.
Another tool is WriteBuildError which will output all error messages or exceptions of nant into TFS build log.
Still this is not good enough, the problem is the invoke process does not pass on the failure status of invoked process to build process automatically, this is why we catch the exit code of InvokeProcess into a variable, then add another if block to respond to the none zero exitcode (a failure occurred for invoke process), in the then block , just throw an exception, in this way, if nant failed, the build will fail.