Get Xml documentation of dependent package in Docker container - docker

We have an Asp.Net Core 2.0 web service, which we deploy in a Docker container.
For the web service we generate an xml documentation file and we use that file in Swagger-ui.
This works well for the xml documentation generated from the project itsself, but I can't get the xml comments that are generated for an included package visible when the web service is deployed in a Docker container.
The nuget package (also created by us) does contain an xml documentation file and we can make swagger use it when running the service on a local machine.
The documentation is made available to swagger by calling .IncludeXmlComments and the path to the documentation is determined by getting the path to the assembly and then replacing the .dll extension with .xml.
I suspect that the xml doc file of the package is not included in the container and can therefore not be found. In the Dockerfile I see the command
COPY publish .
I would either like a command that also adds/copies the package's xml doc file, or know how to make the xml doc file part of the publish assets.
Any other solution that makes this work in a robust way (not just on my machine) is welcome too.
EDIT:
we now have the extra xml doc file in the Docker container, but Swagger still doesn't show the descriptions that are provided in that file (which it does when running locally).
We used something similar to this:
<Target Name="PrepublishScript" BeforeTargets="PrepareForPublish">
<ItemGroup>
<DocFile Include="$(USERPROFILE)\.nuget\packages\{packagename}\**\lib\$(TargetFramework)\{PackageName}.xml" />
</ItemGroup>
<Copy SourceFiles="#(DocFile)" DestinationFolder="$(PublishDir)" SkipUnchangedFiles="false" />
</Target>
In which {PackageName} should be replaced by the actual name of your package. The wild card in the path keeps it version independent (although we must check if this now causes issues, as we have multiple versions).

Make sure you had checked XML Documentation checkbox for Release configuration under Project properties-> Build-> XML Documentation File. You might have already checked it for Debug configuration but not for Release Configuration. Select Release from dropdown under project properties and check XML Documentation file checkbox. Your docker RUN dotnet publish "" -c Release -o /app wouldn't produce Documentation XML file unless you check the checkbox for Release configuration.

I'll answer my own question, as a co-worker found out how to solve this.
You only need to change the nuget package, just follow these steps:
Create a "build" folder in your project
In that folder create a file with extension .targets (say projectname.targets) with content
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<None Include="$(MSBuildThisFileDirectory)projectname.xml">
<Link>projectname.xml</Link>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
Edit the csproj file, add this before the final </Project> tag
<ItemGroup>
<None Include="bin\$(Configuration)\$(TargetFramework)\projectname.xml" Pack="true" PackagePath="build" />
<None Include="build\projectname.targets" Pack="true" PackagePath="build" />
</ItemGroup>
<ItemGroup>
<Folder Include="build\" />
<Folder Include="Properties\" />
</ItemGroup>
Substitute "projectname with your name of choice, we used the name of our project.
These modifications will add the xml documentation file of the package in the root of (the) project(s) that use it with properties Build Action: None and Copy to Output Directory: Copy always.
After that we could see the xml comments from the package also if it was deployed in a Docker container, no modifications to the Docker file were necessary.

Generally, we will not expose API documentation in a production environment. The documentation is useful for the dev environment.
Hence i figured out a different work around for this.
I have run the build & publish commands with default configuration i.e., debug.
RUN dotnet build -o /app
RUN dotnet publish -o /app
I have deployed this in my dev environment for UI developer reference.
I hope this helps.

Related

How to properly configure NuGet Packages for collaboration with DevOps (TFS)

