Publish one web project from solution with msbuild

I blogged about this at http://sedodream.com/2013/03/06/HowToPublishOneWebProjectFromASolution.aspx a few months back. I've copied the details here as well, see below.


Today on twitter @nunofcosta asked me roughly the question “How do I publish one web project from a solution that contains many?”

The issue that he is running into is that he is building from the command line and passing the following properties to msbuild.exe.

/p:DeployOnBuild=true
/p:PublishProfile='siteone - Web Deploy'
/p:Password=%password%

You can read more about how to automate publishing at http://sedodream.com/2013/01/06/CommandLineWebProjectPublishing.aspx.

When you pass these properties to msbuild.exe they are known as global properties. These properties are difficult to override and are passed to every project that is built. Because of this if you have a solution with multiple web projects, when each web project is built it is passed in the same set of properties. Because of this when each project is built the publish process for that project will start and it will expect to find a file named siteone – Web Deploy.pubxml in the folder *Properties\PublishProfiles*. If the file doesn’t exist the operation may fail.

Note: If you are interested in using this technique for an orchestrated publish see my comments at https://stackoverflow.com/a/14231729/105999 before doing so.

So how can we resolve this?

Let’s take a look at a sample (see links below). I have a solution, PublishOnlyOne, with the following projects.

  1. ProjA
  2. ProjB

ProjA has a publish profile named ‘siteone – Web Deploy’, ProjB does not. When trying to publish this you may try the following command line.

msbuild.exe PublishOnlyOne.sln /p:DeployOnBuild=true /p:PublishProfile=’siteone – Web Deploy’ /p:Password=%password%

See publish-sln.cmd in the samples.

If you do this, when its time for ProjB to build it will fail because there’s no siteone – Web Deploy profile for that project. Because of this, we cannot pass DeployOnBuild. Instead here is what we need to do.

  1. Edit ProjA.csproj to define another property which will conditionally set DeployOnBuild
  2. From the command line pass in that property

I edited ProjA and added the following property group before the Import statements in the .csproj file.

<PropertyGroup>
<DeployOnBuild Condition=" '$(DeployProjA)'!='' ">$(DeployProjA)</DeployOnBuild>
</PropertyGroup>

Here you can see that DeployOnBuild is set to whatever value DeployProjA is as long as it’s not empty. Now the revised command is:

msbuild.exe PublishOnlyOne.sln /p:DeployProjA=true /p:PublishProfile=’siteone – Web Deploy’ /p:Password=%password%

Here instead of passing DeployOnBuild, I pass in DeployProjA which will then set DeployOnBuild. Since DeployOnBuild wasn’t passed to ProjB it will not attempt to publish.

You can find the complete sample at https://github.com/sayedihashimi/sayed-samples/tree/master/PublishOnlyOne.


Another option which is better in scenario that requires manual customization of publishing, is to use IsPublishable MSBuild property (found it here https://github.com/dotnet/docs/issues/13365):

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netcoreapp2.2</TargetFramework>

    <IsPublishable>false</IsPublishable>
  </PropertyGroup>
...
</Project>

MSBuild conditions could be used for setting IsPublishable value according to different flows, for example:

    <IsPublishable Condition="'$(TargetFramework.TrimEnd(`0123456789`))' == 'net'>false</IsPublishable>

There is a much simpler solution for this. MSBuild supports targeting a single project while building the solution. You do this by putting the project name in the Target parameter. Note that this is the visual name of the project you specify in the solution (not necessarily the same as the name of the .csproj file).

Note: The only "trick" needed here is to replace the dots (.) in the project name with underscores (_).

Example MSBuild command line, if your project name is "Your.Project.Name":

msbuild.exe YourSolutionName.sln /T:"Your_Poject_Name" /P:DeployOnBuild=true /P:PublishProfile=YourPublishProfile.pubxml

You may also specify a build target for that project, but this target should exist for all projects in the solution:

msbuild.exe YourSolutionName.sln /T:"Your_Poject_Name:Rebuild" /P:DeployOnBuild=true /P:PublishProfile=YourPublishProfile.pubxml

Sources

  1. This is partially documented in MSDN since Visual Studio 2008: https://msdn.microsoft.com/en-us/library/ms164311(v=vs.140).aspx
  2. Special thanks to Vasil Trifonov for pointing out the replacement trick: http://www.codeproject.com/Articles/654910/How-to-build-a-specific-project-from-a-solution-wi