TFS NuGet Installer build step not working - tfs

I'm trying to configure an automated build of a project that has a NuGet package reference, but I'm not having any luck. (FYI I'm still pretty wet behind the ears with all of this, so please provide simple steps and/or configurations.)
Note: this isn't a duplicate of other similar questions, as I'm using a central package repository. Other similar questions make no mention of this important detail, so they should be assumed to not be relevant.
The build runs fine without the reference. I added Newtonsoft.Json and bound to it by including this simple construct:
Dim eHandling As Newtonsoft.Json.ConstructorHandling
eHandling = Newtonsoft.Json.ConstructorHandling.Default
I checked it in and the build started, but NuGet hadn't first copied the assembly to my application's bin folder. It did, however, copy it to here:
Restoring NuGet package Newtonsoft.Json.9.0.1.
Adding package 'Newtonsoft.Json.9.0.1' to folder 'C:\Agent\_work\1\s\packages'
Naturally the build failed, as it couldn't find the dependency.
It's worth noting that I'm using a central package repository on my dev machine:
<config>
<add key="repositoryPath" value="D:\Dev\Packages" />
</config>
I'd like to emulate this behavior on the server as well, e.g. C:\Packages\*\*.nupkg.
I tried using the standard %AppData%\NuGet\NuGet.config file, but the build ignores it. I tried the advice in this answer (using repositoryPath instead of packageSources as shown there), but that causes the server to hang until I restart the VSO Agent service. Thinking it might be a permissions issue, I reconfigured the agent to run under the user account associated with the %AppData% location of NuGet.config. Still no luck. No build.
How can I get NuGet to download and populate the central package repository on the server and then copy the appropriate dependencies to the application bin folder prior to running the build step?
EDIT 1
Update: Apparently something's working, as I now have a C:\Packages\Newtonsoft.Json.9.0.1 folder on the server. However, the assembly still isn't being copied to the application bin folder prior to build. Same result. Failed build.
EDIT 2
OK, I'm getting closer. I created a D: drive on the server and set the local repositoryPath value to D:\Dev\Packages, the same as it is on my dev machine. The build is still failing, but a quick look at the project XML reveals this:
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\..\..\Packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
How to deal with that relative path? That should fix it, yes?
EDIT 3
OK, that worked. I edited the project and changed HintPath to
D:\Dev\Packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll
I now have a successful build.
But this is going to get real tedious real fast. Surely I'm not going to have to do this for every single NuGet reference in every single project, past present and future... am I?

OK, got it.
As long as the repositoryPath folder on the server is the same number of levels deep as on our dev machine—in relation to the folder in which the Build Agent puts the project file—we can put it anywhere we want and retain the relative HintPath value in the project file.
For example, in my case I ended up setting the server location to C:\Agent\Build\Packages, to match the hierarchical location of the local Git repos on my dev machine:
D:\Dev\Packages
D:\Dev\Git\app.repo\App\App.vbproj
Works great.
EDIT
Just to clarify, the action of copying the assembly from the package folder to the application bin folder isn't a NuGet action. It's an MsBuild action (i.e. the CopyLocal setting in the project's assembly reference properties).
The reason it was failing was that MsBuild couldn't find the assembly to copy, according to its relative reference as specified in the project file.
So technically my question title is incorrect. The NuGet Installer step has been working fine all along.

Related

MSBuild builds solution but fails to build the project

I have F# project which I want to build with command line (to use that later in FAKE config).
The problem is that MSBuild fails to resolve assembly dependencies when I use it on the project file directly. While it goes fine when I use solution file with this single project included.
I really have run out of ideas. The solution file seems to not contain any critical information.
Another weird thing is that VSCode also fails to resolve one of those assemblies. I hope that when I fix MSBuild config I may be will able to see what's wrong with VSCode.
Command line:
"C:\Program Files (x86)\MSBuild\14.0\Bin\msbuild.exe" FSharpWeb1\FSharpWeb1.fsproj /t:rebuild
Error message:
C:\work\MNP\testMSBuild1\FSharpWebApi\FSharpWeb1\FSharpWeb1.fsproj(173,5): error MSB4062: The "MSBuild.ExtensionPack.FileSystem.File" task could not be loaded from the assembly C:\work\MNP\testMSBuild1\FSharpWebApi\FSharpWeb1\*Undefined*\packages\MSBuild.Extension.Pack.1.3.0\tools\net40\MSBuild.ExtensionPack.dll. Could not load file or assembly 'file:///C:\work\MNP\testMSBuild1\FSharpWebApi\FSharpWeb1\*Undefined*\packages\MSBuild.Extension.Pack.1.3.0\tools\net40\MSBuild.ExtensionPack.dll' or one of its dependencies. The filename, directory name, or volume label syntax is incorrect. Confirm that the <UsingTask> declaration is correct, that the assembly and all its dependencies are available, and that the task contains a public class that implements Microsoft.Build.Framework.ITask.Done Building Project "C:\work\MNP\testMSBuild1\FSharpWebApi\FSharpWeb1\FSharpWeb1.fsproj" (rebuild target(s)) -- FAILED.
I've pushed the minimal demo to github: https://github.com/alehro/testMSBuild.git
It's actually easy to reproduce independently. In VS 2015 Community edition create new project from F# Web Template named "Web Api 2.2" and then try to build it with MSBuild.
Another disturbing thing is that the minimal demo produces different errors from those I've seen yesterday. Also vscode complains on different items. If yesterday it could not resolve a couple of calls, now it complains on all of:
open System.Net.Http
open System.Web
open System.Web.Http
open System.Web.Routing
telling that neither of them is defined.
Reformatting my comments to a response now that it's verified it works:
Your FSharpWeb1.fsproj references MSBuild.ExtensionPack.FileSystem.File task from MSBuild.Extension.Pack, but the path specified in the <UsingTask> tag contains $(SolutionDir) property which is not defined when you run MSBuild outside of Visual Studio.
The error message you're getting shows that in the highlighted part of the path:
The "MSBuild.ExtensionPack.FileSystem.File" task could not be loaded from the assembly C:\work\MNP\testMSBuild1\FSharpWebApi\FSharpWeb1\*Undefined*\packages\MSBuild.Extension.Pack.1.3.0\tools\net40\MSBuild.ExtensionPack.dll.
This can be remedied by conditionally setting the relative path when the property is not set by VS:
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
(original response for this solution: https://stackoverflow.com/a/33782131/1659828)
One more thing I mentioned in the comments is that this solution assumes you already have the necessary dependencies downloaded in the packages folder. Visual Studio does that automatically by restoring NuGet packages before build, but when you build in another context, you have to make sure the packages are restored, otherwise the build will keep failing.

How to include config transform files in web application filesystem publish output

I've been pulling my hair out on this for a while now. I'm trying to implement a continuous integration and deployment pipeline using TeamCity and Octopus Deploy. I am 99% there, except for one problem. I am using the standard msbuild runner of teamcity, configured to use the version 12 of msbuild.
I need to include the web.config transforms in the published output so they can be packaged into a nuget package for octopus deploy. I do not want the transforms to be applied by msbuild.
I am not using Octopack to create packages. I'm using the built-in teamcity nuget packager. So I'm publishing the website to a filesystem folder and then creating the package from the files in this folder. However, no matter what I do I cannot get msbuild to include the web.config transform files in the publish (I am using Octopus Deploy to perform the transforms, so I don't want msbuild to perform them).
I have verified that all the transform files (Web.Release.config, etc..) are marked as "Content". I have NOT marked them to copy always, because doing this copies them to the bin folder, not the root folder where they belong.
I have removed the /p:Configuration= property from the msbuild command line as I've read that is required for transforms to be applied. my parameters to msbuild look like this:
/p:DeployOnBuild=true /p:PublishProfile=Deployment
There is nothing in the publish profile that seems to relate to transforms. The publish profile contains the filesystem location to publish to.
Any suggestions here?
Note: I've given up and found a different solution, but I'm leaving this open in case anyone has any input.
You could create a custom .nuspec file and reference the files that you want to include from there.
My suggestion would be to have the .nuspec file in the same directory as the web.config / web.release.config files, and make the paths relative from there.
So if you publish to a directory called /output you could use rules like this
<files>
<file src="*.config" target="\" />
<file src="publish\*.*" target="\" />
</files>
So nuget pack nuspecPath would become the way to pack the project
NuSpec Reference
Hope this helps

Release manager for TFS 2013 app.config processing with token files

I seem to be getting a lot of pain with the processing of app.config and token files (we have this working with the older ".11" templates).
It looks like currently (using the ReleaseTfvcTemplate.12.xaml) this is running the tokenisation after the build.
While I can make the app.config / myapp.exe.config stuff work by deliberately copying the .token file into my output folder (so the recursive search finds it) this feels pretty horrid.
As a fix I'm tempted to move lines 182-230 to just before the RunMSBuild task on line 175 (creating a new sequence at that point)
Is this the correct approach or have I missed some documentation somewhere (or a later version of the template?)
Thanks guys... Anyway for future reference I did make the change.
However I had misunderstood the exact nature of the order things happen out of the box which is as follows:
Get the project out of source control
build with msbuild
Copy the .config.token file over the .config file. This is in the TFS template
As part of the deploy to a server then the token entries in the .config files are replaced. This is in the release manager template.
Tests are run in the msbuild binary output folders.
The problem is this doesn't really work if you're using an project type that uses app.config file as the msbuild process renames these output.exe.config during the msbuild stage so you need to create both an output.exe.config (marked as copy to output) and an output.exe.config.token so when the post deploy is the final output gets configured correctly. This also a problem if you want to tokenise some mstest dlls as these typically use an app.config as well. Basically this is a bit of a mess unless you are using web.config.
We worked our way around this by using the modification I suggested above (you need to create a sequence at line 175 and move lines 178-230 up into the sequence, this is GetBuildDirectory variable bits and the if statement) followed by adding an additional deployment stage which copies back onto the build server with the new tokenised files so the mstest can run against them.
So our new process looks like this:
Get the project out of source control
Copy the .config.token file over the .config file i.e app.config.token copied over app.config
build with msbuild (this means we end up with tokenised myapp.exe.config and mytests.dll.config)
As part of the deploy to a server then the token entries in the .config files are replaced. This is a release management step in the release template.
Deploy the tests back into a folder on build server (think this has to be a fixed folder until update 4 of release manager is deployed) the token entries in the .config files are replaced (so our integration tests can use the newly deployed servers). This is a release management step in the release template.
Tests are run in the fixed folder on the build server (rather than the msbuild output directory) so the test wildcard needs to be changed in the tfs build template.
Quick final note we don't use that build directory variable and it's left at blank I'm not convinced this would work if it was set to a value...
The replacement of variables in config files with Release Management happens at deployment time and not at compile time.
When RM deployes your app it inserts the correct variables.
It sounds like you're hitting one of two issues:
You need to include the .token file in your project and make sure it's set to Copy Always, so that it gets copied to the build output folder.
If you're building a web application, I've seen a bug in the release build process template that doesn't touch the contents of the _PublishedWebsites folder. I don't know if it's been fixed in Update 4 or not, but it was definitely still a problem in earlier versions.

How do I get my build agent to build a solution with external project references?

Currently I am plagued with two TFS build issues:
Issue one: I have a solution with a project that now references the dll product of another project in another solution. The build agent does not seem to include these dlls and the build fails.
Issue two: I have a solution that references the a project in another solution. The build agent does not seem to include the externally referenced project and the build fails.
I have looked at the "copy directory" build activity but have no idea where to shim that in or what to put as source and output values.
The best practice is to use project references for referencing other projects within the same solution. For references which are external to your solution you should use file references, and then check in the compiled DLL which is being referenced.
Solution1
\Project1 --> Project1.dll
\Project2 --> Project2.dll
Solution2
\ProjectA (references Project1.dll)
\ProjectB (references Project2.dll)
\References
\Project1.dll -- this DLL gets checked in here and ProjectA references from here
\Project2.dll -- this DLL gets checked in here and ProjectB references from here
ProjectA.csproj
<Reference Include="Project1.dll, Version=blah blah blah">
<HintPath>..\References\Project1.dll</HintPath>
</Reference>
ProjectB.csproj
<Reference Include="Project2.dll, Version=blah blah blah">
<HintPath>..\References\Project2.dll</HintPath>
</Reference>
With this approach you have to build Solution1, get the DLLs which get dropped, and then check them into the References folder for Solution2. You can get real fancy and setup some logic in the build for Solution1 which automatically checks out the Solution2\References folder, replaces the Project1 and Project2 DLLs with the latest from the build, and then checks them in... and if you're using Continuous Integration this kicks off the build for Solution2.
If you are using TFS 2012 / TFS2010 build templates, make sure that the property "Solution Specific Build Output" set to false. This will ensure that the build agent builds all assemblies in the same bin directory.

NuGet Package Restore is not restoring packages on build

I am moving our source code from Vault to TFS, not bothering with the migration or anything, just pulling a get latest in vault and adding it to TFS.
The solution has got several projects, and each one has at least one NuGet package. I am trying to get Package Restore working again. It worked in Vault (but not the way it was supposed to). I was under a bit of a deadline, and it did not work at first, so I added a Pre-Build event to run nuget.exe against the packages.config for each project.
TFS build service complains about that, so I am trying to get it working "right".
I have set the option in Visual Studio Tools menu.
I have installed NuGetEnablePackageRestore and run the fix.
I have verified that the packages directory is is source control, but is empty.
I have verified that the project files each include the following:
<RestorePackages>true</RestorePackages>
<Import Project="$(SolutionDir)\.nuget\nuget.targets" />
Building with Diagnostic level verbosity reveals that each project evaluates those properties, but the RestoreCommand in nuget.targets is never executed.
Any thoughts?
I have attempted to implement the solutions from these links:
nuget - package restore not working
NuGet Package Restore Not Working - I did post a question/comment on there asking for clarification...
http://nuget.codeplex.com/workitem/1879
Edit
Additionally, I have found that the RestoreCommand property is being evaluated during build. Diagnostic Verbosity shows:
RestoreCommand = (set EnableNuGetPackageRestore=true) && "C:\Source\Kiersted Direct And Related\Direct\Kiersted\.nuget\nuget.exe" install "packages.config" -source "#(PackageSource)" -o "C:\Source\Kiersted Direct And Related\Direct\Kiersted\packages"
I figured it out, and I found the answer here: MSBuild not running BuildDependsOn tasks from an imported project
The problem (after looking through the Diagnostic verbosity build output) was that the BuildDependsOn setting was getting un-set. My project files each had the import statement
<Import Project="$(SolutionDir)\.nuget\nuget.targets" />
but that statement was at the beginning of the XML tree. Apparently the import for Microsoft.CSharp.targets can interfere with that import and thus the BuildDependsOn.
My solution was to move the nuget.targets import to below the Microsoft.CSharp.targets import. Now everything builds beautifully.
This answer needs to be considered with the others. In my case, Visual Studio decided not to add the packages.config automatically into Source Control. Hence the file did not make it's way through to the build server for consideration during Nuget restore.

Resources