Someone sent me an email the other day to discuss some issues with the web packing experience in Visual Studio. One of his concerns were the directory structure in the .zip file that VS generates. If you are not familiar with this I’ll give you a quick overview. In Visual Studio 2012 when you create a web deploy package you typically end up with the full directory structure of your source inside the package. For example I have a project named WebApplication1 located at c:\temp\package\, when I create a package it contains the following files.
archive.xml Content\C_C\Temp\package\WebApplication1\obj\Release\Package\PackageTmp Content\C_C\Temp\package\WebApplication1\obj\Release\Package\PackageTmp\bin Content\C_C\Temp\package\WebApplication1\obj\Release\Package\PackageTmp\bin\WebApplication1.dll Content\C_C\Temp\package\WebApplication1\obj\Release\Package\PackageTmp\index.html Content\C_C\Temp\package\WebApplication1\obj\Release\Package\PackageTmp\Web.config parameters.xml systemInfo.xml
These paths can get pretty long depending on where you keep your source. This has been one of my pet peeves as well.
I decided to take a closer look at this and I have a solution. To create a web deploy package in VS you will first create a publish profile for that. When you do this, a .pubxml file will be created for you under Properties\PublishProfiles. This is your publish profile file, its an MSBuild file. You can customize your publish process by editing this file. We will modify this file in order to update these paths in the package.
The best way to solve the issue here is to update the package process to include a replace rule. This replace rule will update the full path into a simple path. We already have this concept baked into our Web Publish Pipeline. The replace rule that we would like to create will replace the paths C:\Temp\package\WebApplication1\obj\Release\Package\PackageTmp with website. Here is how to do it.
Edit the .pubxml file for the profile and add the following before the closing </Project> tag.
<PropertyGroup> <PackagePath Condition=" '$(PackagePath)'=='' ">website</PackagePath> <EnableAddReplaceToUpdatePacakgePath Condition=" '$(EnableAddReplaceToUpdatePacakgePath)'=='' ">true</EnableAddReplaceToUpdatePacakgePath>
<PackageDependsOn> $(PackageDependsOn); AddReplaceRuleForAppPath; </PackageDependsOn> </PropertyGroup>
<Target Name="AddReplaceRuleForAppPath" Condition=" '$(EnableAddReplaceToUpdatePacakgePath)'=='true' "> <PropertyGroup> <_PkgPathFull>$([System.IO.Path]::GetFullPath($(WPPAllFilesInSingleFolder)))</_PkgPathFull> </PropertyGroup> <!-- escape the text into a regex --> <EscapeTextForRegularExpressions Text="$(_PkgPathFull)"> <Output TaskParameter="Result" PropertyName="_PkgPathRegex" /> </EscapeTextForRegularExpressions> <!-- add the replace rule to update the path --> <ItemGroup> <MsDeployReplaceRules Include="replaceFullPath"> <Match>$(_PkgPathRegex)</Match> <Replace>$(PackagePath)</Replace> </MsDeployReplaceRules> </ItemGroup> </Target>
Let me break this down a bit. You create the target AddReplaceRuleForAppPath, and inject that into the package process by adding it to PackageDependsOn property. Once this target is executed it will add a replace rule into the MSDeployReplaceRules item group. The replace rule added is equivalent to the command line switch shown below.
Now when I create a package the contents are.
archive.xml Content\website Content\website\bin Content\website\bin\WebApplication1.dll Content\website\index.html Content\website\Web.config parameters.xml systemInfo.xml
That's a lot better! If you try this out let me know if you have any issues.
Sayed Ibrahim Hashimi | @SayedIHashimi