Including solution level files in your NuGet package
Rarely, if ever, does the OctoPack or for that matter NuGet.exe, miss a file or files that teams are looking to have in the NuGet package. Earlier this week I was helping a team with their NuGet package and they wanted to include files that were inside a folder at the solution level, not within a project folder. Normally, it’s not a difficult solution to solve. Most files under Visual Studio have the option to be included as shown below.
That said, when you have files outside of the project folders themselves they won’t get included in the NuGet package that’s created. Case in point: a testsettings file in a solution level folder. When you right-click on the file, there’s no option for “Copy to Output Directory” or “Build Action”.
Luckily, NuGet can specify files to add to a package within the NuSpec file. From the NuGet documentation, you can add the <files> tag within the NuSpec file of your project, and specify files outside of the project. But for this particular project, I had difficulties figuring out how to get this testsettings file from a solution level folder into the generated NuGet package from the OctoPack. I’ll use my OctopusDemo.testsettings file as an example; here’s my NuSpec file:
<?xml version="1.0"?> <package > <metadata> <id>OctopusDemo.Web</id> <version>22.214.171.124</version> <authors>Ian Paullin</authors> <owners>Ian Paullin</owners> <licenseUrl>http://octopusdemo.com</licenseUrl> <projectUrl>http://octopusdemo.com</projectUrl> <iconUrl>http://octopusdemo.com</iconUrl> <requireLicenseAcceptance>false</requireLicenseAcceptance> <description>Octopus Demo of ASP.NET MVC Web Application Project</description> <releaseNotes>Some release notes</releaseNotes> <copyright>Copyright 2014</copyright> <tags>Cool Tags Man</tags> </metadata> <files> <file src="**"/> <file src="..\OctopusDemo.testsettings" target="TestFiles"/> </files> </package>
The <files> tag and subsequent <file> tag allows you to specify what files are going to be packaged up. The first tag with the “**” as the source recursively copies all the files within the project into the NuGet package. If you omit this tag, the only file that will be copied is the testsettings file. The second file tag source moves up one directory (“..\”) as the testsettings file is at the root of the solution, not the anticipated “Test Files” directory. Why?
Behind the scenes, Visual Studio Solution Explorer is lying to us about the testsettings file! Looking at the solution explorer, we see the testsettings file in the “Test Files” solution level folder.
If you try to use the source as src=”Test Files\OctopusDemo.testsettings” in your NuSpec file, this will not work and the desired file will not be included in your NuGet package. But if we use TFS Explorer to look at our solution, we see something different. There’s no “Test Files” folder, and the testsettings file is in the root of the solution. What’s going on?
In the Visual Studio solution (.sln) file, the contents displayed below show the tools folder exists and is explicitly stated (Tools\Octo.exe = Tools\Octo.exe). The “Test Files” folder is listed as a project, but the path is omitted where the file is listed (OctopusDemo.testsettings = OctopusDemo.testsettings)
Why is this? To paraphrase this stackoverflow post: solution folders are really virtual folders. I’m trying to find official Microsoft documentation that confirms it but I haven’t invested the time to find it. So what are the final results using my NuSpec file? You can see the testsettings file in the “TestFiles” directory that was the specified target in the NuSpec file.
It’s an easy fix for a relatively simple problem. I didn’t realize that Visual Studio solution files abstracted the solution level folders so that was an interesting piece of information. I’ve found that most versions of Visual Studio solutions do this abstraction of solution level folders (2013 and 2014). I haven’t tested out Visual Studio 2012 or 2010 but this virtual solution level folder behavior seems consistent across versions.