MSBuild FlexibleConfigTask

A few months ago, we concentrated on a technical debt feature, so that we could improve our team’s development work flow. Our work became an MSBuild task & FlexibleConfig.FlexibleConfigTask, and this solved a lot of issues that used to bother developers.

This feature became high priority as a result. If you are suffering from similar problems, then you are welcome to purchase this feature though our products page.

Backlog

As a build manager, I need a task that allows me to write inline C#, to conditionally evaluate sections of a file. This will allow me to easily centralize changes in one file with conditional logic for various environments (Integration, QA, and Production Like Environment) that I need to support.

As a release manager, I want to automate replacing development configurations with production values, and to have them integrated with the existing file that the build managers and developers are maintaining.

As a developer, I want a task that can switch:

  • connection strings, appSetting values in Web.config and App.config
  • Silverlight endpoint locations in ServiceReferences.ClientConfig
  • configuration/system.serviceModel/services section of Web.config between mock versions of the WCF service and the concrete implementation.
  • customErrors mode properties in Web.config
  • forms timeout properties in Web.config
  • requireSSL properties in Web.config
  • WCF behaviors includeExceptionDetailInFaults and httpGetEnabled in Web.config
  • specific settings specific to a developer, without affecting integrating environments
  • a flexible Web.config and App.config, and that does not block a distributed workforce not sharing a common infrastructure during development

so that I can make the configuration highly flexible. This would allow me to run the project using mocks, without the specific infrastructure required to run the project.

Feature Prioritized

Having groomed the backlog, we noticed that these user stories have a common theme. So we grouped them into a feature called "FlexibleConfig Task". We hand picked a couple of users, and requested them to add some technical specifications to their story. Here are the specifications that were created:

  1. The input, BaseLineFile, to be processed, has to be broken into an abstract syntax tree.
    The grammar should allow for the following:
    • Include path directive to include external files into the input file being processed.
       
      				<%#include "/path/to/file.txt" %>
      				
    • Xml Include path directive to include external files inside an input file formed as xml.
       
      				<flexibleconfig path="/path/to/file.txt" /> 				
      				
       
      				<flexibleconfig path="/path/to/file.txt"> 	 </flexibleconfig>			
      				
    • Include path must allow for parameter replacement at run time.
       
      				<flexibleconfig path="$(MSBuildStartupDirectory)\bin\MailSettings.config" />		
      				
    • Support for inline C# code - this will be evaluated by the interpreter
       
      				<?="string value" ?> 				
      				

       
      				<?=((IsCondition)? "option1":"option2") ?> 				
      				
    • Support for large C# code blocks - this will be evaluated by interpreter
       
      				<?flexibleconfig  
      					// csharp code here
      					if(IsCondition)
      					{
      				?>
      					option1
      				<?flexibleconfig  
      					}
      					else
      					{
      				?>
      					option2
      				<?flexibleconfig  
      					}
      				?> 				
      				
    • Allow from C# to write to standard output when being interpreted
       
      				Hello<?flexibleconfig  
      				if( "HOME" == Environment.GetEnvironmentVariable("WORKLOCATION", EnvironmentVariableTarget.Process) )
      				{
      					writer.AppendLine(" World");
      				}
      				?> 				
      				
    • All other text that is not any of the above will be pushed to standard output, and remain unprocessed.
  2. As the input file is being processed, the interpreter will push the output stream to the OutputFile.
  3. If the input cannot be processed an error must be written to the ErrorLogFile.
  4. We must be able to pass Parameters into the file being processed. This has to have a collection of key value pairs that will be expanded when evaluating include paths and C# statements.

Introducing our MSBuild FlexibleConfig.FlexibleConfigTask

