Web Setup replacement to create MSI in VS2012? - installation

I have a project in VS2010 that uses Web Setup projects for deployment. I'm now looking into migrating it to VS2012 and have to find a replacement setup routine.
The requirements I have:
One-step build to create a deploy package/installer on a dev machine.
Setup program/routine that can be run on the server - without Visual Studio available.
No direct interaction between Visual Studio and the server. I have to copy the setup files over through an RDP session.
Setup of web applications (MVC) and Windows Services, preferably bundled in one single installer (new requirement currently not solve din Web Setup project).
Possibility to run EF Migrations as part of setup (currently done through a custom action).
Where should I start? Should I look into the improved publishing features in VS2012? Should I look at Wix? Something else?

Looking deeper into Visual Studio 2012 and trying to work with it the way it was intended, instead of against it, we ended up using web deploy packages. It doesn't create an MSI file, but instead a zip file that can be easily imported into IIS on the target machine.
The Windows service project was added as a reference to the web site project. That way the binaries for the service are included in the bin directory of the web site. The migrate.exe file from Entity framework was added as a link from the bin directory which means it is deployed too.
Finally we added a project.wpp.targets file to the project that runs the required commands to install and start the service and to get the service's config file included in the deploy. This worked for us, but is not really that elegant (e.g. the install paths of the site for different configurations is hard coded).
The project.wpp.targets file:
<?xml version="1.0" encoding="utf-8" ?>
<!--
*** WARNING ***
This file is cached by visual studio and changes won't take effect until
visual studio is restarted. When editing this file, it is better to run the
build step for packaging from the command line (a VS command prompt).
There are some problems with dependencies not being correctly identified that
way, but at least the archive.xml file can be verified from the command prompt.
msbuild orderportal.csproj /t:package /p:Configuration=SysTest /p:DeployOnBuild=true;DeployTarget=Package
-->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<IncludeRunMigrations>TRUE</IncludeRunMigrations>
<AfterAddIisSettingAndFileContentsToSourceManifest Condition="'$(AfterAddIisSettingAndFileContentsToSourceManifest)'==''">
$(AfterAddIisSettingAndFileContentsToSourceManifest);
RunMigrations;
ServiceInstall;
</AfterAddIisSettingAndFileContentsToSourceManifest>
<IncludeServiceInstall>TRUE</IncludeServiceInstall>
<BeforeAddContentPathToSourceManifest Condition="'$(BeforeAddContentPathToSourceManifest)' == ''">
$(BeforeAddContentPathToSourceManifest);
ServiceUnInstall;
</BeforeAddContentPathToSourceManifest>
<DeploymentDir Condition="'$(Configuration)'=='SysTest' AND '$(DeploymentDir)'==''">c:\inetpub\wwwroot\SysTest\</DeploymentDir>
<DeploymentDir Condition="'$(Configuration)'=='IntTest' AND '$(DeploymentDir)'==''">c:\inetpub\wwwroot\IntTest\</DeploymentDir>
<DeploymentDir Condition="'$(Configuration)'=='Prod' AND '$(DeploymentDir)'==''">c:\inetpub\wwwroot\</DeploymentDir>
<CopyAllFilesToSingleFolderForPackageDependsOn>
IncludeServicesAppConfig;
$(CopyAllFilesToSingleFolderForPackageDependsOn);
</CopyAllFilesToSingleFolderForPackageDependsOn>
</PropertyGroup>
<Target Name="RunMigrations" Condition="'$(IncludeRunMigrations)' == 'TRUE'">
<Message Text="Adding migration running"/>
<ItemGroup>
<MsDeploySourceManifest Include="runCommand">
<path>$(DeploymentDir)bin\migrate.exe /startupdirectory:$(DeploymentDir)bin Topsi.Core.dll /startUpConfigurationFile:$(DeploymentDir)web.config</path>
<waitAttempts>1</waitAttempts>
<waitInterval>60000</waitInterval>
<dontUseCommandExe>true</dontUseCommandExe>
<AdditionalProviderSettings>waitInterval;waitAttempts;dontUseCommandExe</AdditionalProviderSettings>
</MsDeploySourceManifest>
</ItemGroup>
</Target>
<Target Name="ServiceUnInstall" Condition="'$(IncludeServiceInstall)' == 'TRUE'">
<Message Text="Adding service uninstall" />
<ItemGroup>
<MsDeploySourceManifest Include="runCommand">
<path>net stop "Topsi Schedule Service $(Configuration)"</path>
<waitAttempts>1</waitAttempts>
<waitInterval>60000</waitInterval>
<dontUseCommandExe>true</dontUseCommandExe>
<AdditionalProviderSettings>waitInterval;waitAttempts;dontUseCommandExe</AdditionalProviderSettings>
</MsDeploySourceManifest>
<MsDeploySourceManifest Include="runCommand">
<path>C:\Windows\Microsoft.NET\Framework\v4.0.30319\installutil.exe /u $(DeploymentDir)bin\Topsi.Services.exe</path>
<waitAttempts>1</waitAttempts>
<waitInterval>60000</waitInterval>
<dontUseCommandExe>true</dontUseCommandExe>
<AdditionalProviderSettings>waitInterval;waitAttempts;dontUseCommandExe</AdditionalProviderSettings>
</MsDeploySourceManifest>
</ItemGroup>
</Target>
<Target Name="ServiceInstall" Condition="'$(IncludeServiceInstall)' == 'TRUE'">
<Message Text="Adding service install"/>
<ItemGroup>
<MsDeploySourceManifest Include="runCommand">
<path>C:\Windows\Microsoft.NET\Framework\v4.0.30319\installutil.exe $(DeploymentDir)bin\Topsi.Services.exe</path>
<waitAttempts>1</waitAttempts>
<waitInterval>60000</waitInterval>
<dontUseCommandExe>true</dontUseCommandExe>
<AdditionalProviderSettings>waitInterval;waitAttempts;dontUseCommandExe</AdditionalProviderSettings>
</MsDeploySourceManifest>
<MsDeploySourceManifest Include="runCommand">
<path>net start "Topsi Schedule Service $(Configuration)"</path>
<waitAttempts>1</waitAttempts>
<waitInterval>60000</waitInterval>
<dontUseCommandExe>true</dontUseCommandExe>
<AdditionalProviderSettings>waitInterval;waitAttempts;dontUseCommandExe</AdditionalProviderSettings>
</MsDeploySourceManifest>
</ItemGroup>
</Target>
<Target Name="IncludeServicesAppConfig">
<ItemGroup>
<_CustomFiles Include="..\Services\bin\$(Configuration)\Topsi.Services.exe.config">
<DestinationRelativePath>%(RecursiveDir)%(Filename)%(Extension)</DestinationRelativePath>
</_CustomFiles>
<FilesForPackagingFromProject Include="%(_CustomFiles.Identity)">
<DestinationRelativePath>bin\%(RecursiveDir)%(Filename)%(Extension)</DestinationRelativePath>
</FilesForPackagingFromProject>
</ItemGroup>
</Target>
</Project>

