Force external files to be copied to bin folder on publish - asp.net

I have a web application project that references a third party assembly, which in turn uses some native dlls that it needs to find at runtime.
Because of that, I wanted to add these dlls to the bin folder of the project, so that they can definitely be found.
However, if I add the files to the project in /bin/, select "copy to output", the files are compiled and published into /bin/bin. Adding them to the root folder works, but can hardly be the right solution.
Adding them to the build target "AfterBuild" has no effect when publishing (e.g. "build deployment package")
It also has to work when building the solution via TFS, where the MSBuild target _CopyWebApplicationLegacy is invoked.

The solution was a combination of the things I had tried already:
Include the "Bin" folder in the project
Add the needed files (I added them as a link due to our development structure)
Set the following properties: "Build Action = Content" and "Copy to Output = Do Not Copy"
The files are now copied to the bin folder when publishing, even when automating the builds on TFS.
The component that needed this was GdPicture, which uses a couple of native DLLs during runtime.

It's common to have a lib folder in either your solution or workspace to put your third party dlls within. You would then add a reference to this location from your project and ensure this location is under source control.
You could also look at using NuGet to package this for you, which uses a solution level packages folder automatically.

Based on this article http://sedodream.com/2010/05/01/WebDeploymentToolMSDeployBuildPackageIncludingExtraFilesOrExcludingSpecificFiles.aspx. It's possible to define the msbuild target into main (lastest in order of solution's build) project file that will capture all files of the bin folder. Like this
<Target Name="CustomCollectFiles">
<ItemGroup>
<_CustomFiles Include="$(OutDir)\**\*" />
<FilesForPackagingFromProject Include="%(_CustomFiles.Identity)">
<DestinationRelativePath>$(OutDir)\%(RecursiveDir)%(Filename)%(Extension)</DestinationRelativePath>
</FilesForPackagingFromProject>
</ItemGroup>
</Target>
<PropertyGroup>
<CopyAllFilesToSingleFolderForPackageDependsOn>
CustomCollectFiles;
$(CopyAllFilesToSingleFolderForPackageDependsOn);
</CopyAllFilesToSingleFolderForPackageDependsOn>
</PropertyGroup>
And place it before closing root project element.

Related

How to configure build for build undependent projects

I have a web project (asp.net) and I have several modules which should building in folder of the main project.
I can't find information about it.
I would like just build the main project and all modules should build too, but they not dependent with the main project.
Well, there still would be some dependency involved. You can use MsBuild task to build your projects along with main project. There are several options on how to include msbuild script to your solution, here is one of possible ways that allows to change the list of modules without reloading the MainProject and without adding dependencies to it.
Let's say we have MainProject and 2 modules: ClassLibrary1 and ClassLibrary2. Create a BuildModules.csproj file like this:
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="BuildModules" ToolsVersion="4.0">
<ItemGroup>
<ModuleProject Include="ClassLibrary1\ClassLibrary1.csproj"></ModuleProject>
<ModuleProject Include="ClassLibrary3\ClassLibrary3.csproj"></ModuleProject>
</ItemGroup>
<Target Name="BuildModules">
<MSBuild Projects="#(ModuleProject)" Properties="OutputPath=..\MainProject\Bin\Debug;Configuration=Debug;" ContinueOnError="true">
</MSBuild>
</Target>
</Project>
Now this is a concept only, so I've hardcoded the path to bin\debug folder. ContinueOnError is true so in case some error occurs, it doesn't affect on MainProject build process. Put this file in your solution root folder.
Now in MainProject properties on Build Events tab add the following Pre-build command:
call C:\Windows\Microsoft.NET\Framework64\v4.0.30319\msbuild.exe $(SolutionDir)BuildModules.csproj
The path to msbuild.exe may vary.
Now each time you build, it would rebuild your assemblies and you can easily change the list of modules in your BuildModules.csproj file.
UPDATE 1:
TFS not deploying all files. I guess this is because of our hardcoded paths to msbuild and etc. Try moving Pre-build command to MainProject.csproj file, in BeforeBuild target:
<Target Name="BeforeBuild">
<MSBuild Projects="..\BuildModules.csproj" Properties="Configuration=Debug"/>
</Target>
I'm not sure about the path though, but should be like this. And remove Pre-build command from build.

