I was recently asked how to trigger an Agent based release
from MSBuild. This is a valuable technique if you are upgrading from an older
version of TFS and want to use the Upgrade Template are already use MSBuild for
Maven or Ant builds with Team Explorer Everywhere.
The key is using the Exec task to make a call
to ReleaseManagementBuild.exe
<Exec WorkingDirectory="C:/Program Files (x86)/Microsoft Visual Studio 12.0/Release
Management/bin" Command="ReleaseManagementBuild
release -tfs "$(TeamFoundationServerUrl)" -bd "$(BuildDefinitionName)" -bn "$(BuildNumber)" –tp "$(TeamProject)" –ts "$(ReleaseTargetStage)" "/>
Most of the properties used in the call are provided by Team
Build with the exception of $ReleaseTargetStage. That is defined earlier in the
project file via a Property Group.
<PropertyGroup>
<ReleaseBuild>true</ReleaseBuild>
<ReleaseTargetStage>QA</ReleaseTargetStage>
</PropertyGroup>
The Release Target Stage identifies the desired stage to
deploy through. All the previous stages will also be deployed to but the
release will stop at the target stage. The Release Build property is used to
decide if this build should be released or not. It is used as the condition to
the AfterDropBuild target we override to trigger the release.
<Target Name="AfterDropBuild" Condition=" '$(ReleaseBuild)'=='true' ">
Only when Release Build is set to true will the build
trigger a release in Release Management. The values set in the Property Group
are just the default values each of which can be overwritten from the build definition.
If you are using Tokenization you will have to add additional
targets. For an explanation of Tokenization please see the attached Release
Management for Visual Studio 2013 User Guide PDF. Before the build is dropped
we will need to swap the token files. We can make sure the swap happens by
defining a dependency on the Before Drop Build target.
<PropertyGroup>
<BeforeDropBuildDependsOn>
SwapTokenFile;
</BeforeDropBuildDependsOn>
</PropertyGroup>
<Target Name="BeforeDropBuild" DependsOnTargets="$(BeforeDropBuildDependsOn)" />
Now we just need to define the SwapTokenFile target.
<Target Name="SwapTokenFile">
<BuildStep TeamFoundationServerUrl="$(TeamFoundationServerUrl)" BuildUri="$(BuildUri)" Name="SwapTokenFileBuildStep" Message="Swap
token files">
<Output TaskParameter="Id" PropertyName="SwapTokenFileBuildStepId"
/>
</BuildStep>
<Message Text="Swap
token files" />
<Exec WorkingDirectory="$(BinariesRoot)/ " Command="del
/Q web.config" />
<Exec WorkingDirectory="$(BinariesRoot)/ " Command="rename
web.config.token web.config" />
<BuildStep TeamFoundationServerUrl="$(TeamFoundationServerUrl)" BuildUri="$(BuildUri)" Id="$(SwapTokenFileBuildStepId)" Status="Succeeded"
/>
<OnError ExecuteTargets="OnSwapTokenFileTargetFail"
/>
</Target>
You will need to update each Exec task to point the folder
containing the files to be swapped. In the example above we are pointing to a
web.config file but you can tokenize any text based file. The BuildStep,
Output, Message and OnError tasks could be removed but are shown for
completeness.
When you put everything together you will end up with
something like this:
<PropertyGroup>
<BeforeDropBuildDependsOn>
SwapTokenFile;
</BeforeDropBuildDependsOn>
</PropertyGroup>
<Target Name="BeforeDropBuild" DependsOnTargets="$(BeforeDropBuildDependsOn)"
/>
<Target Name="SwapTokenFile">
<BuildStep TeamFoundationServerUrl="$(TeamFoundationServerUrl)" BuildUri="$(BuildUri)" Name="SwapTokenFileBuildStep" Message="Swap
token files">
<Output TaskParameter="Id" PropertyName="SwapTokenFileBuildStepId"
/>
</BuildStep>
<Message Text="Swap
token files" />
<Exec WorkingDirectory="$(BinariesRoot)/" Command="del
/Q web.config" />
<Exec WorkingDirectory="$(BinariesRoot)/" Command="rename web.config.token web.config"
/>
<BuildStep TeamFoundationServerUrl="$(TeamFoundationServerUrl)" BuildUri="$(BuildUri)" Id="$(SwapTokenFileBuildStepId)" Status="Succeeded"
/>
<OnError ExecuteTargets="OnSwapTokenFileTargetFail"
/>
</Target>
<Target Name="OnSwapTokenFileTargetFail">
<BuildStep TeamFoundationServerUrl="$(TeamFoundationServerUrl)" BuildUri="$(BuildUri)" Id="$(SwapTokenFileBuildStepId)" Status="Failed"
/>
</Target>
<PropertyGroup>
<ReleaseBuild>true</ReleaseBuild>
<ReleaseTargetStage>QA</ReleaseTargetStage>
</PropertyGroup>
<Target Name="AfterDropBuild" Condition="
'$(ReleaseBuild)'=='true' ">
<BuildStep TeamFoundationServerUrl="$(TeamFoundationServerUrl)" BuildUri="$(BuildUri)" Name="StartReleaseBuildStep" Message="Starting
Release">
<Output TaskParameter="Id" PropertyName="StartReleaseBuildStepId"
/>
</BuildStep>
<Message Text="Starting
Release, located at '$(PackageLocation)' for $(ReleaseTargetStage) stage."
/>
<Exec WorkingDirectory="C:/Program
Files (x86)/Microsoft Visual Studio 12.0/Release Management/bin" Command="ReleaseManagementBuild
release -tfs "$(TeamFoundationServerUrl)" -bd "$(BuildDefinitionName)" -bn "$(BuildNumber)" –tp "$(TeamProject)" –ts "$(ReleaseTargetStage)" "/>
<BuildStep TeamFoundationServerUrl="$(TeamFoundationServerUrl)" BuildUri="$(BuildUri)" Id="$(StartReleaseBuildStepId)" Status="Succeeded"
/>
<OnError ExecuteTargets="OnStartReleaseTargetFail"
/>
</Target>
<Target Name="OnStartReleaseTargetFail">
<BuildStep TeamFoundationServerUrl="$(TeamFoundationServerUrl)" BuildUri="$(BuildUri)" Id="$(StartReleaseBuildStepId)" Status="Failed"
/>
</Target>
The On*TargetFail targets are for error reporting should
something fail during the release process. You can simply copy the code above
the close Project tag at the bottom of your MSBuild project file.
With this in place you can now trigger an Agent based release from MSBuild.