I have read and gone trough the following:
https://learn.microsoft.com/en-us/nuget/consume-packages/package-restore#enabling-and-disabling-package-restore
https://learn.microsoft.com/en-us/nuget/consume-packages/package-restore-troubleshooting
Why is there no packages folder in .my NET Core solution's containing folder?
'nuget' is not recognized but other nuget commands working
https://www.skylinetechnologies.com/Blog/Skyline-Blog/July_2016/Relocate_NuGet_Package_Restore_Folder
But i still have issues with the Folder Packages which contains the Packages for my solution, when i check in my code, for my on my side everything works fine, when someone else gets the checked in solution, he has to change the path that is set in the ProjectNameFile.csproj because the path there contains
../../../../NuGetPackages/....
but should be (and only works if changed to)
../Packages
But than when this persons checks in and i get this version, my packages folder is gone ... We have been using TFS on other projects here but this is a first for me ...
I tried all what the links i posted are suggestion but with no luck.
Tools > options > NuGet Package Manager looks like this:
I have a NuGet.Config file on the same level as my projectname.sln file which has following content:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<config>
<!-- I tried each line below seperately and together -->
<add key="globalPackagesFolder" value=".\packages" />
<add key="repositoryPath" value="C:\Development\projectname\Packages" />
</config>
</configuration>
So what i did now is i created a Packages folder on the projectname.sln level and when i build my solution, this works but hey, i can't do that each (X) time and i am sure there is a way of achieving this but don't know how.
Thank you in advance for any feedback.
How to properly configure NuGet Packages for collaboration with DevOps (TFS)
I suppose you are using the packages.config as nuget package management, because you said " he has to change the path that is set in the ProjectNameFile.csproj because the path there contains ../../../../NuGetPackages/....".
So, if you are using packages.config, you should use relative paths for the repositoryPath in your nuget.config file when you build the project with Azure DevOps.
As we know, when we build the project in the Azure DevOps, Azure DevOps always copy the project to the path like D:\a\1\s\xx, which is different with the path in your local. And NuGet always use the relative paths (..\packages or ../../../../NuGetPackages/...)in the ProjectNameFile.csproj like:
<Reference Include="packagename, Version=3.0.0.0, xxx">
<Private>True</Private>
<HintPath>..\packages\xxx\lib\net45\xxx.dll</HintPath>
</Reference>
In this case, when we build the project, nuget will still restore the nuget packages to absolute path C:\Development\projectname\Packages, but since the location of the project has changed to the D:\a\1\s\xx, so the relative paths for the HintPath in the .csproj file should also be changed accordingly, otherwise nuget can't find the corresponding dll file.
So, we should set the repositoryPath as relative paths in the nuget.config file, like:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<config>
<add key="repositoryPath" value=".\packages" />
</config>
</configuration>
With this setting, the path of the packages folder are based on the file nuget.config. As long as you have not modified the location of the nuget.config file, the HintPath does not need to be modified.
Note:
globalPackagesFolder is uesed for another nuget management type packagereference.
If you are working with someone else to develop a project, you need to unify the location of the nuget package, rather than continually modifying the HintPath manually. Otherwise it will cause confusion in your development.
Hope this helps.

Always remove Angular dist folder contents with WebDeploy on Destination