Our FlexibleConfigTask was unit tested to solve all the requirements described above. Our integration into an MSBuild script looks as follows.

 
	<Target Name="Sync">	
		<ItemGroup>
			<WwwParameters Include="MSBuildStartupDirectory">
			  <Key>MSBuildStartupDirectory</Key>
			  <Value>$(MSBuildStartupDirectory)\MSBuild</Value>
			</WwwParameters>
		</ItemGroup>
			
		<Message Text="Creating Www.Web.config" />
		<FlexibleConfig.FlexibleConfigTask
			Parameters="@(WwwParameters)"
			BaseLineFile="$(MSBuildStartupDirectory)\MSBuild\Www.Web.config"
			ErrorLogFile="$(MSBuildStartupDirectory)\MSBuild\bin\Error.log"
			OutputFile="$(MSBuildStartupDirectory)\MSBuild\bin\Www.Web.config"
		/>
	</Target>

Line 14, contains our custom MSBuild task that takes two inputs and provides two output:

  • Parameters: (input) is a dictionary that can be used inside the BaseLineFile.
  • BaseLineFile: (input) is the input file that is conditionally evaluated for each user/platform.
  • ErrorLogFile: (output) is the output location of the error log file in case the task fails.
  • OutputFile: (output) is the output location of the file that needs to be regenerated.

Sample Usage:

We have integrated our build tasks with every project that we are working on. The following are incomplete excerpts that show common examples where you can use this task.

  1. Resolving appSettings for various environments:
     
    	<add key="appSettingKey" value="<?= ((DevLocation == "QA")? "qabox":"integrationbox") ?>" />	
    	
  2. Resolving 'Production' values when pushing to production:
     
    	if(DevLocation=="PRODUCTION")
    	{
    		writer.AppendLine(System.IO.File.ReadAllText(LocationConfigurationPath + @"\mailSettings.config"));
    	}
    	
  3. Supporting developer specific settings during development.
     
    		<connectionStrings>
    		<?flexibleconfig
    		switch(DevId)
    		{
    			case "LEBLANC":
    		?>
    			<add name="dbhReader" connectionString="Data Source=LEBLANC-PC\MSSQLSERVER1;Initial Catalog=BusinessIntelligenceView;Integrated Security=True"/>
    			<?flexibleconfig
    				break;
    			case "DENIS":
    		?>
    			<add name="dbhReader" connectionString="Data Source=(local);Initial Catalog=BusinessIntelligenceView;Integrated Security=True;"/>
    			<?flexibleconfig
    				break;    
    			case "IGOR":
    		?>
    			<add name="dbhReader" connectionString="Data Source=OPTIMUS-PRIME\SQLEXPRESS;Initial Catalog=BusinessIntelligenceView;Integrated Security=True;"/>
    			<?flexibleconfig
    				break;
    			default:
    				throw new ArgumentException("DevId not defined when establishing a connection string.");
    				break;
    		}
    		?>
    		</connectionStrings>
    	
  4. Supporting staging environments and local development settings in ServiceReferences.xslt. VirtualPath is considered by BaseUrl.
     
    		<endpoint address="<?=BaseUrl ?>/Services/Security/SharedRoleService.svc" 
    			binding="customBinding" bindingConfiguration="CustomBinding_ISharedRoleService" 
    			contract="SharedRoleServiceProxy.ISharedRoleService" name="ISharedRoleService"/>
    			
    		<endpoint address="<?=BaseUrl ?>/Services/Security/PermissionService.svc"
    			binding="customBinding" bindingConfiguration="CustomBinding_IPermissionService"
    			contract="PermissionServiceProxy.IPermissionService" name="IPermissionService" />
    								
    	
 
Author: Leblanc Meneses
 
Gravatar

Leblanc has years of industry experience in all aspects of the software development life cycle. He has gathered and managed requirements, and has demonstrated his object-oriented architecture design with many UML and ER diagrams. He has also established his implementation abilities using a test-driven development approach, utilizing various languages and platforms. He has implemented continuous integration tools to maintain and integrate developed products.

He is currently a Silverlight/WPF/ASP.NET MVC/WCF Consultant in Dallas, Texas, and always welcomes inquiries from potential clients in Fort Worth, Dallas, Irving, Richardson, and Plano.

twitter @leblancmeneses linkedin

Be the first to comment!

X

Leave a Response

All comments on this article are moderated