Why does MSBuild ignore my BeforePublish target?

12,034

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.

Share:
12,034
Jez
Author by

Jez

Long-time coder, with some interest in French and Philosophy. I sometimes hang out in the English Language &amp; Usage chatroom. Check out my Firefox addons! :-)

Updated on July 18, 2022

Comments

  • Jez
    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
    Jez over 11 years
    Firstly, Intellisense tells me that it's BeforeTargets and not BeforeTarget as the Target attribute. Secondly, adding that didn't work unfortunately. It still publishes OK. :-(
  • Alexey Shcherbak
    Alexey Shcherbak over 11 years
    Right, 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
    Jez over 11 years
    Yes, but even when I use the BeforeTargets attribute, it doesn't change anything. It still publishes.
  • Alexey Shcherbak
    Alexey Shcherbak over 11 years
    Use <OnError ExecuteTargets="SomeEmptyTarget"/> This hint took from this question (you can upvote it for usefullness either ;) ): stackoverflow.com/questions/3297194/…
  • Alexey Shcherbak
    Alexey Shcherbak over 11 years
    Did I answered your question?
  • Jez
    Jez over 11 years
    I 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
    Alexey Shcherbak over 11 years
    Code 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
    Jez over 11 years
    Here is the output of the publish when msbuild verbosity is set to diagnostic: game-point.net/misc/output.txt
  • Jez
    Jez over 11 years
    It looks like the targets I added to the .csproj - StopBuild and PreventDebugPublish - are not even mentioned. :-(
  • Alexey Shcherbak
    Alexey Shcherbak over 11 years
    Did 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
    Jez over 11 years
    I just used Visual Studio's Publish functionality to generate that output.
  • Alexey Shcherbak
    Alexey Shcherbak over 11 years
    Please 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
    Jez over 11 years
    That's exactly what I did to get the above output.
  • Alexey Shcherbak
    Alexey Shcherbak over 11 years
    Uhh, sorry again - didn't copied properly your output - lost about a half log =(. It's friday... Will add some comments soon
  • Alexey Shcherbak
    Alexey Shcherbak over 11 years
  • Jez
    Jez over 11 years
    Thanks 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
    Peter Mortensen about 9 years
    The first part of the first sentence is not comprehensible. Can you fix it?
  • wot-stefan
    wot-stefan over 8 years
    Up-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).