Apply transforms to config files on publish - msbuild

We currently publish our web app vis MSDeploy, to create a .ZIP file (file deployment).
I have some .config files that I want to apply transforms to when we publish. So I've created an appSettings.test.config and an appSettings.live.config.
To facilitate this, I have added this to the csproj of our web app, which applies the transforms:
<UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v12.0\Web\Microsoft.Web.Publishing.Tasks.dll" />
<Target Name="ApplyConfigTransforms" BeforeTargets="TransformWebConfigCore">
<TransformXml Source="appSettings.config" Transform="appSettings.$(Configuration).config" Destination="appSettings.config" />
<TransformXml Source="connectionStrings.config" Transform="connectionStrings.$(Configuration).config" Destination="connectionStrings.config" />
</Target>
This hooks in to the TransformWebConfigCore target and does the work. BUT, it applies the transforms to the appSettings file in my web folder (as expected from the destination). What I want to do is apply the transform to the file in the output pacakge.
How can I do this? I don't know how to get a handle on the temporary folder that is created when you publish..

Don't you just hate it when you find the answer yourself about 10 mins after posting.
I hacked the destination, which fixed the issue. Would be nice if I had this in a property, but I couldn't find one to use, so I did:
<Target Name="ApplyConfigTransforms" AfterTargets="CopyAllFilesToSingleFolderForMsdeploy">
<TransformXml Source="appSettings.config" Transform="appSettings.$(Configuration).config" Destination="obj\Test\Package\PackageTmp\appSettings.config" />
<TransformXml Source="connectionStrings.config" Transform="connectionStrings.$(Configuration).config" Destination="obj\Test\Package\PackageTmp\connectionStrings.config" />
</Target>
Note, I also changed the target, to make sure it happens just after all my files have been copied

Related

Config transformation in WIX setup

I'm creating a MSI setup with WIX for my Web Application. This works correct. The only thing that I don't get to work is to enabling the config transformation of the standard web application publish method.
I understand that you can add the using tag for existing target files. I try'ed to add the TransformXml to the AfterBuild Target in the project file of the WIX installer but that doesn't work.
<TransformXml Source="Web.Config" Transform="Web.$(Configuration).config" Destination="Web.Config" />
Can someone help me?
I created a test project for this called WebApplicationWix
I didn't see any mention of TransformXml in your example project.
You need code similar to this:
<UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.Tasks.dll" />
<Target Name="AfterCompile">
<CallTarget Targets="TransformWebConfiguration" Condition="Exists('web.$(Configuration).config')"/>
</Target>
<Target Name="TransformWebConfiguration">
<!-- Generate transformed web configuration -->
<TransformXml Source="web.config" Destination="web.transformed.config" Transform="web.$(Configuration).config" />
</Target>
A few things to note:
Check the path to Microsoft.Web.Publishing.Tasks.dll in the UsingTask element (change for your version of Visual Studio)
In your example, the source and destination were the same; you should make sure the destination is a different file so that you don't have file lock issues or overwrite the web.config you're trying to transform with the transformed one.
In Visual Studio 2010, there were file locking issues with TransformXml, so be careful of that if you're using 2010.

Adding files to Azure cspkg in afterbuild msbuild event?

