my continuous integration server (TeamCity) is configured to run all the unit tests in our app on build. Prior to running those tests, i need to change some of the appSettings to make them valid for our CI server. I'm achieving something similar for my web project by using the deployment project provided with Visual Studio. Can i do the same for a Test project?
Thanks, Gonzalo
It's possible to use Web.config Transformations for App.config files through a workaround.
You simply have to invoke the appropriate MSBuild tasks at the right stage in your build process.Add this code snippet to your project file:
<UsingTask
TaskName="TransformXml"
AssemblyFile="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.Tasks.dll" />
<Target Name="AfterCompile" Condition="exists('App.$(Configuration).config')">
<!-- Generates the transformed App.config in the intermediate directory -->
<TransformXml
Source="App.config"
Destination="$(IntermediateOutputPath)$(TargetFileName).config"
Transform="App.$(Configuration).config" />
<!-- Forces the build process to use the transformed configuration file -->
<ItemGroup>
<AppConfigWithTargetPath Remove="App.config"/>
<AppConfigWithTargetPath
Include="$(IntermediateOutputPath)$(TargetFileName).config">
<TargetPath>$(TargetFileName).config</TargetPath>
</AppConfigWithTargetPath>
</ItemGroup>
</Target>
Then add additional App.config files to your project for each build configuration where you wish to apply a transformation. For example:
<ItemGroup>
<None Include="App.config" />
<None Include="App.Release.config">
<DependentUpon>App.config</DependentUpon>
</None>
</ItemGroup>
Related resources:
Web.config Transformation Syntax for Web Application Project Deployment
.Config File Transformation
I have created a Visual Studio add in which can be used to transform app.config in the same way that web.config is transformed. You can find the add-in, SlowCheetah, at http://visualstudiogallery.msdn.microsoft.com/69023d00-a4f9-4a34-a6cd-7e854ba318b5.
I have posted a blog about how to get this working on a build server as well.
I suggest that you wrap your config lookups, extract an interface and stub this when testing.
Related
I'm attempting to automate the build process of my WPF project and set up a CI system. I'm following details found in Continuous Integration in .NET by Marcin Kawalerowicz and Craig Bernston. Specifically, details in chapter 3 pertaining to build automation in MSBuild. The authors provide a sample build file for a project consisting of a single C# file, which looks similar to
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build;Deploy;Execute"
xmlns="http://schemas.microsoft.com/developer/msuild/2003">
<PropertyGroup>
<Debug Condition="'$(Delete)'==''">false</Debug>
<OutputFile>SampleMSBuild.exe</OutputFile>
<OutputDirectory>Output</OutputDirectory>
</PropertyGroup>
<ItemGroup>
**<CompileFile Include="SampleMSBuild.cs" />**
<DeleteFiles Include="SampleMSBuild.exe;SampleMSBuild.pdb" />
</ItemGroup>
<Target Name="Clean">
...
</Target>
<Target Name="Build" DependsOnTarget="Clean">
**<Csc Sources="#(CompileFiles)"**
OutputAssembly="$(OutputFile)"
EmitDebugInformation="$(Debug)" />
<Target Name="Deploy">
...
</Target>
<Target Name="Execute">
...
</Target>
</Project>
My confusion arises from the two lines of code I've surrounded in asterisks. Although I now know how to build a project with a single source file, I am unsure how to scale that up to a WPF project where there is a predefined template with many source files that are placed in different directories. Is it simply a matter of including every source file in the project like so
...
<CompileFile Include="source1.cs;source2.cs;source3.cs;...;sourceN.cs"
...
What about files in a project that are NOT source code? For example, files found in the References folder of a WPF project. I can't imagine all these are to be compiled as well. Does the complied source code communicate with the rest of the uncompiled files in the project? What exactly is the relationship between source files and other non-source files when it comes to build automation in MSBuild?
Here is the scenario:
I have a website which has a web.config file along with many other environment specific config files like Web.Staging.config/Web.Release.config/Web.OnPrem.config
Now, I have configured the BeforeBuild Target in the csproj file of my website project:
<Target Name="BeforeBuild">
<TransformXml Source="Web.config" Transform="Web.$(Configuration).config" Destination="Web.config" />
</Target>
This works well when I setup bamboo to build and create artifacts in Release mode (so the deployed application has the transformed web.config file from web.Release.config But, when I change the bamboo to build and create artifacts using OnPrem configuration, it does not transform the web.config file correctly.
When I say setup bamboo to build in OnPrem config, I have actually change the configuration option to below:
/p:Configuration=OnPrem and also, I have changed the BambooBuild.proj to have
<ConfigurationToBuild Include="OnPrem|Any CPU">
<FlavorToBuild>OnPrem</FlavorToBuild>
<PlatformToBuild>Any CPU</PlatformToBuild>
</ConfigurationToBuild>
What am I missing here?
Because you can only build for one active configuration at a time, whichever configuration you're building is the one that will get substituted into the $(Configuration) variable.
What I normally do is:
build (with no transforms) to produce my DLL & other software artifacts
then I have a separate build process which runs transforms only, and produces separate (in your case) Web.Staging.config, Web.Release.config & Web.OnPrem.config files
in my deployment plans for separate environments, I pick the Web.xxx.config file I need for that environment and rename it to Web.config
You can use MSBuild from the command line just to run transformations. (Note the only difference here is Web.Staging.config or Web.Release.config):
Msbuild.exe /target:Transform "/property:TransformInputFile=path\to\Web.config;TransformFile=path\to\Web.Staging.config;TransformOutputFile=path\to\artefacts\Web.Staging.config" MSBuild.xml
Msbuild.exe /target:Transform "/property:TransformInputFile=path\to\Web.config;TransformFile=path\to\Web.Release.config;TransformOutputFile=path\to\artefacts\Web.Release.config" MSBuild.xml
A MSBuild.xml file suitable for use like this would contain something like the following:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0"
DefaultTargets="Deploy"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<UsingTask TaskName="TransformXml"
AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.Tasks.dll"/>
<Target Name="Transform">
<TransformXml Source="$(TransformInputFile)"
Transform="$(TransformFile)"
Destination="$(TransformOutputFile)"
StackTrace="$(StackTraceEnabled)" />
</Target>
</Project>
We're using an AfterBuild target in the vbproj to replace the config file depending on the selected configuration :
<Target Name="AfterBuild" Condition="'$(Configuration)' != 'Release'">
<Delete Files="$(TargetDir)$(TargetFileName).config" />
<Copy SourceFiles="$(ProjectDir)$(Configuration).config" DestinationFiles="$(TargetDir)$(TargetFileName).config" />
</Target>
As an example, let's say we have 3 configurations : Debug, Test, Release. Debug is the local debugging configuration. Test is a pre-production environment used for user-acceptance tests. Release is our production environment.
In the App.config file, we store the configuration for our Release environment. In the Debug.config file, we store the configuration for our local debugging needs. In the Test.config file, we store the configuration for our user-acceptance environment.
The goal of the AfterBuild target is to replace the Release configuration (App.config) when building/executing with the Debug configuration (Debug.config) or the Test configuration (Test.config).
Everything works as intended when we publish the application (Release, App.config) or if we build the application and launch the bin\<appname>.exe (Debug or Test).
However, if we Start the application from Visual Studio, with the Visual Studio hosting process, the correct configuration is copied to bin\<appname>.exe.config, but it seems that Visual Studio doesn't copy the correct configuration to bin\<appname>.vshost.exe.config. We tried cleaning the solution, performing a Rebuild before debugging, manually deleting the bin\<appname>.vshost.exe.config file before launching, but it seems that the hosting process always copies the configuration from the default App.config file. The same problem happens whether we try to Start with the Debug configuration or the Test configuration.
To add to the confusion, we created multiple test projects, using the same AfterBuild target, and some of them work correctly, others don't. All projects are using .Net Framework 4.5.1, but we also reproduced the problem with .Net 4.5. It doesn't seem to be caused by the project type, since we're able to reproduce the issue with a Console Application as well as a Windows Forms Application.
What could be causing the problem?
Alternatively, could we use another solution to manage the configuration for each of our environment?
Notes
We use the default App.config file for our Release environment
because ClickOnce doesn't seem to support AfterBuild targets.
We use Visual Studio Express 2013 for Windows Desktop, so we can't use any plugin like SlowCheetah.
Following Steve's suggestion, we moved the logic to the BeforeCompile target, specifying to replace the App.config depending on the selected configuration :
<Target Name="BeforeCompile">
<Delete Files="$(ProjectDir)App.config" />
<Copy SourceFiles="$(ProjectDir)$(Configuration).config" DestinationFiles="$(ProjectDir)App.config" />
<Output TaskParameter="DestinationFiles" ItemName="FileWrites"/>
</Copy>
</Target>
It wouldn't replace the file every time, and when it did, it didn't necessarily apply to the <appname>.vshost.exe.config file. We stumbled on another answer which guided us on the right path : Can Visual Studio automatically adjust the name of other file as it does with app.config?
Adding the following <Copy> command, we achieved the desired behavior :
<!--
Copy the application's .config file, if any.
Not using SkipUnchangedFiles="true" because the application may want to change
the app.config and not have an incremental build replace it.
-->
<Copy
SourceFiles="#(AppConfigWithTargetPath)"
DestinationFiles="#(AppConfigWithTargetPath->'$(OutDir)%(TargetPath)')"
OverwriteReadOnlyFiles="$(OverwriteReadOnlyFiles)"
Retries="$(CopyRetryCount)"
RetryDelayMilliseconds="$(CopyRetryDelayMilliseconds)"
UseHardlinksIfPossible="$(CreateHardLinksForAdditionalFilesIfPossible)"
>
<Output TaskParameter="DestinationFiles" ItemName="FileWrites"/>
</Copy>
We now have a config file for each configuration (Debug.config, Test.config, Release.config). Visual Studio replaces the App.config file with the correct one on each build/launch. The resulting <appname>.vshost.exe.config file contains the correct parameters.
Bonus
The underlying benefit with this solution is that we have one config file per configuration, so we can update the .vbproj file and replace, for example,
<None Include="Debug.config" />
<None Include="Release.config" />
with
<None Include="Debug.config">
<DependentUpon>App.config</DependentUpon>
</None>
<None Include="Release.config">
<DependentUpon>App.config</DependentUpon>
</None>
All config files will be grouped under the main App.config file in Visual Studio :
For me it helped to disable VisualStudio hosting process.
Remove the check mark in:
Project -> Preferences -> Debug -> Enable Visual Studio hosting process
This stops Visual Studio from overwriting the *config file.
I need to be able to transform my app.config file using msbuild. I can transform the file if it is called app.DEBUG.config or app.Release.config, but I cannot if I add one called app.PROD.config.
Using regular XDT transforms msbuild recognizes different web.config files if I select a different PublishProfile
msbuild path.to.project.csproj Configuration=Release PublishProfile=DEV
Apparently app.config does not work with this same setup. I can always create a specific build configuration for a DEV.config setup but it seems useless to have a separate build confirm for one app. A hacky way to do so is to just copy the correct app.config per environment POST-BUILD.
I've attempted to use the SlowCheetah plugin, but this only seems to transform the default DEBUG and RELEASE config files, which is not appropriate since I have more than two environments. If I indeed am using this wrong, please let me know what parameter I should pass at to msbuild to choose my app.DEV.config.
The expected result would be that msbuild would transform the app.config according to the transform that is customized called app.DEV.config or the one customized for app.PROD.config. I would expect that there is a parameter that I can pass to msbuild that would allow me to use the Release configuration but a transform with a different name per environment.
I think what is confusing is that we have the ability to make compile-time config transformations and then we have deployment-time config transformations.
In general, you use compile-time config transformations to make changes to your locally-defaulted config file so that it is appropriate for a DEBUG or RELEASE configuration (or any custom configuration you define). For web.config, the tooling is built-in. For app.config, the SlowCheetah Visual Studio extension brings the same capability that we have for web.config to app.config. An example of a config transform for a RELEASE configuration is to remove the debug attribute on system.web compilation.
Deployment-time config transformations are manipulations of the config file while deploying to a specific environment (e.g. QA, PROD). Database connection strings need to change, service endpoints change, etc... For web.config, MSDEPLOY is the IIS tool of choice. For app.config, it seems we need to rely on installer technology. There are different tools for this, like WIX for example.
Anyway, I hope this short explanation of the distinction between compile-time and deployment-time config transformations helps explain why the toolset is fragmented. For more in-depth analysis, you can refer to a blog post I made on this subject: http://philippetruche.wordpress.com/2012/07/11/deploying-web-applications-to-multiple-environments-using-microsoft-web-deploy/
If choosing to use the WIX toolset to produce installers, refer to Creating Multi-Environment Windows Installers with Visual Studio 2012 and Wix.
App.config transforms
To test if transforms work you have to use real transforms. Insert-transform with appSettings block is maybe simplest one. I tested with following configuration files.
App.config:
<?xml version="1.0" encoding="utf-8" ?><configuration> <appSettings> <add key="FirstName" value="Gunnar"/> </appSettings></configuration>
App.Release.config
<?xml version="1.0"?><configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform"> <appSettings> <add key="LastName" value="Peipman" xdt:Transform="Insert"/> </appSettings></configuration>
Configuration file after transform:
<?xml version="1.0" encoding="utf-8" ?><configuration> <appSettings> <add key="FirstName" value="Gunnar"/> <add key="LastName" value="Peipman"/> </appSettings></configuration>
Making App.config transforms work
Let’s see how to do it with console application.
Add App.config and App.Release.config to your project and fill them with content given above..
Unload console application project.
Right-click on project name and select "Edit <project file name>".
Project file is opened as XML-file and you can see what is inside
it.
Before closing tag of first property group add the following line:
<ProjectConfigFileName>App.Config</ProjectConfigFileName>
Find <ItemGroup> where App.Config is defined (<None
Include="App.Config" />) and add the following block after
App.Config node:
<None Include="App.Release.config">
<DependentUpon>App.Config</DependentUpon>
</None>
Find first <Import Project= node and add the following import as
last one to list:
<Import Project="$(VSToolsPath)\Web\Microsoft.Web.Publishing.targets" Condition="'$(VSToolsPath)' != ''" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.targets" Condition="false" />
To the end of file, just before tag, paste the following
block of code:
<UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.Tasks.dll" />
<Target Name="AfterCompile" Condition="exists('app.$(Configuration).config')">
<TransformXml Source="app.config" Destination="$(IntermediateOutputPath)$(TargetFileName).config" Transform="app.$(Configuration).config" />
<ItemGroup>
<AppConfigWithTargetPath Remove="app.config" />
<AppConfigWithTargetPath Include="$(IntermediateOutputPath)$(TargetFileName).config">
<TargetPath>$(TargetFileName).config</TargetPath>
</AppConfigWithTargetPath>
<AppConfigWithTargetPath Include="$(IntermediateOutputPath)$(TargetFileName).config">
<TargetPath>$(TargetName).vshost$(TargetExt).config</TargetPath>
</AppConfigWithTargetPath>
</ItemGroup>
</Target>
Save project file, close it and reload it.
When loading project again Visual Studio may ask you about some modifications to file so all versions from Visual Studio 2010 to current are able to use your project file with no modifications to it. Agree with it because then you don’t have dependencies to Visual Studio versions.
This is what I use for this scenario:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- This target will run right before you run your app in Visual Studio -->
<Target Name="UpdateWebConfigBeforeRun" BeforeTargets="Build">
<Message Text="Configuration: $(Configuration) update from web.template.$(Configuration).config"/>
<TransformXml Source="web.template.config"
Transform="web.template.$(Configuration).config"
Destination="web.config" />
</Target>
<!-- Exclude the config template files from the created package -->
<Target Name="ExcludeCustomConfigTransformFiles" BeforeTargets="ExcludeFilesFromPackage">
<ItemGroup>
<ExcludeFromPackageFiles Include="web.template.config;web.template.*.config"/>
</ItemGroup>
<Message Text="ExcludeFromPackageFiles: #(ExcludeFromPackageFiles)" Importance="high"/>
</Target>
</Project>
I have the following setup:
web.template.config
- web.template.debug.config
- web.template.production.config
- web.template.release.config etc
Should work cross pc without the need for extra plugins etc. In your scenario, you need to edit the contents to say app. instead of web.
You can transform the app.config the same as the web.config.
Use this nuget package located here https://github.com/acottais/msbuild.xdt
There are several other Nuget packages that will allow you to do this. I used this one the other day and it worked.
After installing it is as simple as Add-Transform from the VS Window.
I am creating a .net web application and have some build tasks eg msbuild copy task. I add this by editing the project file for the application and adding the task.
While this works ok, is there any way I can use an external xml file to the project file and have my build tasks in this with the main project file referencing it? I would much prefer this as I wouldn't then have to edit the main project file and there is separation between the project file and the build tasks.
You can simply reference any external project or target file by adding an import to your main project file:
...
<!-- this is the default import for (c#) web project files -->
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v9.0\WebApplications\Microsoft.WebApplication.targets" />
<!-- import your custom project/target file here -->
<Import Project="MyCustom.targets" Condition="Exists('MyCustom.targets')" />
...
Adding the Condition will allow to build your main project even if your custom project/target file is missing because the build is run in a different environment.
I'm using this approach to run FxCop and StyleCop targets on my local machine but the same main project file can be built without any changes in my staging environment.
Update
Your comment suggests that you actually are looking rather for a solution that should work the other way round: You want to execute some steps before and after building your project without modifying the project configuration itself.
In that case the best way is to create your custom project to call the build of your web project. This could look something like this:
<Project DefaultTargets="MyTargetAfterBuild">
<!-- some Project attributes omitted for readability -->
<Target Name="MyTargetBeforeBuild" ContinueOnError="false">
<Exec Command="svn export" />
</Target>
<Target Name="Build" DependsOnTargets="MyTargetBeforeBuild">
<MSBuild Projects="MyWebProject.csproj" Targets="Build" Properties="Configuration=Release" >
</MSBuild>
</Target>
<Target Name="MyTargetAfterBuild" DependsOnTargets="Build">
<Exec Command="powershell.exe .\MyCustomScript.ps1" />
</Target>
</Project>
You might be interested in this answer on a similar scenario with a more detailed example.
You can call msbuild.exe, targetting your separate build file, in a pre-build or post-build event.