Wanted to pick the brains of those MS Build/ VS Post build exponents here.
I would like to have my web.config entries customizable per user/machine/environment.
I could have my configurable/changeable entries marked in the web.config and would like those entries overridden by the respective user/environment file and would like to have an order that decides which entries should trump the other if the entry is found in multiple files.
for eg: web.config has a $connectionstring entry and the customization files per user/environment could have the potential values to replace $connectionstring depending on the context/configuration the solution is built
which means, I could have a set of files like below:
user_joe.config
$connectionstring = db_where_joe_like_to_connect_to
staging.config
$connectionstring = db_where_staging_connect_to
production.config
$connectionstring = db_production
so if joe is compiling the solution from his Dev box, the web.config should have the value "db_where_joe_like_to_connect_to" for $connectionstring.
I am hoping there could be a solution that doesn't involve Nant.
hope someone can throw pointers.
You can use visual studio 2010's web.config transform settings.
http://weblogs.asp.net/gunnarpeipman/archive/2009/06/16/visual-studio-2010-web-config-transforms.aspx
This will allow each developer to have their portion of a web.config that can get merged in for their build settings.
Internally we use an event that was pieced together from various places on the net- since normally this happens during publishing and we wanted it to happen at compile time.
Add a BeforeBuild target
So - from the csproj file:
<Target Name="BeforeBuild">
<TransformXml Source="$(SolutionDir)Web.config" Transform="$(SolutionDir)Web.$(Configuration).config" Destination="$(SolutionDir)Web.$(Configuration).config.transformed" />
</Target>
<PropertyGroup>
<PostBuildEvent>xcopy "$(SolutionDir)Web.$(Configuration).config.transformed" "$(SolutionDir)Web.config" /R /Y</PostBuildEvent>
</PropertyGroup>
I would suggest using the configSource attribute in the web.config entries for debug builds. Then, in your test and releae builds you can use data transformations to insert the testing and production entries.
You would do something like this:
<connectionStrings configSource="myLocalConnectionStrings.cfg" />
Then you have a local file called myLocalConnectionStrings that you don't check into source control. In your Web.config.Release you simply transform the connectionStrings section to include the production strings and remove the configSource attribute.
As Adam said in his answer, you can kind of do this using web.config transforms. Basically you'd have to create a new solution configuration for each environment. Note that having one for each developer will likely quickly become unmaintaniable, as each configuration / platform combination can have it's own build settings.
Also, the transforms are ONLY applied during the web site packaging (calling the Package target). So if you're trying to use this so that joe and sally can have different configs on their own machine, this won't do that for you.
In that case you're probably better off trying to get everyone on the same configuration, than allowing configs to fragment. The more differences between each environment, the harder time you'll have deploying.
Here is a T4 solution. This worked for my case because this was an internal tool that would only be used by developers and because I don't need further processing for the "included" files.
File name App.tt.
<## template debug="false" hostspecific="true" language="C#" #>
<## import namespace="System" #>
<## import namespace="System.IO" #>
<## output extension=".config" #>
<#
string pathToConfigurations = Host.ResolvePath("Configurations");
string pathToMachine = Path.Combine(pathToConfigurations, Environment.MachineName + ".config");
if (File.Exists(pathToMachine))
{
Write(File.ReadAllText(pathToMachine));
}
else
{
Write(File.ReadAllText(Path.Combine(pathToConfigurations, "App.config")));
}
#>
Related
Used MS Studio Community 2015 with SP2.
I need to recompile T4 on the build.
I do add to the project :
<Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v14.0\TextTemplating\Microsoft.TextTemplating.targets" />
and
<PropertyGroup>
<TransformOnBuild>true</TransformOnBuild>
<TransformOutOfDateOnly>false</TransformOutOfDateOnly>
This force to recompile T4's.
But compilation have a problem - instead of using CustomToolNamespace given for specific T4 are used common RootNamespace. Result are a complete disaster.
I paly with location of CustomToolNamespace in the project file, but without positive result.
Point me where to look - still try to play with *.csproj or start look for debugging Microsoft.TextTemplating.targets?
Or simply generate 'tempalaterecompilation.bat' and run it on pre-build? I very much dislikes this way.
I do check a Microsoft.TextTemplating.targets.
It's contain a definition for namespace
<PropertyGroup>
<!-- Unless another namespace has been specified, use the project namespace as the
default namespace from pre-processed files. -->
<PreprocessTemplateDefaultNamespace Condition=" $(PreprocessTemplateDefaultNamespace)=='' ">$(RootNamespace)</PreprocessTemplateDefaultNamespace>
</PropertyGroup>
So, RootNamespace are used intentionally.
What I need to change to get target to use a CustomToolNamespace defined for a template?
I do find temporary solution for a problem.
There are few places from which Namespace can be picked up.
One of them - ClassNamespace​ in Content section. If this are specified given value will be used as a Namespace.
But this is work-around and I still look for solution using *.targets.
this is my situation:
I have VS2010 solution with X projects included.
Wix project that can create msi from all compiled artifacts.
I have build machine \ Jenkins that first compile (MSBuild .Net 4) all the solution, then compile the wix to package it to msi.
What\how can I inject to all artifacts\dlls the number of the product (e.g 11.2.0.4789) - as simple as possible?
Is there and command line arguments that can be passed while compiling the solution?
There are tools, such as several extensions for MSBuild, that do version stamping but each assumes a particular workflow. You might find one that works for you but a DIY method would help you evaluate them, even if it isn't your final solution.
You can add a property to the MSBuild command-line like this:
msbuild /p:VersionStamp=11.2.0.4789
Note: I assume you are going to parameterize the Jenkins build in some way or generate the number during a preceding build step. Here is a simulation of that:
echo 11.2.0.4789 >version.txt
set /p version=reading from pipe <version.txt
msbuild /p:VersionStamp=%version%
Now, the work is in getting each project to use it. That would depend on the project type and where you want VersionStamp to appear.
For a csproj, you might want to use it as the AssemblyVersion. The simplest way is to move the attribute to a cs file by itself and rewrite it every time. I would leave a comment in AssemblyInfo.cs as a clue to where it now comes from. You can include the cs file in your project either dynamically or permanently. I prefer dynamically since it is effectively an intermediate file for the build. So, in your .csproj add the following in a text editor (e.g. Visual Studio. Unload and Edit project):
<Target Name="BeforeBuild">
<PropertyGroup>
<AssemblyVersionPath>$(IntermediateOutputDir)AssemblyVersion.cs</AssemblyVersionPath>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(AssemblyVersionPath)" />
</ItemGroup>
<WriteLinesToFile
File='$(AssemblyVersionPath)'
Overwrite="true"
Condition="'$(ProductVersion)' != ''"
Lines='using System.Reflection%3b;
[assembly: AssemblyVersion("$(VersionStamp)")]' />
</Target>
This is sufficient but a more thorough solution would include adding the file to a list so it is cleaned with other files and only writing the file if the version changed to prevent unnecessary rebuilds, etc.
Use a similar technique for other project types.
I'm using vs 2010 and have a web application project.
I currently use the right-click "publish" option on my project using the "web deploy" option to publish the entire application to a server that is running msdeploy.axd.
I have to publish this project many times (100+), each time just changing the web.config.
I'm looking into what technology I should use to automate this process. I would prefer to use standard MS technology.
Should I be looking at MSBuild? The VS Command Prompt? (are these the same thing?)
What technology should I learn to best automate this scenario. I'm looking for the most standard way to do this...
In my head the script I'm going to write will:
change the web.config file
do the equivalent of right-click my project, click publish, use the web-deploy option, and deploy it to that server using the windows domain\username + password details i've saved in Visual Studio
Any help is appreciated.
I've done this exact thing just with MSBuild. The steps involved in my case were:
TFS performed a build using a custom MSBuild project, not a solution file. Any CI build system would work, nothing special about TFS in this instance.
In the custom build project, in addition to building all the projects, I copied a bunch of web.config "template" files to a special folder under the $(OutDir) folder. These would end up in the build drop from TFS. In my case, the built in configuration file transformations were nowhere close to being sophisticated enough, but if that can work for you it is simpler by far.
The web.config files actually contained references to other config files, one for each configurable feature. This used a custom configuration provider. This technique would also work if you just had a single web.config file though.
I enabled automated deployment (Publish) from the command line, as a new MSBuild target in the same custom MSBuild project that was driving the build.
It was then easy to automate the Publish step in the main build to a VM or QA machine, as well as make it possible to manually deploy to other servers (eventually the staging servers) from the command line.
The web.config templates had stuff like this:
In the connection string: "Data Source=${SQLINSTANCE};Initial Catalog=${SQLDATABASENAME}..."
It is significant that the delimiters for the replacable tokens is ${ } and not $( ), since MSBuild won't mess with the curly brackets.
In the Publish step, use an MSBuild property function to replace bits in the config files, the following is taken from MSDN's description of MSBuild inline tasks:
<UsingTask
TaskName="ReplaceToken"
TaskFactory="CodeTaskFactory"
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
<ParameterGroup>
<File ParameterType="System.String" Required="true" />
<Token ParameterType="System.String" Required="true" />
<Replacement ParameterType="System.String" Required="true" />
</ParameterGroup>
<Task>
<Code Type="Fragment" Language="cs">
<![CDATA[
string content = File.ReadAllText(File);
content = content.Replace(Token, Replacement);
File.WriteAllText(File, content);
]]>
</Code>
</Task>
</UsingTask>
Then in your project, you can use this task with,
<ReplaceToken
File="PathTo\Web.config"
Token="${SQLINSTANCE}"
Replacement=".\SQLEXPRESS"
/>
This is really just a rough guide, all of the parameters to ReplaceToken were also configured in MSBuild item metadata, allowing options for which database, server, security, etc., each of which could be specified individually.
So, for each build/deployment, it would perform the build, copy the config templates, do string replacements on them, then automate the Package/Publish, which is the final bit to go over.
Your best bet is to start with this blog: http://vishaljoshi.blogspot.com/2009/02/web-packaging-creating-web-packages.html which explains a bit, and this answer, which contains likes to a bunch of other related StackOverflow posts MsBuild and MsDeploy with multiple environments, then search online with http://www.bing.com/search?q=msbuild+msdeploy+command+line&go=&form=QBLH&qs=n&sk= to dig deeper. I really hate to just dump you off to a search engine for this part of it, but I've found that there are so many different scenarios it is tough to single one out. About half of the top ten responses have insight on some valuable angle. Reply with more info to help narrow down my response at this point. For my implementation, I called MSDeploy.exe directly from MSBuild using an Exec task. Some things to consider:
How to deal with security on the various publish sites. I had to set up a mess of build service accounts, and always required passing the password on the msbuild command line. If you can get by with Windows auth like you suggest it will be a bit easier.
For services, I had to use PSExec to run installutil on the remote server
There were some additional configuration items that I automated using PSExec calling appcmd on the remote server.
It is easy to open up a remote share on the server and use a "net use" command to map and unmap it during the build, you may have some other preference.
Performance is tough. For larger sites it can run quite long doing it file by file. RoboCopy isn't any faster. I found using MSDeploy to package remotely (point to local drop as source, and remote share for the package source) was very fast to Rackspace. You may need to first package to a zip file using one MSDeploy call, then push the package remotely using a second.
Hope this gets you started. Please comment or supply more detail in your question if there is something I really missed or glossed over.
Response to comment:
The "Publish" target is something along these lines,
<Target Name="Publish">
<!-- token replacement in config files, as above -->
<!-- ...lots of custom setup, selection of various properties used below -->
<PropertyGroup>
<_MsDeployExe>$(PROGRAMFILES)\IIS\Microsoft Web Deploy\msdeploy</_MsDeployExe>
<_MsDeploySourceArg>-source:contentpath="$(_BuildDropFolder)"</_MsDeploySourceArg>
<_MsDeployDestArg>-dest:contentpath=\\$(_RemoteComputerName)\DropFolder</_MsDeployDestArg>
</PropertyGroup>
<Message
Text=""$(_MsDeployExe)" -verb:sync $(_MsDeploySourceArg) $(_MsDeployDestArg)"
/>
<Exec
Condition="'$(DryRun)' != 'true'"
Command=""$(_MsDeployExe)" -verb:sync $(_MsDeploySourceArg) $(_MsDeployDestArg)"
ContinueOnError="false"
WorkingDirectory="$(MSBuildThisFileDirectory)"
/>
</Target>
I'm a huge fan of the addition of web.config transformations in Visual Studio 2010. See also Scott Hanselman's recent talk at MIX2011.
What sucks is that this functionality (appears at least) to only be available to web projects.
In our solution we have several Windows Services that connect to a different database dependant on the environment they are deployed under.
Has anyone come up with a nice, tidy way of achieving similar 'app.config transformation' functionality?
Note: We are using TFS 2010 to build our solutions in a Continuous Integration manner.
I realize you already have an answer, but I stumbled across SlowCheetah this morning which is the best implementation I've seen to date. There is also a blog post on getting this running from a CI server.
You can use the XML transformation functionality with any XML file - we do this all the time. It's available via an MSBuild task.
Try adding the following to your build script:
<UsingTask TaskName="TransformXml"
AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.Tasks.dll"/>
<TransformXml Source="Path\To\Your\Xml.config"
Transform="Path\To\Your\Xml.$(Configuration).config"
Destination="Path\To\Your\Output.config" />
I wrote nice extension to automate app.config transformation like the one built in Web Application Project
Configuration Transform
Using Luke Bennett's answer to set me off on the right track. I found this to be the answer for us.
FTA (see link for code snippets):
Add a new property ProjectConfigFileName that points to your App.Config file
Add a version of App.Config for each configuration, i.e., App.Debug.config To have them nested under App.Config, edit your csproj file,
Import Microsoft.Web.Publishing.targets into your csproj file right after the Microsoft.CSharp.targets import.
Call the TransformXml task in your AfterBuild target. Note, the BeforeBuild and AfterBuild targets are commented out by default.
If you have multiple client assemblies and don't want to duplicate the same configuration data, I created Profigurator. It'll take a JSON file as input and apply the settings to an app.config or web.config.
It's a little rough as I write this, but I am currently using it on a production system for deploys and it works great.
I am using T4toolbox to generate a bunch of files, let's say my t4 file name is x.t4, but default it generate a x.txt, which has nothing inside, can I tell t4 engine not to do this?
Found a trick/hack!
<## output extension="/" #>
or
<## output extension="\\" #>
Visual Studio neither outputs the default file nor complains about its inability too much.
If you want to avoid warnings, you can also modify output path through the extension:
<## output extension="xml/../_" #>
The file will still be created and attached to the T4 file within the project hierarchy in Visual Studio, but you can put it into any directory.
P.S. I've tried it with T4MultiFile NuGet package, but it should work with T4Toolbox too, I think.
Right click on x.t4 in Solution Explorer and click Properties. It will say "TextTemplatingFileGenerator" beside Custom Tool. Delete this.
x.t4 will now be part of your project but it will not generate anything. This is useful when the .t4/.tt file is only being used as an include file in other templates.
No. This file is created by Visual Studio and not by T4. The best you can do is generate something useful in it such as actual code or, perhaps, a log of the code generation run.