Bower and NPM (Node.js package manager) are really helpful open source tools which are integrating well with new versions of Visual Studio. They allowing to very conveniently download popular script libraries and other client-side scripts, and in the case NPM server-side tools as well.
Gulp is a wildly popular build tool which is mostly used for building client-side scripts, but in general could be used for pretty much anything, like publishing your scripts or running tests.
WebDeploy (msdeploy) is a Microsoft deployment tool which can take a package (created by either Visual Studio or msbuild) and deploy it to target web server.
However, when these tools are used together in the same solution, there could be some frictions. WebDeploy was designed with the concept of project file in mind, while bower, NPM and Gulp are built to operate on a set of files.
Let’s consider the following scenario:
- We building a rich web application (using Visual Studio 2015) and we using a lots of script libraries. For example, we using angular.js, bootstrap and many angular.js plugins.
- We using Bower to download our script libraries. Bower is storing downloaded packages in “bower_components” folder. This folder is not part of our Visual Studio solution, because why would it be? Visual Studio is not controlling what’s in this folder – Bower does.
- We need to reference downloaded script libraries from our web pages. We could just reference them from bower-components, of course, but we know better than that! We want to minify and bundle these scripts together, as well our own scripts. And here comes Gulp.
- Gulp is minifying and bundling all client side assets (we wrote a gulp task for it) and writing its output to “build” folder. This could be any folder, of course, but this folder is not a part of Visual Studio solution and these output files are not under source control, because files in this folder are produced dynamically during the build process, and we should not be storing any build results in source control.
So, that’s what we have: we have a set of files which is dynamically produced during the build and Visual Studio knows nothing about these files.
Everything works fine in development, because files produced by Gulp are on local file system.
Now we need to publish the application to the web server. The common way to publish the web application is to use WebDeploy tool. There are different ways use WebDeploy:
- Visual Studio can package application and then deploy it itself
- Visual Studio can package application then package could be deployed manually
- In case of automated build, build system (like Visual Studio Team Services) can invoke MSBuild to package application and then deploy it to target server by either calling WebDeploy, or by deploying it to Azure App Service.
In either case, we need to create package first. The problem with packaging is that it’s done by Visual Studio or MSbuild, but neither tool doesn’t know about our script files generated by Gulp because these files are not referenced from project file. There is a packaging option to include all files in project folder into the package, but we not considering it because it would be deploying all source files too, which we don’t want.
The solution is the following: manually edit project file to add custom build target.
Add the following code at the very end of the project file (just before /Project closing tag):
<Target Name="AddGulpOutput"> <ItemGroup> <_CustomFiles Include="build\**\*" /> <FilesForPackagingFromProject Include="%(_CustomFiles.Identity)"> <DestinationRelativePath>build\%(RecursiveDir)%(Filename)%(Extension)</DestinationRelativePath> </FilesForPackagingFromProject> </ItemGroup> </Target> <PropertyGroup> <CopyAllFilesToSingleFolderForPackageDependsOn> AddGulpOutput; $(CopyAllFilesToSingleFolderForPackageDependsOn); </CopyAllFilesToSingleFolderForPackageDependsOn> <CopyAllFilesToSingleFolderForMsdeployDependsOn> AddGulpOutput; $(CopyAllFilesToSingleFolderForPackageDependsOn); </CopyAllFilesToSingleFolderForMsdeployDependsOn> </PropertyGroup>
The following line is including all files in “build” folder into the package:
<_CustomFiles Include="build\**\*" />
And this line is adding these files (i.e. content of build folder) into the build folder of the package:
You can customize the markup to include Bower or NPM packages, for example.