I have an MVC application which I have got working on Azure apart from getting the published .cspkg file to include css/jscript that is created in an afterbuild process (this works if I publish to a normal server which isn't using Azure).
In the afterbuild process I minify and merge files then add them to a deploy zip:
<PackageLocation>..\Deploy\Website.zip</PackageLocation>
<PropertyGroup>
<CopyAllFilesToSingleFolderForPackageDependsOn>
CustomCollectFiles;
$(CopyAllFilesToSingleFolderForPackageDependsOn);
</CopyAllFilesToSingleFolderForPackageDependsOn>
</PropertyGroup>
What MSBuild code do I need to change in order to do the same task but adding to the cspkg instead?
Here is how I just did it. In this example I have a .csproj file that is part of an Azure solution and the dll produced by my C# project needs a particular Xml file to live right next to it in the deployment. Here are some msbuild fragments from my .csproj file that show the technique. You can place all of this code below the import of Microsoft.CSharp.targets in your .csproj file.
<!-- Identify the Xml input file that must be deployed next to our dll. -->
<ItemGroup>
<SpecialXmlFileItem Include="c:\temp\MySpecialFile.xml" />
</ItemGroup>
<PropertyGroup>
<!-- In my case I needed the as-deployed Xml filename to be fixed and yet I wanted it to be possible
to provide any filename at all to be provided as the source. Here we are defining the fixed,
as-deployed filename. -->
<AsDeployedXmlFilename>MyServiceStorageConfig.xml</AsDeployedXmlFilename>
<!-- Wire our own AddFilesToProjectDeployment target into the GetCopyToOutputDirectoryItems
target. That target is evaluated not only as part of normal .csproj evaluation, but also as part
of .ccproj evaluation. It is how the .ccproj manages to interrogate your dll producing projects
about all of the project files that need to be packaged. -->
<GetCopyToOutputDirectoryItemsDependsOn>
AddFilesToProjectDeployment;
$(GetCopyToOutputDirectoryItemsDependsOn)
</GetCopyToOutputDirectoryItemsDependsOn>
</PropertyGroup>
<Target Name="AddFilesToProjectDeployment">
<Error Condition="!Exists('#(SpecialXmlFileItem)')"
Text="The all important and very special XML file is not found: %(SpecialXmlFileItem.ItemSpec)" />
<ItemGroup>
<ContentWithTargetPath Include="#(SpecialXmlFileItem->'%(FullPath)')">
<!-- In my case I wanted to deploy my xml file right next to my .dll, so I included no relative
path information in the below value of TargetPath, just the simple filename. But, I think if you
included relative path information in the below value that it would be preserved in the deployment. -->
<TargetPath>$(AsDeployedXmlFilename)</TargetPath>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</ContentWithTargetPath>
</ItemGroup>
</Target>
-Bern McCarty
I think this is just a question of timing... make sure the files get combined, minified, and placed into build before the publishing (packaging) step happens.
Sorry I don't have more details; I've never tried to do this sort of thing.

How do I exclude the contents of a directory but not the directory itself in MSBuild

I'm using a Web Deployment Project 2008 to build my web application. I'd like to exclude the contents of several folders from the build but keep the blank directory itself. However, if I do this
<ExcludeFromBuild Include="$(SourceWebPhysicalPath)\ImageCache\**\*.*" />
it will exclude the ImageCache directory itself. So how do I keep the directory? Thanks in advance?
I'm afraid you must do this in two lines :
<ExcludeFromBuild Include="$(SourceWebPhysicalPath)\ImageCache\**\*.*" />
<IncludeFromBuild Include="$(SourceWebPhysicalPath)\ImageCache\" />
But that could not work because The "Copy" task does not support copying directories.
Thus, what I suggest is that you exclude the files like you did and create an empty directory in the AfterMerge target :
<ItemGroup>
<ExcludeFromBuild Include="$(SourceWebPhysicalPath)\ImageCache\**\*.*" />
<ItemGroup>
<...>
<Target Name="AfterMerge">
<MakeDir Directories="$(SourceWebPhysicalPath)\ImageCache" />
</Target>

msbuild fileupdate setup project

I'm looking to use the fileUpdate task from msbuildtasks.tigris.org to modify image src's as part of our web setup project so that they will point to a static img sub domain (or later a CDN)
I can run a task within a given project with:
<Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets" />
<Target Name="AfterBuild">
<FileUpdate
Files="basic.css"
Regex="/images/([^\)]*)"
ReplacementText="http://img.domain.com/images/$1" />
</Target>
However, I dont want to overwrite the original css source file, but want to run this as part of our deployment project that produces an msi.
This is done using a web setup project (.vdproj) which also uses a custom actions project which is just a standard .csproj
My questions are:
How can I run this task in the setup project so that I replace content in the files that go into the .msi?
Is there a way to use wildcards for the files - ideally I want to say do this for ALL .css files?
In order to achieve this you need to use an item group to create the list for you
<Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets" />
<Target Name="AfterBuild">
<ItemGroup>
<CssFiles Include='$(SolutionRoot)\**\*.css' />
</ItemGroup>
<FileUpdate
Files="#(CssFiles)"
Regex="/images/([^\)]*)"
ReplacementText="http://img.domain.com/images/$1" />
</Target>

MSBuild doesn't respect PublishUrl property for my ClickOnce app

I'm trying to make a batch file to publish the few ClickOnce application we have in one click. I'm using msbuild for that, and as an example the below command line shows how I'm doing it:
msbuild
MyApp.sln
/t:Publish
/p:Configuration=Release
/p:PublishUrl="C:\Apps\"
/v:normal > Log.txt
(wrapped for easier reading)
when I run the above command it builds and publish the application in the release directory, i.e. bin\release! Any idea why msbuild doesn't respect PublishUrl property in my example above?
PS: I tried also different combinations including remove 'Configuration', use 'Rebuild' and 'PublishOnly' as targets, and remove the the quotation marks but without any success.
You are setting the wrong property. Try PublishDir instead.
You can pass it into MSBuild as you are or you can set it in the project file (or maybe the sln file too, not sure I always use the project file.) like this
<PropertyGroup>
<PublishDir>C:\Dev\Release\$(BuildEnvironment)\</PublishDir>
</PropertyGroup>
I've just done a few blog posts on MsBuild and ClickOnce stuff, check it out you 'should' find them useful...
Some features are done by Visual-Studio and not by the MSBuild-script. So the click-once-deployment behaves differently when it's executed from the command-line.
The ApplicationRevision isn't increased with every build. This works only when is exectued from Visual Studio
In in somecases, the PublishUrl isn't used. Quote from MSDN:
For example, you could set the PublishURL to an FTP path and set the InstallURL to a Web URL. In this case, the PublishURL is only used in the IDE to transfer the files, but not used in the command-line builds. Finally, you can use UpdateUrl if you want to publish a ClickOnce application that updates itself from a separate location from which it is installed.
I've created a special MSBuild-file which does this things. It runs the publish-target and copies then the files to the right location.
An example of the build-file, as requested by alhambraeidos. It basically runs the regular VisualStudio-build and then copies the click-once data to the real release folder. Note that removed some project-specific stuff, so it's maybe broken. Furthermore it doesn't increase the build-number. Thats done by our Continues-Build-Server:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Publish" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<!-- the folder of the project to build -->
<ProjLocation>..\YourProjectFolder</ProjLocation>
<ProjLocationReleaseDir>$(ProjLocation)\bin\Release</ProjLocationReleaseDir>
<ProjPublishLocation>$(ProjLocationReleaseDir)\app.publish</ProjPublishLocation>
<!-- This is the web-folder, which provides the artefacts for click-once. After this
build the project is actually deployed on the server -->
<DeploymentFolder>D:\server\releases\</DeploymentFolder>
</PropertyGroup>
<Target Name="Publish" DependsOnTargets="Clean">
<Message Text="Publish-Build started for build no $(ApplicationRevision)" />
<MSBuild Projects="$(ProjLocation)/YourProject.csproj" Properties="Configuration=Release" Targets="Publish"/>
<ItemGroup>
<SchoolPlannerSetupFiles Include="$(ProjPublishLocation)\*.*"/>
<SchoolPlannerUpdateFiles Include="$(ProjPublishLocation)\Application Files\**\*.*"/>
</ItemGroup>
<Copy
SourceFiles="#(SchoolPlannerSetupFiles)"
DestinationFolder="$(DeploymentFolder)\"
/>
<Copy
SourceFiles="#(SchoolPlannerUpdateFiles)"
DestinationFolder="$(DeploymentFolder)\Application Files\%(RecursiveDir)"
/>
<CallTarget Targets="RestoreLog"/>
</Target>
<Target Name="Clean">
<Message Text="Clean project:" />
<MSBuild Projects="$(ProjLocation)/YourProject.csproj" Properties="Configuration=Release" Targets="Clean"/>
</Target>
</Project>
I'll put in my 2 cents, this syntax seems to work (right or wrong):
/p:publishUrl="C:\\_\\Projects\\Samples\\artifacts\\Web\\"
For me, the soultion was to escape the path.
Instead of:
/p:PublishUrl="C:\Apps\"
Put:
/p:PublishUrl="C:\\Apps\\"