How do you include build outputs as additional files using web deploy (MSDEPLOY)?

I have a web project that has a post-build event that produces some minified files. Assume for the moment that this needs to remain a post- build event, and cannot be changed into a pre-build event.
I have successfully used a .wpp.targets file to package additional files:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<PropertyGroup>
<SourceRoot>$(BuildDirectory)\Sources\Web</SourceRoot>
</PropertyGroup>
<ItemGroup>
<FilesForPackagingFromProject Include="$(SourceRoot)\ExtraScripts\Normal.js">
<DestinationRelativePath>Scripts\%(RecursiveDir)%(FileName)%(Extension)</DestinationRelativePath>
<FromTarget>Web.wpp.targets</FromTarget>
</FilesForPackagingFromProject>
</ItemGroup>
</Project>
Unfortunately, this fails when I do the same thing for the files produced by the post-build step. This is because the CopyAllFilesToSingleFolderForMsdeploy target executes before the build. Even before the PrepareForBuild target.
Is there a way to add extra files and folders to the deploy package when they are produced during the build?
There was a similar question before (Add Plugin DLL in MVC 4 Website after publish).
You can deploy files only if they have been a part of your project before.
What we usually do is that we include those files as dummy files in our project and overwrite them during the build process. Because those files are part of your project, this approach will publish those files.

Specifying folders not to sync in Web Deploy

I use the following script to deploy my ASP.NET MVC app to our web server:
C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe MySolution.sln^
/p:Configuration=TeamCity-Test^
/p:OutputPath=bin^
/p:DeployOnBuild=True^
/p:DeployTarget=MSDeployPublish^
/p:MsDeployServiceUrl=https://mywebserver.com:8172/msdeploy.axd^
/p:username=MyDomain\MyUser^
/p:password=MyPassword^
/p:AllowUntrustedCertificate=True^
/p:DeployIisAppPath=mywebsitename.com^
/p:MSDeployPublishMethod=WMSVC
Now I need to specify to not sync the /uploads folder. Can I specify that in this script? Thanks!
Clarification:
I have the Uploads folder in my project. I'd like for Web Deploy to create the folder. I do not want it to delete the folder/subfolders/files from my web server because it contains user-uploaded content.
Clarification #2:
I just found the SkipExtraFilesOnServer=True option. However, I don't want this to be global. I'd like to set it to a single folder.
UPDATE:
Apparently, what you really want is prevent web deploy from removing existing directory on the destination server, but still have the folder created in case it's not there. You can accomplish this as follows:
create YourWebProjectName.wpp.targets file next to you the project file with the following content:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<MsDeploySkipRules Include="SkipELMAHFolderFiles">
<SkipAction></SkipAction>
<ObjectName>filePath</ObjectName>
<AbsolutePath>$(_DestinationContentPath)\\NameOfYourFolder\\.*</AbsolutePath>
<Apply>Destination</Apply>
<XPath></XPath>
</MsDeploySkipRules>
<MsDeploySkipRules Include="SkipELMAHFolderChildFolders">
<SkipAction></SkipAction>
<ObjectName>dirPath</ObjectName>
<AbsolutePath>$(_DestinationContentPath)\\NameOfYourFolder\\.*\\*</AbsolutePath>
<Apply>Destination</Apply>
<XPath></XPath>
</MsDeploySkipRules>
</ItemGroup>
</Project>
Change NameOfYourFolder and YourWebProjectName accordingly. This assumes, you have it in the root, I believe, you can use relative path if it's not the case.
The first MsDeploySkipRules entry tells webdeploy not to remove any files in Name_OfYourFolder.
The second MsDeploySkipRules tells webdeploy not to remove any child folders in Name_OfYourFolder.
Also, to have the folder created if it's not present on the destination server, you have to do the following:
include the folder into the project
add a dummy DeployemntPlaceholder.txt file into it and include it into the project as well
DeployemntPlaceholder.txt is required to tell MSBUild to add the folder into the package: empty folders are ignored.
I've tested this approach and it works fine when you run publish in the manner you've shown. I've used this answer to get the msbuild items syntaxt right. I believe, this is a MSBuild way to customize flags, passed to webdeploy by MSBuild Deployment Pipeline.
If you ran MSDeploy directly, you could use skip arguments in the following manner:
-skip:objectname='filePath',absolutepath='logs\\.*\\someNameToExclude\.txt'
UPDATE 2
You might also want to have ACL write permissions set on your \Uploads folder - there's a complete guide to do this: Setting Folder Permissions On Web Publish
Conserning the original question "Specifying folders not to sync in Web Deploy", the easiest way to do this is as follows:
You can create a publish profile and add the following lines:
<PropertyGroup>
<ExcludeFilesFromDeployment>
File1.aspx;File2.aspx
</ExcludeFilesFromDeployment>
<ExcludeFoldersFromDeployment>
Folder1;Folder2
</ExcludeFoldersFromDeployment>
</PropertyGroup>
I've tested this approach for excluding files using publish profiles. An easy guide is here (scroll to Edit the .pubxml file to exclude robots.txt section).
You can also do this in .wpp.targets file or edit you csproj. See more information at Web Deployment FAQ for Visual Studio and ASP.NET

