I would not reccomend for anyone to follow my steps described here while trying to solve similiar issue. It’s more to highlight a path I’ve taken than to give you a proper solution. My solution is probably deeply flawed and for proper DevOps would look more like flailing in the wind than anything else. You’ve been warned.
There are days, when DevOps seems just easy and a little boring. Those are the days when everything works, and problems you encounter are ‘I forgot to check-in .csproj file’ kind. And there are days when you just try to pull your hair from your head just to experience a lower level of pain, then when dealing with this particular build problem.
Let’s do it as Azure functions project
That was the dooming sentence, although I haven’t know that at a time. I vaguely remembered that we did some work around azure functions recently, so it should be piece of cake I thought. Well, DevOps wouldn’t be one of the best-paid jobs in the IT if it would be easy.
Creating a build process
Build process in TeamCity by default looks something like this:
- GitVersion – creates build version by looking at tags and branch
- Nuget restore – gets all packages required to build solution
- Visual Studio 2015 (sln) – I’m not really sure about this one, but I would guess it compiles solution using Microsoft Build Tools for VS2015
- NUnit/Xunit tests – we usually have one chosen per solution
- Nuget Pack – per every package that needs to be created
So I’ve created something that looked exactly like this (with all the customisation that might be required for the particular project) and crossed my fingers.
Function1.cs(5, 22): error CS0234: The type or namespace name 'Http' does not exist in the namespace 'System.Net' (are you missing an assembly reference?) Function1.cs(8, 21): error CS0234: The type or namespace name 'Azure' does not exist in the namespace 'Microsoft' (are you missing an assembly reference?) Function1.cs(9, 21): error CS0234: The type or namespace name 'Azure' does not exist in the namespace 'Microsoft' (are you missing an assembly reference?) Function1.cs(10, 21): error CS0234: The type or namespace name 'Azure' does not exist in the namespace 'Microsoft' (are you missing an assembly reference?) [...]
This was pretty easy to spot – it would just refuse to compile and complain that packages that were obviously on my machine were missing on the TeamCity Agent.
At this point I was kinda confused – the build tools for VS2015 were already installed, so I tried to install just Azure SDK package from Microsoft site. The installer, however, was taking ages to finish, so after about 90 minutes, I cancelled the installation and just plainly installed VS2017 with the same setup as on my dev machine (~9gb of space). I’ve loaded project (still on the agent) and it compiled fine.
I admit I probably should have wait for Azure SDK, especially that I’ve bloated Agent with entire VS2017, but for now, it propelled me forward.
Wrong VS build agent
D:\TeamCityAgent\work\bd22badbf4330b79\src\Service.Azure\Service.Azure.csproj(1, 1): error MSB4041: The default XML namespace of the project must be the MSBuild XML namespace. If the project is authored in the MSBuild 2003 format, please add xmlns="http://schemas.microsoft.com/developer/msbuild/2003" to the element. If the project has been authored in the old 1.0 or 1.2 format, please convert it to MSBuild 2003 format.
This one was pretty easy to figure thanks to VS installed on the agent. I’ve opened project on the Agent and compiled it fine via Remote Desktop. So it was ok. But compile process that was using VS2015 was unable to read csproj file.
Switching Build Step 3 (see default Build process at the beginning) in TeamCity to 2017 should have solved the issue, but for some reason, it still uses 2015 on Agent if that is the only available version. I limited myself to build only on one agent (which I knew it worked on) for the remainder of my tests.
Nuget restore won’t work properly for Azure Functions
C:\Program Files\dotnet\sdk\2.1.4\Sdks\Microsoft.NET.Sdk\build\Microsoft.PackageDependencyResolution.targets(327, 5): Assets file 'D:\TeamCityAgent\work\bd22badbf4330b79\src\Service.Azure\obj\project.assets.json' not found. Run a NuGet package restore to generate this file.
This took me a while. I could see the offending file on the dev machine and in VS on the agent. It was just built process that was missing it. It kinda confused me as I assumed that build problem issue. However, I’ve found a issue on github ‘NuGet Restore task fails for .Net Core project’ and substituted Build Step 2 (nuget restore) for dotnet restore command line
Dotnet restore won’t work for anything but Azure Functions 😛
D:\TeamCityAgent\work\bd22badbf4330b79\src\Service.WebApi\Service.WebApi.csproj(275,3): error MSB4019: The imported project "C:\Program Files\dotnet\sdk\2.1.4\Microsoft\VisualStudio\v15.0\WebApplications\Microsoft.WebApplication.targets" was not found. Confirm that the path in the declaration is correct, and that the file exists on disk.
To this point, errors were usually related to Service.Azure project, but this one concerned Service.WebApi – a simple test project that we used to test functions using swagger that was faster than using Azure Functions each time. But wait. It should work!
Few minutes of googling later it turns out that dotnet build solution with old ASP.NET Web project fails. So yet another time I’ve probably done the wrong thing, that just worked – I run Nuget restore for Service solution and Dotnet restore for Service.Azure project.
The peak of abstraction
[_ComputeLockFileCopyLocal] C:\Program Files\dotnet\sdk\2.1.4\Sdks\Microsoft.NET.Sdk\build\Microsoft.PackageDependencyResolution.targets(557, 9): error MSB4186: Invalid static method invocation syntax: "[System.IO.Directory]::GetParent().get_Name()". Method 'System.IO.Directory.GetParent' not found. Static method invocation should be of the form: $([FullTypeName]::Method()), e.g. $([System.IO.Path]::Combine(`a`, `b`)).
I’ve pretty much given up at this point, but fortunately, Viv came to my rescue. Together using Remote Desktop we just ran MSBuild process, which managed to finish and build successfully.
So another question appeared: why would TeamCity’s Microsoft Visual Studio solution (.sln) runner fail, but MSBuild completed without a problem? Short answer – I don’t know.
The final build process
So after all those changes and substitutions, the resulting Build Process for our little Azure functions project looked like this:
- Nuget Restore (for solution)
- Dotnet Restore (for Azure Functions csproject only)
- MSBuild command for solution
- Nunit3 Tests runner
- Nuget Pack to build artefact, that will be (hopefully) deployed as Azure Functions