How to get code coverage report in donetcore 2 application - code-coverage

I am new to dotnet core 2.0, not sure how to extract code coverage report with
dotnet test command.
Looking for help from the community.

Here is my .bat file to generate dotnet core.
This will "pop" an index.htm file.
This is just a .bat file version of what I found here:
https://gunnarpeipman.com/aspnet-core-code-coverage/
Note, I have a few "3.1" references. It should not make a difference with 2.x. Just tweak the below to your version/world.
Thankfully, the dotnet-core version was MUCH easier than the dotnet-framework version (my answer for dotnet framework here : MSTest Code Coverage )
The below shows how to get results from 1 OR MORE UnitTests.csproj and have the html-report be a "merge" off all of them. (as described here : Is there anyway to merge cobertura coverage xml reports together? )
REM Make a .bat file like ZzzCoverageReports.bat and put the below in it. Save the .bat file to the same directory as your .sln file
REM DEVELOPERS, going forward, if you have an alternate path, please put in a (new) IF-EXIST check instead of hard coding to a single specific path
IF EXIST "C:\Program Files\dotnet\shared\Microsoft.NETCore.App\1.0.16\dotnet.exe" set __dotNetExe=C:\Program Files\dotnet\shared\Microsoft.NETCore.App\1.0.16\dotnet.exe
IF EXIST "C:\Program Files\dotnet\shared\Microsoft.NETCore.App\1.1.13\dotnet.exe" set __dotNetExe=C:\Program Files\dotnet\shared\Microsoft.NETCore.App\1.1.13\dotnet.exe
IF EXIST "C:\Program Files (x86)\dotnet\dotnet.exe" set __dotNetExe=C:\Program Files (x86)\dotnet\dotnet.exe
IF EXIST "C:\Program Files\dotnet\dotnet.exe" set __dotNetExe=C:\Program Files\dotnet\dotnet.exe
set __fullDirectory=%~dp0%__subdirectory%
REM Start __datetimeStampString
for /f "tokens=2-4 delims=/ " %%g in ('date /t') do (
set mm=%%h
set dd=%%g
set yy=%%i
)
set __mydate=%yy%%mm%%dd%
for /f "tokens=1-2 delims=: " %%j in ('time /t') do (
set hh=%%j
set mn=%%k
)
set __mytime=%hh%%mn%
set __datetimeStampString=%__mydate%___%__mytime%
REM End __datetimeStampString
set __outputFilesDirectory=%__fullDirectory%ZzzTempOutputFiles\
set __testResults=%__outputFilesDirectory%TestResults_%__datetimeStampString%\
set __coverageResults=%__outputFilesDirectory%CoverageReports_%__datetimeStampString%
set __toolsPath=%__outputFilesDirectory%ToolsPath
RD %__testResults% /S /Q
RD %__coverageResults% /S /Q
RD %__outputFilesDirectory% /S /Q
MD %__outputFilesDirectory%
MD %__testResults%
MD %__coverageResults%
REM install reportgenerator locally
call "%__dotNetExe%" tool install dotnet-reportgenerator-globaltool --tool-path "%__toolsPath%"
set __slnShortName=My.Solution.sln
set __slnFullName=%__fullDirectory%%__slnShortName%
set __unitTestCsProjFullName001=%__fullDirectory%..\ProjectOne.UnitTests.csproj
set __unitTestCsProjFullName002=%__fullDirectory%..\ProjectTwo.UnitTests.csproj
set __unitTestCsProjFullName003=%__fullDirectory%..\ProjectThree.UnitTests.csproj
set __trxLogShortFileName001=My.ProjectOne.UnitTests.trx
REM sometimes if a project targets different frameworks, you get something like MyCoverletOutputXYZ.netcoreapp3.1.xml just be aware.
set __coverageInputParameterShortFileName001=MyCoverletOutput001.xml
set __trxLogShortFileName002=My.ProjectTwo.UnitTests.trx
REM sometimes if a project targets different frameworks, you get something like MyCoverletOutputXYZ.netcoreapp3.1.xml just be aware.
set __coverageInputParameterShortFileName002=MyCoverletOutput002.xml
set __trxLogShortFileName003=My.ProjectThree.UnitTests.trx
REM sometimes if a project targets different frameworks, you get something like MyCoverletOutputXYZ.netcoreapp3.1.xml just be aware.
set __coverageInputParameterShortFileName003=MyCoverletOutput003.xml
REM now define a wild card for all the coverage cobertura xml files
set __coverageGeneratedShortNameWildCard=MyCoverletOutput*.xml
call "%__dotNetExe%" restore "%__slnFullName%"
REM Below does not seem necessary. which makes this process much faster.
REM call "%__dotNetExe%" build "%__slnFullName%" /p:Configuration=Debug /flp:v=diag;logfile="%__outputFilesDirectory%%__slnShortName%_Manual_DotNetExe_Build_31_DebugVersion_LOG.log" --framework netcoreapp3.1
REM Note, (PackageReference "coverlet.msbuild") AND (PackageReference "coverlet.collector") must be in unit test project for COVERAGE to work. The tell-tale is you get a trx file but none of the "MyCoverletOutput.xml" files.
REM find the relative path to your UNIT TEST csproj. Also note the CoverletOutput may generate a slightly different filename if you have multiple targets for your build. Aka, MyCoverletOutput.xml gets written as MyCoverletOutput.netcoreapp3.1.xml on my 3.1 targeted project
call "%__dotNetExe%" test "%__unitTestCsProjFullName001%" --logger:trx;LogFileName="%__testResults%%__trxLogShortFileName001%" /p:CollectCoverage=true /p:CoverletOutput="%__testResults%%__coverageInputParameterShortFileName001%" /p:CoverletOutputFormat=cobertura
call "%__dotNetExe%" test "%__unitTestCsProjFullName002%" --logger:trx;LogFileName="%__testResults%%__trxLogShortFileName002%" /p:CollectCoverage=true /p:CoverletOutput="%__testResults%%__coverageInputParameterShortFileName002%" /p:CoverletOutputFormat=cobertura
call "%__dotNetExe%" test "%__unitTestCsProjFullName003%" --logger:trx;LogFileName="%__testResults%%__trxLogShortFileName003%" /p:CollectCoverage=true /p:CoverletOutput="%__testResults%%__coverageInputParameterShortFileName003%" /p:CoverletOutputFormat=cobertura
REM the string-length of the FULL file name of file
REM "(blah blah blah)\src\Solutions\ZzzTempOutputFiles\ToolsPath\.store\dotnet-reportgenerator-globaltool\4.5.4\dotnet-reportgenerator-globaltool\?.?.?\tools\netcoreapp?.?\any\ReportGenerator.runtimeconfig.dev.json"
REM cannot be longer than 254 (on a windows machine), or you will get "Invalid runtimeconfig.json" errors.
REM note below, we're using a wild card
call "%__toolsPath%\reportgenerator.exe" "-reports:%__testResults%%__coverageGeneratedShortNameWildCard%" -targetdir:"%__coverageResults%" -reporttypes:HTML;HTMLSummary
start "" "%__coverageResults%\index.htm"
set __dotNetExe
set __fullDirectory=
set mm=
set dd=
set yy=
set __mydate
set hh=
set mn=
set __mytime=
set __datetimeStampString=
set __outputFilesDirectory=
set __testResults=
set __coverageResults=
set __toolsPath=
set __slnShortName=
set __slnFullName=
IMPORTANT NOTE:
Your UnitTest csproj file should have/will need these references:
<PackageReference Include="coverlet.msbuild" Version="x.y.z">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="a.b.c">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Mine were (at the time of writing this answer)
"coverlet.msbuild" Version="2.8.0">
"coverlet.collector" Version="1.2.1"
other useful links:
https://github.com/danielpalme/ReportGenerator
Mid Year 2021 Update/Append (for linux)
Here is a linux .sh version of the above.
Much cleaner.
Obviously, my filename is "zzzCoverage.sh"
I put the file "next" to my .sln file.
Watch the "..", I'll explain my folder structure after the .sh contents.
#!/bin/bash
# don't forget to allow execution of this file
# chmod +x zzzCoverage.sh
# READ THIS --> this has a prerequisite step of installing the below global dotnet tool
# dotnet tool install -g dotnet-reportgenerator-globaltool
# delete any existing coverage.cobertura.xml files (to ensure clean coverage report)
# note, if you target more than 1 framework in your UnitTests.csprojs, you may get multiple file names like "coverage.net5.0.cobertura.xml" AND "coverage.netstandard2.1.cobertura.xml". In this case, you will have to adjust the "wildcard" value for finding one set of the cobertura.xml files
find .. -type f -name 'coverage.cobertura.xml' -delete
# run dotnet test. Note the arguments. And see the PackageReferences below
dotnet test --logger "trx;logfilename=TestResults123.trx" --results-directory ./BuildReports/UnitTests /p:CollectCoverage=true /p:CoverletOutput=BuildReports/Coverage/ /p:CoverletOutputFormat=cobertura
# generate the report with the global dotnet tool. note the prerequisite above
# note the -reports argument. You may have to play with it a little. note the "coverage.net5.0.cobertura.xml coverage.netstandard2.1.cobertura.xml" issue earlier in this file as well
reportgenerator -reports:../**/coverage.cobertura.xml -targetdir:BuildReports/Coverage -reporttypes:"HTML;HTMLSummary"
#open html root index.html file in a Chrome
open -a "Google Chrome" ./BuildReports/Coverage/index.html
# change the above to open ./BuildReports/Coverage/index.html (aka remove the -a "Google Chrome") if you do not have Chrome installed.
# Here is the list of packages (at the time of writing) in EACH UnitTest.csproj. Note There are TWO <ItemGroup>'s.
# <ItemGroup>
# <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
# <PackageReference Include="MSTest.TestAdapter" Version="2.2.5" />
# <PackageReference Include="MSTest.TestFramework" Version="2.2.5" />
# <PackageReference Include="coverlet.collector" Version="3.1.0">
# <PrivateAssets>all</PrivateAssets>
# <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
# </PackageReference>
# </ItemGroup>
# I believe the below PackageReferences are the ones that allow the 'reportgenerator' to function correctly. "coverlet.collector" (from above) may also be a requirement for 'reportgenerator'
# <ItemGroup>
# <PackageReference Include="coverlet.msbuild" Version="3.1.0">
# <PrivateAssets>all</PrivateAssets>
# <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
# </PackageReference>
# <PackageReference Include="Microsoft.CodeCoverage" Version="16.10.0" />
# <PackageReference Include="ReportGenerator" Version="4.8.12" />
# </ItemGroup>
Ok, my folder setup.
./src/
./src/MyCsprojProjectOne/mycsprojone.csproj
./src/MyCsprojProjectTwo/mycsprojtwo.csproj
./src/UnitTestsHolder/UnitTestLibOne/myunittestone.csproj
./src/UnitTestsHolder/UnitTestLibTwo/myunittesttwo.csproj
./src/Solutions/MySolution.sln
Key Words
DotNet Core Coverage Reports Linux Multiple CsProj CsProjs cs projects AspNet Core AspNetCore
Links:
Is there anyway to merge cobertura coverage xml reports together?
https://gunnarpeipman.com/aspnet-core-code-coverage/