We've got a single page application on Angular 5 with an ASP.NET backend, and when we compile it, the release contents for Angular are output to a folder "Project\dist".
This works great on local dev machines, but all of the dist files are randomized with different names such as:
polyfills.dc7175a7225af84b3c9b.bundle.js
styles.dc7175a7225af84b3c9b.bundle.js
inline.dc7175a7225af84b3c9b.bundle.js
When we use Web Publishing to deploy to staging or production, everything transfers great and our custom folder in the publish profiles is included and published.
However, on the destination server (staging or production) these old, randomly named files and old (no longer used) folders persist. This results in hundreds and hundreds of old files (from old web deploys) that have accumulated on the staging and production servers. I need a method to automatically delete these every time we push updates with webdeploy.
Ideally, the workflow is:
Select publish profile, click Publish
Enter my credentials
Application builds successfully
If app built successfully, we go delete "Project\dist" folder on the destination server. "Project" could be in c:\inetpub\www\project or d:\websites\Project, for example.
Updated files are copied
Web deploy executes and copies the custom files in dist folder (already working).
Here's a redacted version of our current publish profile:
<?xml version="1.0" encoding="utf-8"?>
<!--
This file is used by the publish/package process of your Web project. You can customize the behavior of this process
by editing this MSBuild file. In order to learn more about this please visit http://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<WebPublishMethod>MSDeploy</WebPublishMethod>
<LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
<LastUsedPlatform>Any CPU</LastUsedPlatform>
<SiteUrlToLaunchAfterPublish />
<LaunchSiteAfterPublish>True</LaunchSiteAfterPublish>
<ExcludeApp_Data>False</ExcludeApp_Data>
<MSDeployServiceURL>staging.example.com</MSDeployServiceURL>
<DeployIisAppPath>Project</DeployIisAppPath>
<RemoteSitePhysicalPath />
<SkipExtraFilesOnServer>True</SkipExtraFilesOnServer>
<MSDeployPublishMethod>WMSVC</MSDeployPublishMethod>
<EnableMSDeployBackup>False</EnableMSDeployBackup>
<UserName>WebDeployUser</UserName>
<PublishDatabaseSettings>
<Objects xmlns="">
</Objects>
</PublishDatabaseSettings>
<ADUsesOwinOrOpenIdConnect>False</ADUsesOwinOrOpenIdConnect>
</PropertyGroup>
<Target Name="CustomCollectFiles">
<ItemGroup>
<_CustomFiles Include="..\Project\dist\**\*" />
<FilesForPackagingFromProject Include="%(_CustomFiles.Identity)">
<DestinationRelativePath>dist\%(RecursiveDir)%(Filename)%(Extension)</DestinationRelativePath>
</FilesForPackagingFromProject>
</ItemGroup>
</Target>
<PropertyGroup>
<CopyAllFilesToSingleFolderForPackageDependsOn>
CustomCollectFiles;
$(CopyAllFilesToSingleFolderForPackageDependsOn);
</CopyAllFilesToSingleFolderForPackageDependsOn>
<CopyAllFilesToSingleFolderForMsdeployDependsOn>
CustomCollectFiles;
$(CopyAllFilesToSingleFolderForMsdeployDependsOn);
</CopyAllFilesToSingleFolderForMsdeployDependsOn>
</PropertyGroup>
</Project>
I've tried a few accepted answer solutions already and can't get this to work:
https://stackoverflow.com/a/45538847/559988 (I tried this in the csproj and in the publish profile pubxml file.)
https://stackoverflow.com/a/5080942/559988
Any ideas? I have essentially zero knowledge of web deploy aside from setting it up in IIS.
Best,
Chris
EDIT I've also tried this: (Based on this: https://stackoverflow.com/a/15113445/559988)
<Target Name="CleanFolder">
<PropertyGroup>
<TargetFolder>$(_MSDeployDirPath_FullPath)\dist</TargetFolder>
</PropertyGroup>
<ItemGroup>
<FilesToClean Include="$(TargetFolder)\**\*"/>
<Directories Include="$([System.IO.Directory]::GetDirectories('$(TargetFolder)', '*', System.IO.SearchOption.AllDirectories))"
Exclude="$(TargetFolder)"/>
</ItemGroup>
<Delete Files="#(FilesToClean)" ContinueOnError="true"/>
<RemoveDir Directories="#(Directories)" />
</Target>
Update
This is specifically what we're doing: https://learn.microsoft.com/en-us/aspnet/web-forms/overview/deployment/visual-studio-web-deployment/deploying-extra-files
The first comment from there is the same problem we're experiencing:
This comes very handy in deploying Angular distribution files along
with ASP.Net backend, whenever both SPA and the backend share the same
single virtual application. Unfortunately, due to browser cache
busting techniques, the bundle files for Angular deployment will
always ship with unique names and, therefore, an msbuild
command/attribute or other possibility to wipe the folder clean on the
IIS side before sending the updated files would be very welcomed. If
anyone has found a way to do that, please share.
"Sync" functionality described here for msdeploy is exactly what we need to be doing but I don't know how to hook into this:
https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2008-R2-and-2008/dd569034(v=ws.10)#sync
In a sync operation, if the source file or folder does not exist on
the destination, the provider creates the folder and any subfolders
that have the corresponding files and attributes. If the destination
folder already exists, the provider updates only those objects that do
not match the source. This means that in some cases only one file or
folder will be updated. Files on the destination that do not exist on
the source will be deleted. The source and destination folders for
contentPath do not have to have the same name. If the name of the
destination folder differs from that of the source, the name of the
destination folder will remain the same, but the contents of the
folder will be updated to those of the source.
If I understood you correctly, then resolve this problem is help DeleteExistingFiles property in publish profile.
<DeleteExistingFiles>True</DeleteExistingFiles>
If set to True the output directory (publishUrl) will be purged
before output is written to it, it's good to start out with a clean
slate.
How can I apply this to a specific folder, ex. only delete the
"Project\dist" folder?
As I know result of this property is removing all files. For specify directories to remove you can try verb delete from MSDeploy, that can be wrap in <Exec> task of custom script:
<Exec Command="$(MSDeploy) -verb:delete -dest:"ContentPath=D:\TestDir\Test.txt&quot"/>
/*
* $(MSDeploy) is path to MSDeploy binary that you passed to script.
*/
This example show removing file on local machine. You should customize own call.
Exec Task | How to create a Web Deploy package when publishing a ClickOnce project (Some snippets for using targets)
Try please setting this in publish profile
<SkipExtraFilesOnServer>False</SkipExtraFilesOnServer>
in angular with nodejs, we will handle this problem with 'ng build --output-hashing=false'. Maybe you can search in this scope.

