I'm attempting to build up an MSBuild file to deploy a set of several websites to multiple environments. At the moment, I've built the project and have it dumped into a directory.
I'm using the variable $(AdminConsoleConfigFilePath) to refer to the path of the web.config file in this directory. My substitutions file is denoted by the variable $(AdminConsoleConfigReplacementFile). I've set up the following task in my msbuild file for the project:
<Target Name="AdminConsoleConfig" DependsOnTargets="BuildAdminConsole">
<Message Text="Beginning configuration for AdminConsole at $(AdminConsoleConfigFilePath) using $(AdminConsoleConfigReplacementFile)" Importance="high"/>
<XmlMassUpdate ContentFile="$(AdminConsoleConfigFilePath)" SubstitutionsFile="$(AdminConsoleConfigurationReplacementPath)"
ContentRoot="$(ConfigurationContentReplacementXPathRoot)" SubstitutionsRoot="$(SubstitutionsXPathRoot)"/>
<Message Text="Finished configuration for AdminConsole using $(AdminConsoleConfigReplacementFile)" Importance="high"/>
`
The values of the other variables here are declared as follows:
<ConfigurationContentReplacementXPathRoot>/configuration</ConfigurationContentReplacementXPathRoot>
<SubstitutionsXPathRoot>/configuration/substitutions/$(Environment)</SubstitutionsXPathRoot>
Environment is the name of where the build is targeted. In this case, it's "qa".
My web.config has the typical hibernate-config section as follows:
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory>
<property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
<property name="connection.isolation">ReadCommitted</property>
<property name="dialect">NHibernate.Dialect.MsSql2005Dialect</property>
<property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
<property name="connection.connection_string">[redacted]</property>
<property name="show_sql">false</property>
<property name="generate_statistics">true</property>
<property name="current_session_context_class">web</property>
<property name="adonet.batch_size">250</property>
<property name="proxyfactory.factory_class">NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle</property>
<mapping assembly="[redacted]"/>
</session-factory>
</hibernate-configuration>
My replacements file looks like the following:
<configuration xmlns:xmu="urn:msbuildcommunitytasks-xmlmassupdate">
<substitutions>
<dev></dev>
<qa>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory>
<property xmu:key="name" xmu:Transform="Replace" xmu:Locator="Match(name)" name="connection.connection_string">[redacted-qa]</property>
</session-factory>
</hibernate-configuration>
</qa>
Now, the connection.connection_string property is not being replaced (in the above code, I also could never get the closing tags for configuration and substitution to show in the editor on this site - might be a firefox issue). Any ideas? I've tried the following:
removing the namespace declaration for nhibernate-configuration from the web.config in question and the replacement file (both together and individually).
Adding a namespace prefix to the nhibernate declaration and prefixing the relevant nodes and attributes with it. Again, tried on both the web.config and the replacement file, both individually and in combination.
Attempted to use the latest nightly build from the MSBuild contrib project. Not only did this not work, it broke other things as well.
Tried using an XPath-based xmu:Locator. Also tried both with and without the xmu:key value (and even tried replacing it with xmu:Key).
I'm absolutely stumped. Does the XMLMassUpdate thing work at all? We'd like to keep the qa and production versions of the web.config (or at least the various sensitive bits) out of the main body of the code in case we get a junior dev or contractor in, so the current method of the web.qa.config and web.prod.config isn't really viable at this point. Any thoughts on how to implement this (besides the obvious one of just keeping copies of the full web.config for each environment and copying over after the build)?
Thanks,
Will
I haven't tried using the xmlmassupdate approach to configuration substitution myself. But there is an alternate approach which is to specify each configuration update individually in your target using the XmlFile/UpdateElement method, e.g.
<MSBuild.ExtensionPack.Xml.XmlFile
TaskAction="UpdateElement"
File="$(AdminConsoleConfigFilePath)"
XPath="/hibernate-configuration/session-factory/property[#name='connection.connection_string']"
InnerText="Initial Catalog=MyDatabase;Data Source=$(DatabaseServerInstance)"
/>
You then put your configurations in the header of your targets file, e.g.
<Choose>
<When Condition="$(Environment)=='DEV'">
<PropertyGroup>
<DatabaseServerInstance>DEVSERVER</DatabaseServerInstance>
</PropertyGroup>
</When>
<When Condition="$(Environment)=='QA'">
<PropertyGroup>
<DatabaseServerInstance>QASERVER</DatabaseServerInstance>
</PropertyGroup>
</When>
<When Condition="$(Environment)=='PROD'">
<PropertyGroup>
<DatabaseServerInstance>PRODSERVER</DatabaseServerInstance>
</PropertyGroup>
</When>
</Choose>
You can also put the "Choose" block into a separate file and include it from your targets file. I personally prefer this approach because it has allowed us to centralise the list of environment-specific properties, which can then be used by multiple targets files (e.g. if you have multiple applications to deploy that use the same database server in each environment).
Related
MSBuild really seems to like me.
Recently I am trying out the different possibilities to build and deploy from command line.
However I am experiencing some seemingly strange behaviour when I pass a publish profile to MSBuild.
Here is an example of what I just did:
I deploy to a local IIS for the moment with a command such as this:
msbuild D:\PathToFile\DeployDBVariation01\DeployDBVariation01\DeployDBVariation01.csproj
/p:Configuration=Release;
Platform=AnyCpu;
DeployOnBuild=true;
DeployTarget=MSDeployPublish;
MSDeployServiceURL="localhost";
DeployIisAppPath="DeployApp/DeployThis";
MSDeployPublishMethod=InProc;
Username=thisIsNotActuallyMyUsername;
password=guesswhat;
AllowUntrustedCertificate=true
And this works! After that it is successfully deployed and I can call it in a browser.
However, since Visual Studio gives us the comfort of using publishing profiles I wanted to try that in conjunction with MSBuild over command line and tried the following command:
msbuild D:\PathToFile\DeployDBVariation01\DeployDBVariation01\DeployDBVariation01.csproj
/p:DeployOnBuild=true;
AllowUntrustedCertificate=true;
PublishProfile=ReleaseLocal
ReleaseLocal is a profile I created in Visual Studio and it looks like this:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<WebPublishMethod>MSDeploy</WebPublishMethod>
<LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
<LastUsedPlatform>Any CPU</LastUsedPlatform>
<SiteUrlToLaunchAfterPublish />
<ExcludeApp_Data>False</ExcludeApp_Data>
<MSDeployServiceURL>localhost</MSDeployServiceURL>
<DeployIisAppPath>DeployApp/DeployThis</DeployIisAppPath>
<RemoteSitePhysicalPath />
<SkipExtraFilesOnServer>True</SkipExtraFilesOnServer>
<MSDeployPublishMethod>InProc</MSDeployPublishMethod>
<EnableMSDeployBackup>False</EnableMSDeployBackup>
<UserName />
<_SavePWD>False</_SavePWD>
<PublishDatabaseSettings>
<Objects xmlns="">
<ObjectGroup Name="DefaultConnection" Order="1" Enabled="False">
<Destination Path="Data Source=.\SQLEXPRESS;Initial Catalog=HorstDataProductive;User ID=sa;Password=GuessWhat" />
<Object Type="DbDacFx">
<PreSource Path="Data Source=.\SQLEXPRESS;Initial Catalog=HorstData;User ID=sa;Password=GuessWhat" includeData="False" />
<Source Path="$(IntermediateOutputPath)AutoScripts\DefaultConnection_IncrementalSchemaOnly.dacpac" dacpacAction="Deploy" />
</Object>
<UpdateFrom Type="Web.Config">
<Source MatchValue="Data Source=.\SQLEXPRESS;Initial Catalog=HorstData;User ID=sa;Password=GuessWhat" MatchAttributes="$(UpdateFromConnectionStringAttributes)" />
</UpdateFrom>
</ObjectGroup>
</Objects>
</PublishDatabaseSettings>
</PropertyGroup>
<ItemGroup>
<MSDeployParameterValue Include="$(DeployParameterPrefix)DefaultConnection-Web.config Connection String">
<ParameterValue>Data Source=.\SQLEXPRESS;Initial Catalog=HorstDataProductive;User ID=sa;Password=GuessWhat</ParameterValue>
</MSDeployParameterValue>
</ItemGroup>
</Project>
As you can see I have some additional connection string replacement there that I want to test.
So I execute that last MSBuild-command that I have shown you and it executes without any errors. Instead it says that the web deployment task was successful and that a package has been created in a certain path. Now that is actually the right package. When I import that manually in my IIS it is the result I expect, also the connection string replacement has been done.
But I do not understand why it actually is just creating the package but not deploying it in one run, like in my first command.
Can someone explain?
(Or even better, what do I have to do to make it also deploy that package immediately)
You need to specify the VS version.
http://www.asp.net/mvc/tutorials/deployment/visual-studio-web-deployment/command-line-deployment
msbuild /P:DeployOnBuild=True /P:VisualStudioVersion=11.0 /P:PublishProfile=Dev.pubxml
Don't forget to allow untrusted certs if you're using a 'fake' internal certificate
Looks like you're missing the deploy target.. it's got all the necessary info, but doesn't know what you want it to do. Try this;
msbuild D:\PathToFile\DeployDBVariation01\DeployDBVariation01\DeployDBVariation01.csproj
/p:DeployOnBuild=true;DeployTarget=MSDeployPublish;AllowUntrustedCertificate=true;PublishProfile=ReleaseLocal
I've seen lots of info on the web about this, but nothing clear & specific which seems to address the problem of simply publishing a web service or web site to a specific folder that I specify at build time.
I'm using Nant and Nant Contrib:
<target name="build" description="builds the service">
<msbuild project="${buildoutput}\${service.source}\wsMyService.sln" >
<property name="Configuration" value="Release" />
<property name="PublishDir" value="${buildoutput}\${service.target}\" />
<property name="Targets" value="Publish" />
</msbuild>
</target>
Can anyone show me how this is supposed to be done. I can change the output folder in the property pages of the project, but I want this to be configurable from Nant so I can specify the path at build time.
When you call msbuild from the command line, you can pass in strings to assign to msbuild properties. I don't know anything about NAnt, so I assume it has to resort to a calling msbuild.exe. So you can override msbuild properties like this:
MsBuild /property:buildoutput=C:\arbitrary\folder\bin\
These properties specified from the command line override anything you specify in your build files.
This is what im currently using to build webservices using msbuild:
<Target Name="BuildWebService">
<ConvertToAbsolutePath Paths="$(Root)">
<Output TaskParameter="AbsolutePaths" PropertyName="Root" />
</ConvertToAbsolutePath>
<ItemGroup>
<WebServices Include="$(Root)\services\Solutions\**\*.host.csproj"/>
</ItemGroup>
<MSBuild Projects="%(WebServices.FullPath)"
Targets="Build"
Properties="WebProjectOutputDir=$(Root)\services\build\WebService\%(RecursiveDir);OutDir=$(Root)\services\build\WebService\%(RecursiveDir)\bin\" />
</Target>
Hope you can translate to nant easy enough.
Using Nant 0.92 (and previously 0.85 with same consequence)
I'm trying to call the delete task in NAnt to remove all files except the .dll file after calling msbuild (see the script below .. I'm referring to the delete in the "build" target). The delete task does not seem to execute.
The initial delete task works fine, and behaves as expected, removing all files from the specified build directory. The second delete task, after the compile however doesn't work.
I've tried just deleting everything (not using the exclude tag), tried explicitly setting failonerror and verbose to true. None of these make a difference. I've also tried using sleep to stop the process prior to the delete task, in case something in msbuild was not releasing the files in time for a delete. I've tried putting the delete into a separate target, still no luck.
The command obviously works prior to calling msbuild, and it works after msbuild if trying to delete from a directory other than the msbuild output target (i.e. copy the output files, then delete relevant files).
I'm sure that this is too fundamental a problem to be a bug, but I thought I'd ask anyway. Of course I'll use the workaround in the mean time of just copying the files to a different location delete what I don't need then move appropriately, but I'm curious about this.
I suspect, that unless this behaviour is by design (although I can find nothing in the NAnt documentation to suggest it is), then perhaps the msbuild process is locking the output files until NAnt process completes? This is my best guess. Further insights would be appreciated.
EDIT: also, if I explicitly set the \OutputPath switch of msbuild, then I don't have the same problem. It only appears that when the default OutputPath is used is the problem created.
NAnt build file:
<?xml version="1.0" encoding="utf-8" ?>
<project name="Reports.TestBench.PreBuild" default="postbuild.cleanup" basedir="." xmlns="http://nant.sourceforge.net/release/0.86-beta1/nant.xsd">
<property name="nant.settings.currentframework" value="net-4.0" />
<property name="project.StandardReports" value="${project::get-base-directory()}\Reports.StandardReports\Palladium.Reports.StandardReports.csproj" />
<property name="output.Dir" value="${project::get-base-directory()}\bin\debug\"/>
<property name="build.Type" value="debug"/>
<!--Deletes the pre-existing build files-->
<target name="clean">
<delete>
<fileset basedir="${output.Dir}">
<include name="*.*" />
</fileset>
</delete>
</target>
<!--Builds the projects to the specified build directory-->
<target name="build" depends="clean" description="Build the Palladium Reports Standard Reports application">
<msbuild project="${project.StandardReports}">
<arg value="/p:Configuration=${build.Type}" />
<!--arg value="/p:OutputPath=${path::get-full-path(buildDir.Client)}\Reports" /-->
<arg value="/t:Rebuild" />
</msbuild>
<delete failonerror="true" verbose="true">
<fileset basedir="${output.Dir}">
<include name="*.*" />
<exclude name="Palladium.Reports.StandardReports.dll" />
</fileset>
</delete>
</target>
</project>
Summary of NAnt output showing build success with no further messages:
[msbuild] Build succeeded.
[msbuild] 0 Warning(s)
[msbuild] 0 Error(s)
[msbuild]
[msbuild] Time Elapsed 00:00:03.19
BUILD SUCCEEDED
Total time: 3.5 seconds.
Try poking into NAnt source code - most likely the msbuild task creates an MSBuild engine and does not force it to close the files afterwards. Although looking at the MSDN docs, I don't really see a way to do it - MSBuild constructs lack a Dispose() function. There's a finalizer on classes such as ProjectInstance, so one could force it release the file handles by running in an application domain and closing the whole domain afterwards - that's quite a bit of work.
I've read in this post that all the ProxyFactory dependency was removed by using an interface in this post.
So you need to specify which implementation to use in the hibernate.cfg.xml file.
I've this configuration:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory>
<property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
<property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
<property name="connection.connection_string">Data Source=.\SQLEXPRESS;Initial Catalog=MYDB;Integrated Security=true</property>
<property name="dialect">NHibernate.Dialect.MsSql2005Dialect</property>
<property name='proxyfactory.factory_class'>NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle</property>
</session-factory>
</hibernate-configuration>
I've added a refernce to the NHibernate.ByteCode.Castle.dll. When I run the test using MBunit, I get the error that my deployment folder should contain either NHibernate.ByteCode.Castle.dll or NHibernate.ByteCode.LinFu.dll. I guess this is right configuration and it should work. But it is not working. I've spent much time behind this.
P.S: When I donwloaded NHibernate, the NHibernate.ByteCode.Castle project was not built. I added the project to the solution and built it. Then I referenced the assembly.
I had this same situation not too long ago.
When you say you added a reference, was it to the actual project or test project? It should be within both. Also, ensure "Copy Local" is set to true in the properties(F4) of the reference.
Another approach I took to check that if dll is within the directory that the application is running from was by calling the following prior to any of the Configuration.
Console.WriteLine(Directory.GetCurrentDirectory());
In my situation, I learned that when using ReSharper to execute tests, it was running in a location different than I had expected and was not containing the dll. Doing a Clean Solution and rebuilding seemed to correct the issue.
Hope this gives you a couple of things to check.
I'm new to NAnt and have been able to create a <target> which
1) Deletes any code from the current folder
2) Exports fresh code from SVN
3) Builds the code in the default directory which is the PrecompiledWeb folder (its a web app)
Here it is:
<target name="export" description="export code from svn">
<delete dir="${Delete.Dir}"></delete>
<exec program="svn" commandline="export ${MySVN.Repos} ${MySVN.Dest}" />
<msbuild project="${Solution.Filename}">
<property name="Configuration" value="Release"/>
</msbuild>
</target>
I want to specify a custom output directory (other than "PrecompiledWeb"). Is this possible and could you please show me the necessary tag/property?
Thank you!
EDIT
Thanks to Scott and Si, I'm getting closer to a solution, but I still don't have anything that works. There comments led me to this article on MSBuild's Output Path Property. Using their code:
<?xml version="1.0"?>
<project name="test" default="build" basedir="." xmlns="http://nant.sf.net/schemas/nant-0.84.win32.net-1.0.xsd">
<target name="build">
<exec program="${framework::get-framework-directory('net-3.5')}/msbuild.exe">
<arg value="${Full.Path}\Code\MySolution.sln" />
<arg value="/p:OutputPath=${Full.Path}\Output\" />
</exec>
</target>
</project>
This will sucessfully run; however, in my solution which contains a web site and a class library, it publishes the class library dll to the specified output path, but it still publishes the actual web site to the default PrecompiledWeb folder.
Does anyone have any suggestions for how to change the PrecompiledWeb folder path from NAnt?
Thanks again to everyone who's helped!
Edit 2 Final Solution
Here is what I finally was able to get working (updated for .net 4):
<exec program="${framework::get-framework-directory('net-4.0')}/msbuild.exe">
<arg value="${Full.Path}\Code\MySolution.sln" />
<arg value="/t:Rebuild" />
<arg value="/t:ResolveReferences" />
<arg value="/t:_CopyWebApplication" />
<arg value="/p:OutDir=${Build.Output}bin\" />
<arg value="/p:WebProjectOutputDir=${Build.Output}" />
<arg value="/p:Configuration=Release" />
</exec>
One can specify and override some of properties for msbuild. In order to specify the output directory, override the OutputDir property.
<target name="export" description="export code from svn">
<delete dir="${Delete.Dir}" />
<exec program="svn" commandline="export ${MySVN.Repos} ${MySVN.Dest}" />
<msbuild project="${Solution.Filename}">
<property name="Configuration" value="Release"/>
<property name="OutputDir" value="${Output.Dir}"/>
</msbuild>
</target>
Just had a quick peek at a project, does OutputPath instead of OutputDir help?
Another option might be a web deployment project, which I like because it calls aspnet_compiler as well as the C# compiler, so it picks up issues which you may otherwise miss until deployment.
A build script for one of our projects uses this command to publish a web application:
msbuild.exe /t:_CopyWebApplication /p:Configuration=Release /p:OutDir=.\..\published\ /p:WebProjectOutputDir=.\..\published
(The current directory is set to the web app's project directory at this point, which is why no .csproj file is specified. The entire solution has already been rebuilt earlier in the script.)
By the sound of it, WebProjectOutputDir might be the property you need.
/t:_CopyWebApplication may also be important. I've not used NAnt so I don't know if you can pass this parameter with the msbuild task. You may need to use an exec task, like in this example: http://www.netomatix.com/development/wapwithnant.aspx. This example appears to rebuild and copy all in one go.
When using the task, the correct property name is OutDir, not OutputDir:
<msbuild project="${Solution.Filename}">
<property name="Configuration" value="Release"/>
<property name="OutDir" value="${Output.Dir}"/>
</msbuild>
A source of confusion is that you're blending two distinct build systems. Your NAnt target is delegating all the work of figuring out how to publish your web application to the solution file, hence by extension to the csproj files it references.
csproj files are MsBuild files, so you should probably look there for how to direct your project output. This post by dave^2 might be helpful on that issue.
You can publish your web application wherever you want using NAnt, provided it's doing the publishing. You can do the same with MsBuild. The cause of your quandary is that NAnt is not doing the publishing in this case, and you're letting the csproj file determine the location of your web directory. So either bypass the csproj file and have NAnt (or MsBuild) publish the code; or modify the csproj file to publish the web application where you want; or make a second location for your web application and publish it there as well using your build tool.
AFAIK, those options are exhaustive.
Hmm, don't know how to do it with MSBuild in Nant, but using NAnt, I've done it previously like this:
<solution solutionfile="${build.dir}\solution.sln">
<webmap>
<map url="http://localhost/somdir/project.csproj"
path="c:\inetpub\wwwroot\somelocaldir" />
<webmap>
</solution>
But then, you're using the NAnt 'solution' task offcourse instead of using MSBuild directly.
edit:
I'm also having a look at some msbuild options;
If you set OutDir & OutputPath to the same value, what happens ?
Try something like this:
<property name="nant.settings.currentframework" value="net-3.5"/>
<msbuild project="${Solution.Filename}">
<property name="Configuration" value="Release"/>
<property name="OutDir" value="${Full.Path}\Output\\"/>
<property name="WebProjectOutputDir" value="${Full.Path}\Output\Web\\"/>
</msbuild>