How To Run MSBuild scripts in .wixproj?

Im trying to learn to make a web installer using Windows Installer XML (WIX 3.5). I found this blog about using msbuild in .wixproj files to avoid the scenario where the installer ends up dropping the web project assemblies right in the root of the app instead of keeping them in the bin folder like they're supposed to be.
Here is the link to that:
<http://www.paraesthesia.com/archive/2010/07/30/how-to-consume-msdeploy-staged-web-site-output-in-a.aspx>
But after adding the MSBuild scripts in the .wixproj file, I don't know what to do anymore. According to the instruction after adding the MSBuild script:
"When that target runs, you'll see a .wxs file pop out in the .wixproj project folder. Add the generated .wxs to your .wixproj project so it knows to include it in the build."
I really don7t know what this means. How can I run the target? I tried to build it but there was no .wxs file generated in the .wixproj folder.
Am I missing something? Please help...
Assuming you have added the section from the tutorial:
<Target Name="BeforeBuild">
...
</Target>
The target will be run automatically when you build the project. The "BeforeBuild" target is one of the standard entry-points to add your own modifications to the build. The target will then generate a file (named [WebProjectName].wxs that is placed in the same directory as your wixproj file. Click on the show all files button in visual studio and right-click on the file and "Include in project" That will then include the wxs is your installer and when you next build it will have the correct folder/file structure.

Make MSDeploy (Visual Studio) not delete App_Data folder but delete everything else

I'm using Visual Studio's Publish button to deploy my website, and want a different App_Data folder on the server. There's a checkbox for Leave extra files on destination (do not delete) which prevents my App_Data folder from getting deleted, but then it'll eventually accumulate a lot of vestigial files as the website changes.
Is there any way to make it exclude just App_Data when it deletes everything?
It can be done when you invoke msdeploy manually - just add the following parameter:
-skip:Directory=\\App_Data
See Web Deploy Operation Settings. The path is a regular expression, so it is quite flexible.
If you deploy using the VS-generated ProjectName.deploy.cmd script, you can also pass this parameter in the _MsDeployAdditionalFlags environment variable (when running that script).
This is the best I've come up with for our needs (we have a similar situation as you). I haven't tried integrating it with VS's Publish button, since we deploy from command line.
EDIT:
I have learned a few things about MSDeploy since I posted this answer, so I thought I'd update it now.
First of all, the above skip rule skips any operations on the matching path (App_Data). If more granular control is needed, a more verbose syntax is available. For example, to skip only deletes (to keep any extra files on target server, but add any new ones and update existing ones):
-skip:skipaction='Delete',objectname='filePath',absolutepath='\\App_Data\\.*' -skip:skipaction='Delete',objectname='dirPath',absolutepath='\\App_Data\\.*'
This skips deletes of all files and all subfolders (with all their content) in App_Data, but doesn't prevent adds and updates.
Another useful thing is that skip rules can be defined in the project file (.csproj) so that they are automatically included in the .deploy.cmd script generated along with the package. This makes it unnecessary to pass them to the script through _MsDeployAdditionalFlags.
The above skip rule will be added if the following is included in csproj file:
<PropertyGroup>
<OnBeforePackageUsingManifest>AddCustomSkipRules</OnBeforePackageUsingManifest>
</PropertyGroup>
<Target Name="AddCustomSkipRules">
<ItemGroup>
<MsDeploySkipRules Include="SkipDeleteAppData">
<SkipAction>Delete</SkipAction>
<ObjectName>filePath</ObjectName>
<AbsolutePath>$(_Escaped_PackageTempDir)\\App_Data\\.*</AbsolutePath>
<XPath>
</XPath>
</MsDeploySkipRules>
<MsDeploySkipRules Include="SkipDeleteAppData">
<SkipAction>Delete</SkipAction>
<ObjectName>dirPath</ObjectName>
<AbsolutePath>$(_Escaped_PackageTempDir)\\App_Data\\.*</AbsolutePath>
<XPath>
</XPath>
</MsDeploySkipRules>
</ItemGroup>
</Target>
(the names AddCustomSkipRules and SkipDeleteAppData are completely arbitrary; $(_Escaped_PackageTempDir) is supposed to be possibly needed, but in practice I've always seen it evaluate to an empty string)
See Web Deploy: Customizing a deployment package and How to set MSDeploy settings in .csproj file for more info.
One caveat: this only adds those rules to the .deploy.cmd script, so it is useless if you want to use the graphical IIS Manager for package deployment, as it doesn't use that script (the same probably goes for deployment from VS, but I haven't checked).
From my experience, MsDeploySkipRules are only run when deploying from the command line.
If you are publishing from Visual Studio to Azure (or using another Web Deploy method), you can set the following when Publishing.
Remove additional files at destination
Exclude files from the App_Data folder
When "Remove additional files at destination" is checked, it will make a comparison between the files and folders you are deploying and the ones on the server.
Be warned, you may run into issues if you have User Generated content, e.g. Uploads. But this could be worked around by storing those folders in a different location, e.g. S3 / Azure Storage.
From Powershell, if you want to use msdeploy.exe or the myproj.deploy.cmd (Deploying Web Packages) produced when publishing with Web Deploy Package, in order to to skip deleting the App_Data folder and avoid the
All arguments must begin with "-"
error, you have to enclose the skip directive in triple quotes, e.g. :
myproj.deploy.cmd /y /u:myusername /p:mypass """-skip:Directory=\\App_Data"""
Have you looked at Package/Publish Web in the project settings, because you can tell it to exlude files in the App_Data folder.
For asp.net core web apps use MsDeploySkipRules in csproj.
<ItemGroup>
<MsDeploySkipRules Include="CustomSkipFile">
<ObjectName>filePath</ObjectName>
<AbsolutePath><dir_name>\\app_data</AbsolutePath>
</MsDeploySkipRules>
<MsDeploySkipRules Include="CustomSkipFile">
<ObjectName>dirPath</ObjectName>
<AbsolutePath><dir_name>\\app_data</AbsolutePath>
</MsDeploySkipRules>
</ItemGroup>
Replace <dir_name> with your root folder
https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/visual-studio-publish-profiles?view=aspnetcore-2.1#exclude-files
This isn't ideal since you may be copying a lot of files doing this (I'm not), but here's my solution for backing up a folder. Adapted to move the folder to the location from which it will be published, during the publish process. Put this in your pubxml file:
<Project>
...
<Target Name="PreserveSelectedFolder" AfterTargets="GatherAllFilesToPublish">
<ItemGroup>
<SITEDIR Include="$(publishUrl)\App_Data\**\*.*" />
</ItemGroup>
<Copy SourceFiles="#(SITEDIR)"
DestinationFolder="$(ProjectDir)\obj\$(Configuration)\Package\PackageTmp\%(RecursiveDir)"></Copy>
</Target>
</Project>
And if you just want to do any number of specific known files, do one Copy block per file:
<Target Name="PreserveSelectedFiles" AfterTargets="GatherAllFilesToPublish" >
<Copy SourceFiles="$(publishUrl)\MYFILENAME.EXT"
DestinationFolder="$(ProjectDir)\obj\$(Configuration)\Package\PackageTmp\"
Condition="Exists('$(publishUrl)\MYFILENAME.EXT')"></Copy>
</Target>
Put this in your pubxml file:
<ExcludeApp_Data>True</ExcludeApp_Data>

Resources