Why does MSBuild ignore my BeforePublish target?
Solution 1
The answer I finally came up with which works well for Visual Studio 2010 and Visual Studio 2012; put this just before the end of your web application's .csproj
file:
<Project...
[...]
<!-- The following makes sure we can't accidentally publish a non-Release configuration from within Visual Studio -->
<Target Name="PreventNonReleasePublish2010" BeforeTargets="PipelinePreDeployCopyAllFilesToOneFolder" Condition="'$(BuildingInsideVisualStudio)'=='true' AND '$(VisualStudioVersion)'=='10.0'">
<Error Condition="'$(Configuration)'!='Release'" Text="When publishing from Visual Studio 2010, you must publish the Release configuration!" />
</Target>
<Target Name="PreventNonReleasePublish2012" BeforeTargets="MSDeployPublish" Condition="'$(BuildingInsideVisualStudio)'=='true' AND '$(VisualStudioVersion)'=='11.0'">
<Error Condition="'$(Configuration)'!='Release'" Text="When publishing from Visual Studio 2012, you must publish the Release configuration!" />
</Target>
</Project>
Read on to see my thinking behind this answer, but basically it revolves around the fact that Visual Studio 2010 defines the PipelinePreDeployCopyAllFilesToOneFolder
Target to hook on to, and Visual Studio 2012 defines the more "standard" MSDeployPublish
Target to hook on to.
The above code only allows deploy publishing when in a Release
configuration from within Visual Studio, but it could easily be modified to prevent all deploy publishing from within Visual Studio.
AFAIR, "Publish" from Visual Studio 2010 context menu invokes webdeploy\msdeploy tool. I played with it a bit, but I didn't liked at all. If you still want to use this functionality and insert your target somewhere - you need to know the exact target and its dependency property.
Check
c:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.targets
You will find two tasks - MSDeploy and VSMSDeploy. The latter one sounds right for me. The first one is not used in this file at all. But VSMSDeploy used in three different targets, PackageUsingManifest, TestDeployPackageToLocal and MSDeployPublish. Again the latter one sounds good ;)
<Target Name="MSDeployPublish" DependsOnTargets="$(MSDeployPublishDependsOn)">
So you just need to override one property. Put this before your target and "YourTargetName" will be called right before MSDeployPublish.
<PropertyGroup>
<MSDeployPublishDependsOn Condition="'$(MSDeployPublishDependsOn)'!=''">
$(MSDeployPublishDependsOn);
YourTargetName;
</MSDeployPublishDependsOn>
</PropertyGroup>
If you already switched to MSBuild 4.0, there is an easier way to hook your target. You just need to specify the BeforeTarget
attribute. In our case it will be like this:
<Target Name="MyTarget" BeforeTargets="MSDeployPublish">
<Error Condition="'foo'=='foo'" Text="test publish error" />
</Target>
I hope this helps. Ask if you have more questions.
PS: I didn't checked all that, because I don't have any MSDeploy-ready environments ;)
NB: I remember that I was discouraged from using MSDeploy for our own products because it was pretty counter-intuitive to properly configuring it for a continuous integration (CI) system. Maybe I wasn't very good at that, and your solution will work properly. But proceed with MSDeploy carefully.
Solution 2
Not sure if it would work in VS 2010 as I've only tested this in VS 2012, but I've found that putting the target in the (ProjectDir)/Properties/PublishProfiles/(ProfileName).pubxml file as an "AfterBuild" target works. As in, it doesn't fire when you build the project, but does fire when you publish the project.
So instead of putting
<Target Name="BeforePublish">
<Error Condition="'foo'=='foo'" Text="test publish error" />
</Target>
in your .csproj file, try putting
<Target Name="AfterBuild">
<Error Condition="'foo'=='foo'" Text="test publish error" />
</Target>
in your .pubxml file instead, and it should fire just before publishing.
Jez
Long-time coder, with some interest in French and Philosophy. I sometimes hang out in the English Language & Usage chatroom. Check out my Firefox addons! :-)
Updated on July 18, 2022Comments
-
Jez almost 2 years
I must be missing something obvious here, but I have this at the end of my ASP.NET MVC web project's
.csproj
file:[...] <Target Name="BeforePublish"> <Error Condition="'foo'=='foo'" Text="test publish error" /> </Target> </Project>
As far as I can tell, that should always cause the publish to fail with an error. Yet, if I load the project, right-click on it, and click "Publish", the thing publishes without a hitch. What am I missing?
-
Jez over 11 yearsFirstly, Intellisense tells me that it's
BeforeTargets
and notBeforeTarget
as theTarget
attribute. Secondly, adding that didn't work unfortunately. It still publishes OK. :-( -
Alexey Shcherbak over 11 yearsRight, looks like I accidentialy erased one letter =(. Attribute name should be BeforeTargets. I just tested it and my console have publish error "test publish error". habrastorage.org/storage2/5a1/d4e/553/… Fixed in main answer
-
Jez over 11 yearsYes, but even when I use the
BeforeTargets
attribute, it doesn't change anything. It still publishes. -
Alexey Shcherbak over 11 yearsUse <OnError ExecuteTargets="SomeEmptyTarget"/> This hint took from this question (you can upvote it for usefullness either ;) ): stackoverflow.com/questions/3297194/…
-
Alexey Shcherbak over 11 yearsDid I answered your question?
-
Jez over 11 yearsI tried this code at the end of my project file: pastebin.com/7WR4fp6m - it didn't stop the publish from occurring. Does that look right to you?
-
Alexey Shcherbak over 11 yearsCode look right. But to try this - I need msdeploy env. I tested all samples with fake msdeploy so I can't tell for sure - whether it stopped or not, my deploy failed on error fallback or on msdeploy service connection error. Can you switch your msbuild output to diag, repeat your steps and post log somewhere?
-
Jez over 11 yearsHere is the output of the publish when msbuild verbosity is set to diagnostic: game-point.net/misc/output.txt
-
Jez over 11 yearsIt looks like the targets I added to the .csproj -
StopBuild
andPreventDebugPublish
- are not even mentioned. :-( -
Alexey Shcherbak over 11 yearsDid you added mentioned targets to Bacp.Assess.Web.csproj, right? What command line was used to produce that output.txt log? It doesn't looks like usually "diag" output looks like. Could you post anywhere how did you added targets to csproj and what technic did you used to hook your targets? Gist maybe ? or if there any private stuff - mail me at centur (at) gmail
-
Jez over 11 yearsI just used Visual Studio's Publish functionality to generate that output.
-
Alexey Shcherbak over 11 yearsPlease do the following: Go Tools->Options->Projects and Solutions-Build and Run and switch two lowest options "MSbuild .... verbosity" to Diagnostic. After that generate build log again and upload it.
-
Jez over 11 yearsThat's exactly what I did to get the above output.
-
Alexey Shcherbak over 11 yearsUhh, sorry again - didn't copied properly your output - lost about a half log =(. It's friday... Will add some comments soon
-
Alexey Shcherbak over 11 years
-
Jez over 11 yearsThanks for all your help on this - your suggested changes to the
.csproj
file do EXACTLY what I wanted. :-) Extremely useful, this answer deserves +100. :-) -
Peter Mortensen about 9 yearsThe first part of the first sentence is not comprehensible. Can you fix it?
-
wot-stefan over 8 yearsUp-voted because this answer shows a technique to encapsulate the event, which is specific to a particular publish profile, with the event definition (instead of shoving it into the main project file).