Does the 'MSDeployUseChecksum' option work with the FileSystem Publish method - azure-devops

I am trying to implement a deployment step using MSBuild with options DeployOnBuild and a PublishProfile with the WebPublishMethod set to FileSystem.
My PublishProfile looks like this:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<WebPublishMethod>FileSystem</WebPublishMethod>
<LaunchSiteAfterPublish>False</LaunchSiteAfterPublish>
<ExcludeApp_Data>False</ExcludeApp_Data>
<DeleteExistingFiles>False</DeleteExistingFiles>
<MSDeployUseChecksum>true</MSDeployUseChecksum>
</PropertyGroup>
</Project>
I am calling this from a build script that invokes MSBuild on individual projects and sets the PublishUrl depending on the build target (developer or deployment):
>msbuild.exe /p:DeployOnBuild=true /p:PublishProfile=c:\path\to\Profile.pubxml /p:SolutionDir=c:\path\to\solution /p:PublishUrl=c:\path\to\target /p:VisualStudioVersion=14.0 c:\path\to\project.csproj
The build process is intended to overlay project resources, including DLLs, over a third-party application. Due to behavior of NuGet restore, later versions of DLLs do not consistently have later change dates, so are being skipped during the deployment process. I have added the MSDeployUseChecksum option, which per documentation (here and here) should resolve the issue, but is not having an impact.
Does MSDeployUseChecksum work with the FileSystem deployment method? If yes, are any additional steps or settings required to make it work?

Related

Output Directory of native dll bundled with NuGet

I am trying to build a NuGet package that includes native DLLs which are to be placed in the output folder when a project uses the package. I have tried to use the several suggestions from this question, but I am always running in the same problem.
My current NuGet package layout is like this:
\build
packageId.targets
file1.dll
file2.dll
\lib
\netstandard1.4
assembly.dll
The contents of packageId.targets is:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<NativeLibs Include="$(MSBuildThisFileDirectory)\*.dll"/>
<None Include="#(NativeLibs)" Link="$(RecursiveDir)$(Filename)$(Extension)">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
This should, according to the answers of the other questions, lead to my DLLs being placed in the bin\Debug directory of the project using the package. However, they are not. Instead, they are placed in bin\Debug\packages\packageId\build.
Now I have experimented a lot, and I noticed more and more strange behavior which I cannot make any sense of:
If I move the DLLs to the root of the NuGet package (like one answer suggests) and change the .targets file accordingly, they are not copied at all. There also is no error message.
If I change the .targets file to only reference file1.dll in both Include= and Link=, both files get copied anyway.
So I wondered if some policy just ignores the .targets file and copies whatever is in build to that path in the output folder, but when I remove the .targets file, the DLL files will not get copied anymore.
Now I understand even less what's happening.
What do I need to change to get the DLLs copied right into bin\Debug?
The new way to handle runtime-specific assents in NuGet is to use the runtimes folder to place native assets:
\lib
\netstandard2.0
ManagedWrapper.dll
\runtimes
\win-x86
\native
NativeThing.dll
\win-x64
\native
NativeThing.dll
\linux-x64
\native
libNativeThing.so
\osx-x64
\native
libNativeThing.dylib
If the package is consumed from a .NET Framework project, you may need to add a reference to the Microsoft.NETCore.Platforms package wich provides the runtime graph (runtimes.json) for NuGet to provide proper RID mappings if you don't use base RIDs (e.g. win10-x64 falls back to win-x64 resources).

Nuget packages bundled in teamcity not installing documentation files

