Wow - it's been a long time. Sorry for the insanely long delay between posts, loyal reader(s). A lot has happened since I last managed to post something on my blog:
In any case, during a conference call the other day, a Team Build user expressed a desire to easily insert build steps into a build from within a csproj file... In previous posts I have laid out custom tasks which, as part of their execution, insert build steps. In this post, I lay out a simpler custom task which inserts arbitrary text as a build step - think of it as a <Message> task in Team Build form.
Here's the code:
using System; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; using Microsoft.TeamFoundation.Client; using Microsoft.TeamFoundation.Build.Proxy; namespace CustomTasks { public class BuildStepTask : Task { /// <summary> /// Execute the task logic. /// </summary> /// <returns>True. Exceptions thrown on failure.</returns> public override bool Execute() { BuildStore.AddBuildStep(BuildUri, Text, Text); BuildStore.UpdateBuildStep(BuildUri, Text, DateTime.Now, m_buildStatus); return true; } /// <summary> /// The text of the build step. /// </summary> [Required] public String Text { get { return m_text; } set { m_text = value; } } /// <summary> /// The status of the build step. If not specified, BuildStepStatus.Succeeded will be assumed. /// </summary> public String Status { get { return m_buildStatus.ToString(); } set { // Go ahead and throw an exception if the value is invalid. m_buildStatus = (BuildStepStatus)Enum.Parse(typeof(BuildStepStatus), value); } } /// <summary> /// The Url of the Team Foundation Server. /// </summary> [Required] public String TeamFoundationServerUrl { get { return m_tfsUrl; } set { m_tfsUrl = value; } } /// <summary> /// The Uri of the Build for which this task is executing. /// </summary> [Required] public String BuildUri { get { return m_buildUri; } set { m_buildUri = value; } } /// <summary> /// Lazy init property that gives access to the TF Server specified by TeamFoundationServerUrl. /// </summary> protected TeamFoundationServer Tfs { get { if (m_tfs == null) { m_tfs = TeamFoundationServerFactory.GetServer(TeamFoundationServerUrl); } return m_tfs; } } /// <summary> /// Lazy init property that gives access to the BuildStore service of the TF Server. /// </summary> protected BuildStore BuildStore { get { if (m_buildStore == null) { m_buildStore = (BuildStore)Tfs.GetService(typeof(BuildStore)); } return m_buildStore; } } private String m_text; private BuildStepStatus m_buildStatus = BuildStepStatus.Succeeded; private String m_tfsUrl; private String m_buildUri; private TeamFoundationServer m_tfs; private BuildStore m_buildStore; } }
And here's a snippet from the csproj file I used to test it out:
<UsingTask TaskName="CustomTasks.BuildStepTask" AssemblyFile="..\TeamBuildTypes\HelloWorld\CustomTasks.dll" /> <Target Name="AfterCompile" Condition=" '$(BuildUri)' != '' "> <BuildStepTask TeamFoundationServerUrl="$(TeamFoundationServerUrl)" BuildUri="$(BuildUri)" Text="Finished compiling $(AssemblyName)" /> </Target>
Note the condition on the Target to avoid running it outside of a Team Build environment.
Hopefully this post will be the first of many in the months to come.