Related

Visual Studio project with a custom build step only (no default build)

I want to create a Visual Studio project that would allow me to see a bunch of JavaScript and other files and edit them as normal, but would also have a build step that can run any custom commands I want (currently some npm commands, possibly more later). Basically I want 3 features combined:
Be able to browse and edit files just like for any VS project (C#, C++, etc.)
Be able to run a custom build step by selecting "Build" in Visual Studio (including building the whole solution).
Be able to run that same custom build step from the command line (MSBuild).
Using a "shared project" (.shproj) allows me to easily see and edit the files, but there is no Build item in the context menu, even if I manually add a Build target:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Label="Globals">
<ProjectGuid>...</ProjectGuid>
</PropertyGroup>
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.Default.props" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.props" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.CSharp.targets" />
<Import Project="MyItems.projitems" Label="Shared" />
<PropertyGroup>
<Configuration>Debug</Configuration>
<Platform>Any CPU</Platform>
</PropertyGroup>
<Target Name="Build">
<Exec Command="ECHO My custom build!" />
</Target>
</Project>
I've also tried using a stripped-down VC++ project (since I don't actually want to run the C++ compiler) and this allows a build to be run from VS, but opening the project logs warnings like error MSB4057: The target "GetProjectDirectories" does not exist in the project. and trying to add files to fails with that error or similar ones.
There must be an easier way to do this!
From your current description, I think you want to create a js project in VS IDE.
However, VS IDE has the node js project template by default. And you should install the workload Node.js development under VS_Installer so that you can use it.
After that, you can create such project.
1) Adding js files or other files by right-click on the project-->Add-->Existing Item so that you can modify the files on VS IDE.
2) If you want to execute a custom build step that does not break the whole build, you should make the custom target depends on the default build.
You can use this:
<Target Name="CustomStep" AfterTargets="Build">
<Exec Command="ECHO My custom build!" />
</Target>
or
<Target Name="CustomStep" BeforeTargets="Build">
<Exec Command="ECHO My custom build!" />
</Target>
Note: If you use
<Target Name="Build">
<Exec Command="ECHO My custom build!" />
</Target>
It will overwrite the system build process and instead, run the command, which breaks the whole default build.
3) If you want to execute the custom build on msbuild command, you should specify the name of the custom target:
msbuild xxx\xxx.proj -t: CustomStep(the name of the custom target)
===============================================
Besides, if you still want to use C++ project template, you could create a empty c++ project which does not contain any clcompile files and then do the same steps.
If you do not want to use C++ compiler, you should only remove any xml node on the vcxproj file like these:
<ClCompile Include="xxx.cpp" />
<ClInclude Include="xxx.h" />
When you use the empty C++ project, you do not have to worry about that.
=========================================
Update 1
If you want to build this project on a build sever without VS IDE, I suggest you could install Build Tool for VS2019 which is an independent, lightweight build command line(It is equivalent to dotnet cli).
Build Tool for VS2019
Under All Downloads-->Tools for Visual Studio 2019--> Build Tools for Visual Studio 2019
Then, you have to install the related build workload such as Node.js Build tools and then we can use the command line to build node.js project on build sever.
The entire installation process is fast.
Inspired by Perry Qian-MSFT's answer, I managed to strip down a Node.js project to the bare minimum that I needed to get Visual Studio to load and build it, but without referencing any external files.
The main trick was VS needs a target named "CoreCompile" to be defined to show the Build menu item! (It also needs a "Build" target, but that one is more obvious.)
My project now looks like this:
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>(some guid)</ProjectGuid>
<ProjectHome>.</ProjectHome>
<ProjectTypeGuids>{3AF33F2E-1136-4D97-BBB7-1795711AC8B8};{9092AA53-FB77-4645-B42D-1CCCA6BD08BD}</ProjectTypeGuids>
</PropertyGroup>
<!-- These property groups can be empty, but need to be defined for VS -->
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
</PropertyGroup>
<Import Project="My.Build.targets" />
<!-- Define empty standard MSBuild targets, since this project doesn't have them. Doing it this way allows My.Build.targets to also be used in a project that does define them. -->
<Target Name="Build" />
<Target Name="ReBuild" />
<Target Name="Clean" />
<!-- NOTE: a target named "CoreCompile" is needed for VS to display the Build menu item. -->
<Target Name="CoreCompile" />
<!-- Files shown in Visual Studio - adding and removing these in the UI works as expected -->
<ItemGroup>
<Content Include="myfile..." />
</ItemGroup>
</Project>
And My.Build.targets looks like this:
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="MyBuild" AfterTargets="Build">(build steps)</Target>
<Target Name="MyReBuild" AfterTargets="ReBuild">(re-build steps)</Target>
<Target Name="MyClean" AfterTargets="Clean">(clean steps)</Target>
<!-- This target is needed just to suppress "warning NU1503: Skipping restore for project '...'. The project file may be invalid or missing targets
required for restore." -->
<Target Name="_IsProjectRestoreSupported" Returns="#(_ValidProjectsForRestore)">
<ItemGroup>
<_ValidProjectsForRestore Include="$(MSBuildProjectFullPath)" />
</ItemGroup>
</Target>
</Project>