So basically I am building nuget packages in TeamCity via a .proj file that runs a "pack" target:
<MSBuild
Projects="$(MSBuildProjectDirectory)\PROJNAME.csproj"
Targets="Rebuild;pack"
Properties="Configuration=$(Configuration);Version=$(BUILD_NUMBER)" />
With an artifact output of:
PROJNAME\bin\Release\PROJNAME.%build.number%.nupkg
This works nicely for basic consuming of the nuget package, however I am having trouble getting the documentation xml files to work.
I have looked inside the output nupkg and I see that the documentation xml is actually bundled and included in the package, however the problem is that when I finally restore nuget packages in my consuming project, the dll gets copied across as expected, however the documentation does not.
I wondered if this is because of the TC generated .nuspec file, and if I may need to abandon teamcities nuspec and create my own, however I was hoping to avoid this, given it works nicely the way it is, and handles versioning etc.
Is there a simple way to include documentation xml when the package is restored?
In the end i found it came down to 3 things, being:
Ensure projects configuration is set to generate documentation.
Either by adding code manually such as:
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"><DocumentationFile>bin\$(Configuration)\netstandard2.0\Project.documentation.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> <DocumentationFile>bin\$(Configuration)\netstandard2.0\Project.documentation.xml</DocumentationFile>
</PropertyGroup>
Or alternatively via the Visual Studio Project properties menu, if you are doing it through VS also make sure you do it for all configurations (as depicted as A in the picture below):
Add EnableDocumentationFile to your .csproj file, eg:
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace>project</RootNamespace>
<Configurations>Debug;Release</Configurations>
<EnableDocumentationFile>true</EnableDocumentationFile>
</PropertyGroup>
and most importantly let your project know (again in your .csproj) that it should be copying over the documentation file, and use PackageFlatten if you want it to appear at the same level as your package dll:
<ItemGroup>
<None Remove="bin\$(Configuration)\netstandard2.0\Project.documentation.xml" />
</ItemGroup>
<ItemGroup>
<Content Include="bin\$(Configuration)\netstandard2.0\Project.documentation.xml">
<Pack>true</Pack>
<PackageCopyToOutput>true</PackageCopyToOutput>
<PackageFlatten>true</PackageFlatten>
</Content>
</ItemGroup>

How do you upgrade the *.props included in a NuGet package?

I'm working on an internal NuGet package that adds a pre-build event.
It does this by specifying a build folder containing a MyPackage.props file per the documentation.
Here is the contents of the props file:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<PreBuildEvent>Some commands go here</PreBuildEvent>
</PropertyGroup>
</Project>
If the project has never had a pre-build event, the event is created. However, if there is an existing PerBuildEvent element in the csproj file, the new value does not get populated. I can get it to work if I open up the csproj file and manually delete the appropriate PropertyGroupElement:
<PropertyGroup>
<PreBuildEvent>Some command line stuff</PreBuildEvent>
</PropertyGroup>
However, I have to delete if from there, as just deleting the contents of the pre-build event in the UI does not allow the new value to be written.
I want to use the convention based method over doing this in install.ps1 because the documentation specifies:
[NuGet 3.x] This script will not be executed in projects managed by project.json
...and (I left this part out before) is there a way to do this when there is no csproj file?
What's going on?
I would suggest you do not use a prebuild event in your .props since I would expect that you do not want to overwrite an existing one in the project.
Instead you could look at using another target so that your logic runs before the build by using the BeforeTargets:
<Target Name="MyBeforeBuild" BeforeTargets="Build">
<Message Text="### MyBeforeBuild ###" Importance="high" />
</Target>
You may also want to look at using the DependsOnTargets attribute if you need your pre-build event to run after some other target.
<Target Name="MyTarget" DependsOnTargets="$(CoreCompileDependsOn)">
</Target>

MSBuild project deploy to local folder with config transformed

