The Visual Studio 2010 Team Foundation Server (TFS) 2010 build system automates the compiling, testing, and deployment of software for development organizations of all sizes -- from small software efforts to large development enterprises. TFS's build services can scale out to accommodate development as the code base grows -- which facilitates TFS's adoption in most scenarios. The TFS build system can also scale out and scale up the build environment, which can greatly reduce the amount of time it takes a team to complete a build. Finally, TFS supports the use of Continuous Integration (CI) builds, which can improve your code by providing frequent feedback on build quality. This article covers the features available in TFS's build services and the different taxonomy options for creating a build environment, using a typical scenario for using the build capability.
TFS Build Scenario
To see the benefits of TFS 2010 and TFS build services, let's look at a simple example of a 500-employee organization that has approximately 50 people working with TFS. The TFS group's project teams include:
- a centralized 20-person development team
- a centralized test team that performs manual and acceptance testing (QA)
- a centralized build and deployment team that is responsible for building and deploying major releases. This team does not perform builds for the development team.
This simple team structure represents three main uses of TFS build services.
The development team is interested in continuous feedback on the quality of code that is being checked into TFS's source control. The code's quality is determined not only by whether it compiles but also by automated tests that are run as part of the build process. The build succeeds only if it compiles and all automated tests pass. It is acceptable to have all logical layers of the application run on one physical machine.
The test team's aim is to test the application at the end of each development iteration. The application that the team tests should be of high quality and free of any known defects. The application's logical layers are deployed to different physical machines based on the physical architecture.
The build and deployment team packages the software after it has completed and passed all testing and development cycles. Deployment may be manual or automated depending on the team's requirements.
To support the team structure, a branching strategy is employed that allows a separate code base each for the developer, tester, and build and
deployment teams. Figure 1 shows a branching strategy that has a Main branch representing the code base for QA testing.
Figure 1: Branching strategy in Source Control Explorer
Main branches into the Development folder to create development branches for each iteration. Once all testing is done and Main is ready to be released, a branch into the Release folder is created.
Figure 2 shows the branching from Main to Development. (This diagram is taken from the open source Visual Studio TFS Branching Guide 2010 v1.)
Figure 2: Branching diagram
When the developers are confident their code is ready, a merge will take place from Development to Main. When all testing is completed, a branch from Main to Release occurs.
This is important since the workflow for Development builds might differ from QA builds and Release builds. Having three separate code bases allows for three different build definitions to exist. (I discuss build definitions in more detail later.) But before a build definition can be created, there must be a build environment.
The build environment consists of one or more build machines. A build machine is a computer that has the TFS Build Services installed. This machine can be a physical or virtual machine. Any type of machine can be set up as long as the Build Services can be installed. The larger the software development effort, the more build machines may be needed.
A build environment requires configuration of a build controller and one or more build agents. A build controller is registered to one team project collection. Each controller manages the build agents and distributes the workload of the builds to the agent machines.
The build controller processes the workflow -- for example, creating a label in source control, naming the build, updating work items, and reporting the build's status. The controller's activities are not in themselves processor intensive, so it might work to have the controller installed on a virtual machine or even on the TFS application tier. However, in some situations the controller requires a large amount of memory, so it is important to have adequate memory on the controller machine.
Each build agent is registered to a build controller and does the processor-intensive work, such as getting files from source control, compiling the code, and running tests. More than one build agent may need to be part of the environment, depending how much build work needs to be done. You might also need to have more than one agent if machines need specialized capabilities. For example, if you build and deploy the database using database projects, you might require the build to be done on the machine that has the database server installed. The same might apply to building a web application: The build might need to be done on a machine that has the web server installed. It is possible to use tags to assign the agent the build is run on. A tag is an attribute that can be assigned to a build agent. The tag can be specified in a build definition. If tags are not used, the controller will decide what agent to use based on the current load on the agent machines.
For small teams, it may be adequate to have the controller and agent installed on the application tier server. However, in most situations, this is not a good idea. For medium-size teams, the controller can be installed on the application tier while the agents are installed on separate machines. For large teams, the controller might need to be installed on its own machine and the agents installed on separate machines.
Once the build environment is set up, you can create one or more build definitions. A build definition defines the workflow and process that occur in a build. Some of the properties that are set in a build definition are
- build definition name
- what triggers a build
- the workspace for the build
- the controller and drop folder
- the build process template
- the project to build
- the tests to be run
- specific agents to use based on a tag
- what version to get from source control
You create a new build definition using the New Build Definition wizard. The first screen of the wizard is simply the name of the build definition. This name should indicate the build definition's purpose. For example, if the build definition is for a Development branch as shown in Figure 2, it could be named Dev_CI, which stands for a Continuous Integration build of the development code base. The second screen in the wizard determines the trigger for launching the build.
- Manual means the build must be launched manually. A build definition for the QA branch (the Main branch shown in Figure 2) would be triggered manually. The same applies to a release build.
- Continuous Integration launches a build with every check-in. This gives instant feedback on the quality of each check-in, but it also launches a large number of builds. Therefore, Continuous Integration is not an option for anything other than very short builds.
- Rolling builds will launch a build on every check-in but will wait until the prior build that has been queued is completed.
- Gated Check-in launches a build when someone attempts to check in code. Rather than being checked in, the code is shelved. The build will first get the latest version from source control and then overlay the shelved code. If the build passes, the check-in is completed. Otherwise, the code is not checked in. This control helps prevent build breaks.
- Schedule sets the day and time each week a build is launched.
The wizard's third screen sets the workspace of the build definition. This maps the location of the code in source control to a location on the build
agent. By default, the build agent folder path starts with $(SourceDir). This variable is set on the build agent machine's Working Directory property.
This property by default has the following path: \$(SystemDrive)\Builds\$(BuildAgentId)\$(BuildDefinitionPath). The environment variable
$(BuildAgentId) is an automatically generated integer that identifies the build agent ID in the team project collection. The environment variable
$(BuildDefinitionPath) consists of the team project name and build definition name separated by a backslash. Thus, if the team project is named
ClientA, the build definition is named Dev_CI, and the build definition has a build agent folder set to $(SourceDir)\Application, as in Figure 4, the
physical path on the build agent is C:\Builds\1\ClientA\Dev_CI\Application.
Figure 4: Workspace mapping
The fourth screen in the wizard is the Build Defaults screen, which is where you select the controller and drop folder. The drop folder is a Universal Naming Convention (UNC) path where the final compiled code will be dropped. The service identity of the build agent must have rights to this shared folder.
The final screen in the wizard, the Process screen, is where you select the build process template, what project(s) to build, what tests to run, and other items for the build. This is the screen that determines what is going to happen when a build is queued.
Build Process Template
TFS 2010 Build Services is based on Windows Workflow Foundation (WF) 4.0. The build process template is a WF XAML file that controls the process and flow of a build. There are three out-of-the-box process templates:
- The Default template is used for .NET applications and build definitions created in TFS 2010.
- The Upgrade template is used when upgrading from TFS 2008 Team Build to TFS 2010 Build.
- The Lab Default template is used when TFS Build is used with the Lab Management Feature Pack.
The Default template is the most commonly used template. It has a workflow that handles all standard areas of a build:
- initializes the build by assigning a build number and creating the drop folder
- cleans the workspace and downloads source from source control
- compiles projects using MSBuild and creates a work item when a build fails
- runs automated tests
- associates work items and changesets with the build
- performs test impact analysis to identify what test cases should be run
- copies output to a drop folder
- checks in source if the build definition has a gated check-in trigger
You can configure which of these steps will be performed by setting the properties on the Process screen.
Process Template Customization
Even though the Default template is well suited for applications based on the .NET Framework, you may need to customize the workflow. For example, you might need to run special scripts to deploy the application or to assign a version number to the assemblies -- tasks that can only be done during a build. To accomplish this, you need to be familiar with the process template workflow and XAML. You will also need to decide whether an existing activity can be used or a custom activity needs to be created.
Activities -- the backbone of a WF application -- drive the processing of the workflow. Since activities are nothing more than .NET assemblies, it is possible to create custom activities and use them within a custom workflow.
To customize a process template, you must first open it in the Workflow Designer or in a text editor to work with the raw XAML. With each team project
created, a BuildProcessTemplates folder containing the three out-of-the-box templates is created in source control. I strongly recommend that you make
a copy of any process template that will be customized. Open the DefaultTemplate.xaml file in the Workflow Designer to display its contents in design
mode, as shown in Figure 5.
Figure 5: Default Template in the Workflow Designer
Understanding the Default template's workflow has a learning curve, but once you understand the workflow, the template is relatively easy to customize.
If custom activities are part of the workflow, they must first be stored in source control before they can be used in a build. Once the custom activities are in source control, you must configure the build controller(s) to point to the custom activities' location. You can do this in Team Explorer by following these steps:
- Open Team Explorer.
- Right-click Builds and select Manage Build Controllers.
- Select the controller and click Properties.
- Enter the appropriate path in the Version control path to custom assemblies field. Figure 6 shows the Build Controller Properties window with our example path.
Figure 6: Build controller properties
Running a Build
To queue a build, right-click the build definition name under the team project in Team Explorer and select Queue New Build. This opens a window that allows some of the build definition properties to be overridden. You can change the build controller along with the drop folder, the priority in the queue, and whether a build of a shelveset is done. You can also change other build definition properties, such as clean workspace, logging verbosity, perform code analysis, agent settings, and label sources.
When a build is running, a window opens showing the build's progress. Depending on the logging verbosity chosen, the current step in the workflow is shown in real time. Once the build is finished, the full build report is displayed, showing details of the build: build status, summary, test results, code coverage results, associated changesets, associated work items, and impacted tests.
From this report, you can open the drop folder, view the log, and choose whether to delete the build or retain it indefinitely. You can also assign a build quality that can be used to categorize the build. The report is stored in TFS for the time period set on the Retention screen of the build definition configuration. The retention policy is based on the build outcome. For each build outcome, the number of builds to retain and what to delete is specified. Delete options include details of the build (e.g., build steps, requestor, date/time queued); drop folder output; test results; label; and symbols.
The TFS 2010 build environment can make a development team more productive by creating fast, reliable builds and can improve the quality of code before it ever reaches the testers by allowing integration of automated tests in the build process. Additionally, the use of WF in TFS 2010 Build Services makes the task of customizing the build process much easier than in previous TFS versions. Finally, with TFS 2010's build support for deployment automation, development teams have more confidence that all components make it and nothing is missing from the build.
Brian Minisi is an ALM Ranger and a software architect for Computer Enterprises, Inc. (CEI), a consulting company specializing in Microsoft technologies. Follow him on Twitter: @brianminisi.