Azure: Include unreferenced DLLs when publish

I was struggling to publish my project because some DLLs were missing. After some investigations, I found what I was looking for:
http://sedodream.com/2010/05/01/WebDeploymentToolMSDeployBuildPackageIncludingExtraFilesOrExcludingSpecificFiles.aspx
Here my code:
<Target Name="CustomCollectFiles">
<Message Text="Publishing unreferenced DLLs" Importance="High" />
<ItemGroup>
<_CustomFiles Include="$(UnreferencedDlls)" />
<FilesForPackagingFromProject Include="%(_CustomFiles.Identity)">
<DestinationRelativePath>bin\%(RecursiveDir)%(Filename)%(Extension)</DestinationRelativePath>
</FilesForPackagingFromProject>
</ItemGroup>
</Target>
It is working fine now but only when I publish on local. When I try to publish on Azure, these same DLLs are missing. So I tried to add the following line:
<DestinationRelativePath>obj\Release\Package\PackageTmp\bin\%(RecursiveDir)%(Filename)%(Extension)</DestinationRelativePath>
Because when I publish on azure, the Output says:
Copying all files to temporary location below for package/publish:
obj\Release\Package\PackageTmp.
But still, the DLLs are missing and I have no idea how to add them when I'm publishing on Azure.
I find a solution. The first one add the DLLs when I deploy on my local machine and the second do the same thing but when I deploy the app on Azure.
<PropertyGroup>
<!-- Publish on the FILE SYSTEM -->
<CopyAllFilesToSingleFolderForPackageDependsOn>
CustomCollectFiles;
$(CopyAllFilesToSingleFolderForPackageDependsOn);
</CopyAllFilesToSingleFolderForPackageDependsOn>
<!-- Publish on AZURE: Web Deploy -->
<CopyAllFilesToSingleFolderForMSDeployDependsOn>
CustomCollectFiles;
$(CopyAllFilesToSingleFolderForMSDeployDependsOn);
</CopyAllFilesToSingleFolderForMSDeployDependsOn>
</PropertyGroup>