I'm having trouble trying to find the right way to use MSBuild to build a web project and output the project with only deployable files (i.e. no .cs, .csproj, .Debug.config etc.), but published to a local folder that I can then FTP, RoboCopy, (or whatever) to a secondary location.
The published output must have the Web.config file pre-transformed as per the specified configuration and the transformation config files (e.g. Web.Debug.config) not included in the output. I don't need any fancy publishing to IIS, database deployment or anything like that, I just want clean file system output that I can then test. Note that this cannot be done using visual tools as I want to run it as part of an automated build process.
I can generate a web deployment package, but I can't get WebDeploy to work because it doesn't seem to handle quoted command line options anymore (seems to be some kind of bug) and the directory structure has spaces, so I was hoping to accomplish the whole task using MSBuild, seeing as MSBuild seems to have native capacity to transform the config file (TransformXml), which is the only real bit of proper deployment functionality I'd be using.
Got it figured out eventually. The following build script does the trick:
<?xml version="1.0"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.Tasks.dll"/>
<PropertyGroup>
<OutputDir>obj\website-output</OutputDir>
</PropertyGroup>
<Target Name="PrepareDeploy">
<ItemGroup>
<DeployableFiles Include="App_Code/**/*.*;App_Data/**/*.*;Areas/**/Views/**/*.*;bin/**/*.*;Views/**/*.*;*.aspx;*.asax;*.html;*.htm;sitemap.xml;*.ico;*.png" Exclude="App_Data/**/*.log" />
</ItemGroup>
<RemoveDir ContinueOnError="true" Directories="$(OutputDir)" />
<MSBuild Projects="Website.csproj" />
<MakeDir ContinueOnError="true" Directories="$(OutputDir)" />
<Copy SourceFiles="#(DeployableFiles)" DestinationFiles="#(DeployableFiles->'$(OutputDir)\%(RelativeDir)%(Filename)%(Extension)')" />
<TransformXml Source="Web.config" Transform="Web.$(Configuration).config" Destination="$(OutputDir)\web.config" />
</Target>
</Project>

MSBuild: OutputPath directory is empty

I want to deploy my ASP.NET MVC site and have the following script.
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\MyProjName\MyProjName.csproj"/>
<PropertyGroup>
<NewInstallDir>C:\DeployFolder\</NewInstallDir>
<BinDir>$(NewInstallDir)bin\</BinDir>
</PropertyGroup>
<Target Name="Build">
<MSBuild Projects="..\MySlnName.sln"
Properties="Configuration=Release;Platform=Any CPU;OutputPath=$(BinDir)" />
<Copy SourceFiles="#(Content->'..\MyProjName\%(RelativeDir)%(FileName)%(Extension)')"
DestinationFiles="#(Content->'$(NewInstallDir)%(RelativeDir)%(FileName)%(Extension)')" />
<Copy SourceFiles="#(None->'..\MyProjName\%(RelativeDir)%(FileName)%(Extension)')"
DestinationFiles="#(None->'$(NewInstallDir)%(RelativeDir)%(FileName)%(Extension)')" />
<MakeDir Directories="#(Folder->'$(NewInstallDir)%(RelativeDir)')" />
</Target>
</Project>
Main idea.
I copied binary to C:\DeployFolder (take structure of folder from sources). I build my dll to C:\DeployFolder\Bin (I don't have this folder in sources folder so I need separately copy this).
I run my script - all works instead of copy DLLs to OutputPath. Same scripts works for other asp.net mvc project. I have no idea what is wrong in this case.
I complete this issue with workaround but I would like to know what is wrong with this script.
The first thing I'd try is to replace your use of the deprecated $(OutputPath) with $(OutDir). From when I've seen this error, 9 times out of 10 it is due to a mismatch between the Platform/Configuration requested and how a particular project is actually defined. Take care to keep track of the discrepency between "Any CPU" (with a space) preferred by solution files and "AnyCPU" actually used inside project files for $(Platform). Some project wizards only set up an "x86" Platform or otherwise omit "AnyCPU" which can cause the OutputPath to be empty.
Beyond that, the approach of importing a project file and then building a solution (presumbably that includes the very same project" is a bit off center. Consider making the deployment changes you desire within the project file itself, through an import. You can either wire into the existing build targets at the right place, or perhaps add an additional target.