Packages not downloading on MSBuild Server

ASP.NET 4.5 MVC application
Builds/runs just fine locally
Packages folder NOT checked into source control
Source control looks like this:
MyProject
-.nuget
-.tfignore
-MyProject.Web
-MyProject.Utilities
-MyProject
-MyProject.sln
Inside of the .nuget folder, there is only one file: NuGet.config, which has this inside:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<solution>
<add key="disableSourceControlIntegration" value="true" />
</solution>
</configuration>
I can delete the packages folder locally, run it, and it correctly downloads everything again.
When I kick off a build from Visual Studio which should build the solution and deploy it to Azure Cloud Service, the build fails saying:
The type or namespace name 'WindowsAzure' does not exist in the namespace 'Microsoft' (are you missing an assembly reference?)
The "new" way to do this is to run
nuget.exe restore "MySolution.sln"
I put my (versioned) nuget.exe files on my build server like this:
c:\MyProgFiles\NuGet\2.8.6\nuget.exe
c:\MyProgFiles\NuGet\3.3.0\nuget.exe
Then before I build, I call
"c:\MyProgFiles\NuGet\2.8.6\nuget.exe" restore "c:\MyFullPath\MySolution.sln"
You can also read this about "how to clean up old nuget stuff"
http://docs.nuget.org/consume/package-restore/migrating-to-automatic-package-restore
(using CI macros when possible to create the file names)
I also put these lines before the "nuget restore" to help with debugging.
"c:\MyProgFiles\NuGet\2.8.6\nuget.exe" sources list
"c:\MyProgFiles\NuGet\2.8.6\nuget.exe" locals all -list
BTW: If you have a package source where the packages might get deleted, then add this to your build scripts (before the "restore" mentioned above)
"c:\MyProgFiles\NuGet\2.8.6\nuget.exe" locals all -clear
This will clear your local cache and force a fresh download.
Again, the value here is ONLY if you use a source where packages get deleted.

Xml file transforms using One-click publish, but not Web Deploy

I have two different ways to deploy my web app - one via IIS Web Deploy, hooked up via MSBuild arguments in a custom build template, and using the One-click publish built into Visual Studio. I have also installed the Slow Cheetah transforms, and can successfully transform Web.config files.
However, I'm stuck on the Nlog.config files. If I use One-click publish, the webapp deploys fine with the updated config file. However, using the actual build process, nothing gets transformed. The source Nlog.config file is copied to the drop locations, the deployment package, and the output folder on the remote server.
The .csproj file has Nlog set up the same way as web.config, i.e.
<Content Include="NLog.config">
<TransformOnBuild>true</TransformOnBuild>
<Content Include="Web.config">
<SubType>Designer</SubType>
<TransformOnBuild>true</TransformOnBuild>
<Content Include="NLog.Debug.config">
<DependentUpon>NLog.config</DependentUpon>
<IsTransformFile>True</IsTransformFile>
The SlowCheetah preview function lets me know that my transform files are well-formed, as well. Not sure what I could be missing.
Well, I really biffed that one. After much poking, I found that I needed to include the following -
<Import Project="TransformsFiles.targets" />
<PropertyGroup>
<TransformOnBuild>true</TransformOnBuild>
</PropertyGroup>
right above the final Project tag, as Sayed mentions here. This will actually transform the files, but they still won't deploy successfully. I'll have to add in some post-build events or something to take care of that. Not the best solution, but it's working, at least.
UPDATE: Just for comprehension, to deploy the files, I had to edit the .csproj file to include a new target and dump them manually to the remote server, but only after the transformation had completed. Take a look at your log file to see what's going on, then just pick up the transformed file and move it to the remote server. That part of the code looks like this -
<Target Name="PostTransformNLogConfig" AfterTargets="TransformAllFiles">
<Copy Condition="Exists('d:\Builds\Binaries\NLog.config' )"
SourceFiles="d:\Builds\NLog.config"
DestinationFiles="\\remoteserver\NLog.config" />
</Target>