Is there a pubxml setting to delete folder on production prior to publishing using Visual studio

I am deploying visual studio project that contains Angular 5 application that is built in dist subfolder. Because dist subfolder is not a part of the project, I am copying it using the publishing profile settings (in .pubxml file):
<Target Name="CustomCollectFiles">
<Message Text="=== CustomCollectFiles ===" Importance="high" />
<ItemGroup>
<_CustomFiles Include="dist\**\*" />
<FilesForPackagingFromProject Include="%(_CustomFiles.Identity)">
<DestinationRelativePath>dist\%(Filename)%(Extension)</DestinationRelativePath>
</FilesForPackagingFromProject>
</ItemGroup>
</Target>
<PropertyGroup>
<CopyAllFilesToSingleFolderForPackageDependsOn>
CustomCollectFiles;
$(CopyAllFilesToSingleFolderForPackageDependsOn);
</CopyAllFilesToSingleFolderForPackageDependsOn>
<CopyAllFilesToSingleFolderForMsdeployDependsOn>
CustomCollectFiles;
$(CopyAllFilesToSingleFolderForMsdeployDependsOn);
</CopyAllFilesToSingleFolderForMsdeployDependsOn>
</PropertyGroup>
This works fine the first time but on subsequent deployments the dist folder doesn't get updated. I thought of deleting it first before publishing so it's updated every time but I can't find the xml tag/setting for that.
Is there a setting/tag to delete the folder dist on production prior to publishing?

MSBuild pre clean customization