Related

Dotnet command failed with non-zero exit code on the following projects :

I am trying to build on TFS an API which contains several projects in dotnet.core3.1
When I run the dotnet build Api.sln command on TFS here is the result
[error] Dotnet command failed with non-zero exit code on the following projects: D: \ TFS \ Agent01 \ _work \ 18 \ s \ PMTApi.sln
Context:
TFS 2017
Dotnet Core 3.1 projects
Source code management on TFS git
Agents unable to connect to the outside (internet)
The dependency imports are done from a private server thanks to the NuGet.Config file set up to redirect the dependency imports not on the https://api.nuget.org/v3/index.json server but well on that of our private company.
Explanation:
On the TFS git source manager, the PMTApi.sln project contains 7 .csproj files
But in the PMTApi.sln file there are only 4 .csproj which are included
Local result:
Is good dotnet build completes the 5 successful projects.
Result on TFS:
Not good.
[error] Dotnet command failed with non-zero exit code on the following projects: D: \ TFS \ Agent01 \ _work \ 18 \ s \ PMTApi.sln
Observation:
I have the impression that TFS is based on all the .csproj files from the TFS git source manager and not from PMTApi.sln
Conclusion the projects which are not referenced in PMTApi.sln still try to restore the dependencies which causes the top error.
Test:
I tried to exclude these 3 .csproj projects in my NuGet.Config file like this, but it doesn't work
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="XXX NuGet Packages" value="http://privateServer/nuget" />
</packageSources>
<ItemGroup>
<ProjectReference Exclude="..\folderName1\projectName1.csproj" />
<ProjectReference Exclude="..\folderName2\projectName2.csproj" />
<ProjectReference Exclude="..\folderName3\projectName3.csproj" />
</ItemGroup>
</configuration>
Do you have any ideas for a successful dotnet build task?
The construction:
TFS Return:
Local configuration:
1- here you have to right click on the solution
2- manage package Nuget for solution
3- add the url of your private server
4- and uncheck the server of api.NuGet.org
for my case I also uncheck the visual studio packages
You should specify the csproj file(s) to use in the Project(s) argument, not sln file. Check the description of this argument:
The path to the csproj file(s) to use. You can use wildcards (e.g.
**/*.csproj for all .csproj files in all subfolders). This field follows glob pattern, and is run against root of the repository at all
times irrespective of Working Directory.
https://learn.microsoft.com/en-us/azure/devops/pipelines/tasks/build/dotnet-core-cli?view=azure-devops

Where does Ionide + Fake put the output executable?

I am trying to use .NET Core + Ionide + VS Code + Fake + Paket on macOS High Sierra.
Using the project generator, I have created a Suave application called Test. Ionide seems to have generated the appropriate files. After tweaking the TargetFramework to .NET Core, I can build successfully:
$ ./build.sh
...
Build succeeded.
0 Warning(s)
0 Error(s)
Time Elapsed 00:00:03.72
Finished Target: Build
---------------------------------------------------------------------
Build Time Report
---------------------------------------------------------------------
Target Duration
------ --------
Clean 00:00:00.0026904
InstallDotNetCLI 00:00:01.2292511
Restore 00:00:04.2731055
Build 00:00:07.1234434
Total: 00:00:12.7035334
---------------------------------------------------------------------
Status: Ok
---------------------------------------------------------------------
There are now some files in Test/bin, but none of them are .exe, which is what I would expect as output from fsharpc.
Where does Ionide + Fake put the output executable?
My project has OutputType executable:
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Compile Include="Test.fs" />
</ItemGroup>
<Import Project="..\.paket\Paket.Restore.targets" />
</Project>
.Net Core compiles all projects (even executable applications) to .dll not .exe that can be run with dotnet PATH_TO_DLL. In the bin folder, in the subfolder for given framework target there should be file YOUR_PROJECT_NAME.dll that can be run with dotnet CLI.
To generate an exe you need to supply a run-time identifier. You can include this in the fsproj with
<PropertyGroup>
<RuntimeIdentifiers>win10-x64</RuntimeIdentifiers>
</PropertyGroup>
but you don't need to do that. You can use VSCode's tasks.json file to set up a build task.
My suggestion is just to run the dotnet CLI:
dotnet build -c Release -r win10-x64
This will create an exe in the bin\release\netcoreapp2.0\win10-x64 folder. You can also dotnet publish if you want a self-contained directory to deploy (this can get large). The id for OSX will be something like osx-x64.
By default ionide generates an fsproj file that is targetting net461, and you might also need Fake 5 for dotnetcore. I also suggest you use paket in magic-mode, and commit the .exe to github (if you use git).

Can't access ArtifactStagingDirectory variable in MSBuild

During my build process I'm trying to copy a folder to the artifacts folder (\myserver\d$\TFBuild-Agent01\66\a).
So I put this in the .csproj file:
<Target Name="BeforeBuild">
<Exec
Command="xcopy.exe Databases "$(Build.ArtifactStagingDirectory)\Databases" /i /e /y /d" />
</Target>
This gets me
Error MSB4184: The expression """.ArtifactStagingDirectory" cannot be evaluated. Method 'System.String.ArtifactStagingDirectory' not found*
Everything I can find online says that $(Build.ArtifactStagingDirectory) is the way to do it. But it doesn't work.
Building with Visual Studio 2015 on TFS 2015
This doesn't work either:
<Exec
Command="xcopy.exe Databases "$($Env:BUILD_ARTIFACTSTAGINGDIRECTORY)\Databases" /i /e /y /d" />
The expression "$Env:BUILD_ARTIFACTSTAGINGDIRECTORY" cannot be evaluated.*
This doesn't error, but it looks like %BUILD_ARTIFACTSTAGINGDIRECTORY% gets replaced as an empty string:
<Exec Command="xcopy.exe Databases "%BUILD_ARTIFACTSTAGINGDIRECTORY%\Databases" /i /e /y /d" />
You have been mixing ways to access the build variables that the agent allows you. The syntax using $(some.variable) is interpreted by the agent itself. MSBuild has a similar looking syntax - $(PropertyName) - which does something different - it gives access to msbuild properties and does not allow for dots (.) in it's name, since you can use the dot to call functions on the value (e.g. $(OutputPath.Substring(3))).
When you want to reference build variables from MSBuild, you need to reference the environment variable that the agent sets. This is possible because MSBuild makes all environment variables accessible as global properties using its property syntax. The environment variable for Build.ArtifactStagingDirectory is BUILD_ARTIFACTSTAGINGDIRECTORY so you can use it in MSBuild using $(BUILD_ARTIFACTSTAGINGDIRECTORY).
I have been using it successfully in this script to default a property when run as part of a TFS/VSTS build (PublishBaseDir is a custom property used later):
<PropertyGroup>
<!-- Default artifact staging directory when built via VSTS / TFS agent -->
<PublishBaseDir Condition="'$(PublishBaseDir)' == '' and '$(BUILD_ARTIFACTSTAGINGDIRECTORY)' != '' ">$(BUILD_ARTIFACTSTAGINGDIRECTORY)</PublishBaseDir>
<!-- If not built on a known agent, use a "publish" subdir next to this file -->
<PublishBaseDir Condition="'$(PublishBaseDir)' == ''">$(MSBuildThisFileDirectory)publish\</PublishBaseDir>
<!-- Normalize directory if set manually or through ENV var -->
<PublishBaseDir Condition="!HasTrailingSlash('$(PublishBaseDir)')">$(PublishBaseDir)\</PublishBaseDir>
</PropertyGroup>
OK, I guess that because I'm using Visual Studio to build my solution, I can't access $(Build.StagingDirectory) from the .csproj. However, it's being passed on the command line to the "Visual Studio Build" build step as a property:
/p:OutDir="$(Build.StagingDirectory)"
So that can be accessed by doing
<Exec Command="xcopy.exe Databases "$(OutDir)\Databases" /i /e /y /d" />

Adding files to the bin directory at Build and Publish

I have two license files that I would like to include in my \bin directory both when I build and publish.
Both files are in the App_Data directory (their initial location doesn't matter, they just need to end up in the \bin) and have the following properties set:
Build Action = Content
Copy to Output Directory = Copy Always
They are in not the \bin when I build or publish.
What is wrong with my setup: the settings, the folders, the files, something else...?
UPDATE
I moved the files out of the App_Data directory and placed them in the project root and now they are copied to the \bin on build.
I've done this in a few projects by expanding my .csproject file slightly. The following code should be put directly beneath the Project node in your WebProject.csproj.
The AfterBuild target simply copies a set of files ("unreferenced DLLs" in this case) to the bin-folder when building normally from Visual Studio. The CustomCollectFiles basically do the same thing when deploying.
<PropertyGroup>
<UnreferencedDlls>..\lib\Unreferenced\**\*.dll</UnreferencedDlls>
</PropertyGroup>
<PropertyGroup>
<CopyAllFilesToSingleFolderForPackageDependsOn>
CustomCollectFiles;
$(CopyAllFilesToSingleFolderForPackageDependsOn);
</CopyAllFilesToSingleFolderForPackageDependsOn>
</PropertyGroup>
<Target Name="AfterBuild">
<Message Text="Copying unreferenced DLLs to bin" Importance="High" />
<CreateItem Include="$(UnreferencedDlls)">
<Output TaskParameter="Include" ItemName="_UnReferencedDLLs" />
</CreateItem>
<Copy SourceFiles="#(_UnReferencedDLLs)" DestinationFolder="bin\%(RecursiveDir)" SkipUnchangedFiles="true" />
</Target>
<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>
The part you need to modify is basically the UnreferencedDlls node to match your folder structure. The **\*.dll part simply means "every DLL file at any level beneath here".
If you're using Visual Studio:
Show your file properties (Click on your file or Right-click on it then choose Properties)
At the Copy to Output Directory property choose Copy always or Copy if newer.
At build time, the file is going to be copied at the bin directory: Debug or Release...
not necessarily a direct answer, but I highly suggest not using the baked in "publish" mechanism, but rather wire up a build script (probably in powershell) that will do everything you need. It's really easy to hook into MSBuild as well as nUnit and also copy files and move them around.
POWERSHELL (rough) example.
# Get Directory Location
$invocation = (Get-Variable MyInvocation).Value
$directorypath = Split-Path $invocation.MyCommand.Path
# Build the application using MSBuild
cmd /c C:\Windows\Microsoft.NET\Framework\$v4_net_version\msbuild.exe "$directorypath\MyProject.sln" /p:Configuration=Release
# Run the tests using nUnit
cmd /c $directorypath\build\nunit\nunit-console.exe $solutionPath\MyProject.Tests\bin\debug\MyProject.Tests.dll
# Copy the license to the appropriate directory
Copy-Item -LiteralPath "$directorypath\mylicensefile.txt" "$directorypath\bin\release" -Force
# NOTE: You are going to have to adjust this to match your solution and projects.
In this post on Microsoft Connect the answer is much simpler:
Referenced assemblies in Unit Test are not copied in TestResults/Out
So what I did was the following:
[TestClass]
[DeploymentItem("Some.dll")]
public class SomeTests
{
...
}
It works fine for me.
Hope it help.

Use MSDeploy to copy buildresult to targetserver

There is a webproject with a batchfile that generates all files needed on the targetserver and puts them in a folder "/Deployable" .
The batch file is quite involved because the project contains a pluginsystem and all plugins need to be copied to a certain location.
When I use webdeploy to deploy to the targetserver it happens what you expect: there are some of the needed assemblies copied over, but not the files as specified in the batchfile.
My plan is now to first execute the batchfile and then use webdeploy to copy the folder "/Deployable" to the targetserver. Can this be done with webdeploy?
This is what I see in Visual Studio deploy menue:
This is the resulting publish profile
<?xml version="1.0" encoding="utf-8"?> <publishData>
<publishProfile publishUrl="http://myserver/msdeployagentservice"
deleteExistingFiles="False"
ftpAnonymousLogin="False"
ftpPassiveMode="True"
msdeploySite="mysite/"
msdeploySiteID=""
msdeployRemoteSitePhysicalPath=""
msdeployAllowUntrustedCertificate="False"
msdeploySkipExtraFilesOnServer="False"
msdeployMarkAsApp="False"
profileName="publish_to_myserver"
publishMethod="MSDeploy"
replaceMatchingFiles="True"
userName="myuser"
savePWD="True" userPWD="xxx" SelectedForPublish="True" />
</publishData>>
I think there is an ability to add third-party files into webdeploy package by modifying .csproj file, however, I have never had to use it.
Alternatively, you can easily achieve the same result by using MSDeploy's command-line client and its sync verb, by specifying your /Deployable folder as the -source argument and your target server's msdeploy service as the -dest, e.g.:
$(WebDeployToolPath)\msdeploy -verb:sync -source:dirPath='Deployable\' -dest:dirPath='$(DeployDirectoryLocalPath)',computerName=$(DeployTargetURL),userName='$(DeployUserName)',password='$(Password)',authType='Basic' -verbose -allowUntrusted
Substitutions:
$(WebDeployToolPath) - full path to folder with msdeploy executable (e.g. c:\Program Files (x86)\IIS\Microsoft Web Deploy V3\msdeploy.exe )
Deployable\ - full path to the folder you wnat to copy to the target server.
$(DeployDirectoryLocalPath) - full local path to the target folder on the target server.
$(DeployTargetURL) - web deploy service URL (e.g. https://192.168.142.55:8172/MsDeploy.axd or http://myserver/msdeployagentservice)
$(DeployUserName) - username to be used for deployment (should be admin for Win 2003)
$(Password) - user's password.
That's it - this command will synchronize Deployable\ folder with $(DeployDirectoryLocalPath) folder (i.e. make the content exactly match).
You can wrap it into an msbuild target in your .csproj file:
<PropertyGroup>
<DeployTargetURL Condition="'$(DeployTargetURL)'==''">https://192.168.142.55:8172/MsDeploy.axd</DeployTargetURL>
<DeployUserName Condition="'$(DeployUserName)'==''">tergetServer\Administrator</DeployUserName>
<Password Condition="'$(Password)'==''">AdminPassword</Password>
<WebDeployToolPath Condition="'$(WebDeployToolPath)'==''">c:\Program Files (x86)\IIS\Microsoft Web Deploy V3\msdeploy.exe</WebDeployToolPath>
<Target Name="Deploy">
<Exec Command=""$(WebDeployToolPath)"\msdeploy -verb:sync -source:dirPath='Deployable\' -dest:dirPath='$(DeployDirectoryLocalPath)',computerName=$(DeployTargetURL),userName='$(DeployUserName)',password='$(Password)',authType='Basic' -verbose -allowUntrusted " />
</Target>
And than run it from the command-line in the following way:
%windir%\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe MyProject.proj /t:Deploy*

Resources