How to I perform a web.config transform with MSBuild or MSDeploy?

I've tried a number of different configurations with this and I haven't achieved my result.
TL;DR
I'm trying to add config transforms into my build process and am looking for the right way to do it from MSBuild so that it shows up in my deployments via MSDeploy.
Background
I have an WebApp (MVC3), a Core app (CS Class Lib), and two test class libs, one for each.
I have a build script in my solution that uses MSBuild to compile.
One of those MSBuild targets deploys to an IIS server using MSDeploy
This process is working so far both manually and via CruiseControl.NET
Goal
I would like to add Web.Config transforms to this process. I figured I would do something simple at first, like an app setting called "PEAppsEnvironmentName", which I would make Dev, Test, or Prod based on the current environment.
Theory So Far
To me, it appears that when packaging with MSDeploy, I'm not transforming the config file.
When I run MSBuild with the DeployOnBuild option set to true, it creates another package that has the appropriately transformed config. It just seems like somehow I can't get it all to match up. The end result is that the web page displays "None" (the initial setting) instead of the transformed "Development" string.
I think if I could find out how to use MSDeploy during the packaging phase to transform the MSConfig, I'd be good to go.
Code
My web.config file
<appSettings>
<add key ="PEAppsEnvironmentName" value="None"/>
...
</appSettings>
My Web.Dev.config file
<appSettings>
<add key ="PEAppsEnvironmentName" xdt:Transform="Replace" xdt:Locator="Match(key)" value="Development" />
</appSettings>
My MSBuild Targets
Property group showing default config is "Dev"
<PropertyGroup>
<Configuration Condition="'$(Configuration)' == ''">Dev</Configuration>
</PropertyGroup>
My MSBuild "Compile" Target
<Target Name="Compile" DependsOnTargets="Init">
<MSBuild Projects="#(SolutionFile)" Targets="Rebuild" Properties="OutDir=%(BuildArtifacts.FullPath);DeployOnBuild=True"/>
</Target>
My MSBuild "Package" Target
<Target Name="Package" DependsOnTargets="Compile;Test">
<PropertyGroup>
<PackageDir>%(PackageFile.RootDir)%(PackageFile.Directory)</PackageDir>
<Source>%(WebSite.FullPath)</Source>
<Destination>%(PackageFile.FullPath)</Destination>
</PropertyGroup>
<MakeDir Directories="$(PackageDir)"/>
<Exec Command='"#(MSDeploy)" -verb:sync -source:iisApp="$(Source)" -dest:package="$(Destination)" '/>
</Target>
My MSBuild "Deploy" Target
(scrubbed for PWs, etc.)
<Target Name='Deploy' DependsOnTargets='Package'>
<PropertyGroup>
<Source>%(PackageFile.FullPath)</Source>
</PropertyGroup>
<Exec Command ='"#(MsDeploy)" -verb:sync -source:package="$(Source)" -dest:iisApp=PEApps,computerName=$(WebServerName),username=[User],password=[Password]'/>
</Target>
There was a lot to this question, I'm not sure if I'm fully on the same page as you but I'll summarize my impression of what you are asking. You have an existing web project which is in a solution with other projects. You need to be able to package the web project so that you can publish it to multiple destinations.
I have created a NuGet package which can be used for this exact purpose. It's called package-web. When you add it to your web project it will update the packaging process. When you create a package a few additional files will be included in the package, including all the web.config transform files. A .ps1 file will be created next to the package as well. You can use this script to publish the package. It will prompt you for which transform to run and for all the Web Deploy parameters. You can also save the responses to a file and then just pass them to the .ps1 file so that you can perform non-interactive publishes. I created a 5 minute video on it at http://nuget.org/packages/PackageWeb
package web: http://sedodream.com/2012/03/14/PackageWebUpdatedAndVideoBelow.aspx. FYI this is not yet working with VS 2012 but I'm working on the fix and should have it updated by the time VS 2012 is released.
If you don't find that useful you can see how I implemented the solution at https://github.com/sayedihashimi/package-web and you should see examples of everything that you need to do to roll your own.
FYI if you need to transform any files besides web.config on package create then you should take a look at my VS extension SlowCheetah. Here is a blog about how to integrate it into a build server.

Resources