I am working with Visual Studio 2010. I have directed project output to a specific folder which will contain all the DLLs and EXEs when built. However when I clean the solution, the folder is not getting cleaned, and the DLLs are still present in it.
Can anyone tell me how to handle the clean solution command to clear out the folders I want to clean? I tried working with MSBuild and handling the BeforeClean and AfterClean targets, but it did not provide the desired result.
The answer from Sergio should work but I think it could be cleaner to override the BeforeClean/AfterClean targets. These are hooks into the build/clean process provided by microsoft. When you do a clean, VS do call the targets : BeforeClean;Clean;AfterClean and by default the first and the last do nothing.
In one of your existing .csproj file you can add the following :
<Target Name="BeforeClean">
<!-- DO YOUR STUFF HERE -->
</Target>
You can add to your VS .sln file special target named let's say BuildCustomAction.csproj:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
</PropertyGroup>
<ItemGroup>
<CleanOutCatalogFiles Include="..\..\bin\$(Configuration)\**\*.dll">
<Visible>false</Visible>
</CleanOutCatalogFiles>
<CleanOutCatalogFiles Include="..\..\bin\$(Configuration)\**\*.exe">
<Visible>false</Visible>
</CleanOutCatalogFiles>
</ItemGroup>
<Target Name="Build">
</Target>
<Target Name="Rebuild"
DependsOnTargets="Clean;Build">
</Target>
<Target Name="Clean"
Condition="'#(CleanOutCatalogFiles)'!=''">
<Message Text="Cleaning Output Dlls and EXEs" Importance="high" />
<Delete Files="#(CleanOutCatalogFiles)" />
</Target>
</Project>
Place it everywhere you want and specify relative path to the output catalog for your binaries. Add in VS this project as existing. That's all. With this you can do own custom actions for three common actions in VS: Build, Rebuild, Clean.
There exists more complex way to customize build process using CustomBeforeMicrosoftCommonTargets and CustomAfterMicrosoftCommonTargets but it requires to be very good in MSBuild.
Hope this helps.

Visual Studio. Publish project from command line

Is there a way to publish a web project in MS Visual Studio 2010 using CLI? I use DevEnv.exe /Build to build a project and it works fine, but I could not find option to Publish a project.
One other thing I want to mention. I am trying to publish web project NOT to the IIS directly. I have a location where I publish several projects and then build them automatically into NSIS bundle to be deployed.
From ASP.NET Web Deployment using Visual Studio: Command Line Deployment, you can use
msbuild myproject.csproj /p:DeployOnBuild=true /p:PublishProfile=MyPublishProfile
where MyPublishProfile is the profile name that you've already set up somewhere
What works best is to add following target to the project file:
<Target Name="AfterBuild">
<Message Text="Copying to Deployment Dir:" />
<Copy SourceFiles="#(Content)" DestinationFolder="..\XXX\%(Content.RelativeDir)" />
<CreateItem Include="$(OutputPath)\*">
<Output TaskParameter="Include" ItemName="Binaries"/>
</CreateItem>
<Copy SourceFiles="#(Binaries)" DestinationFolder="..\XXX\bin" />
</Target>
This way, whenever project got build (from command line or from IDE) it automatically get deployed to specified folder. Thank you everybody for pointing me to right direction.
The /t:publish switch is for ClickOnce applications only, it's not applicable to web projects. Hence the error saying it's unpublishable. :)
#RobKent As https://stackoverflow.com/a/2775437/21233364
using
<Target Name="AfterBuild" Condition=" '$(Configuration)' == 'Release' ">
<Message Text="Copying to Deployment Dir:" />
<Copy SourceFiles="#(Content)" DestinationFolder="..\XXX\%(Content.RelativeDir)" />
<CreateItem Include="$(OutputPath)\*">
<Output TaskParameter="Include" ItemName="Binaries"/>
</CreateItem>
<Copy SourceFiles="#(Binaries)" DestinationFolder="..\XXX\bin" />
</Target>
you can have publish only on release compiling.

Resources