Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I reference instead of copy js files from a Nuget package at build time in TeamCity?

I've got a packages.config file checked into source control. This specifies the exact version of the Nuget dependency I want. We have our own NuGet repository. We are creating these NuGet packages ourselves.

<packages>
  <package id="Dome" version="1.0.0.19" targetFramework="net45" />
  <package id="Dome.Dojo" version="1.0.0.19" targetFramework="net45" />
</packages>

These packages have some JavaScript files which when you add the Nuget package as a reference in Visual Studio are copied to the Scripts folder in the project.

I don't want to check these JS files in to source control, I just want to check in the packages.config file.

When my project builds in Team City (or when I build in Visual Studio after a fresh checkout) it doesn't copy the JS files from the NuGet package. There's a question here explaining a similar problem:

NuGet package files not being copied to project content during build

But, the solution in the answer to that question doesn't work for me; that solution uses ReInstall, which is problematic because it can automatically upgrade the version in the packages.config file (say if a dependency is specified as a >=).

The whole point of this is that I want to be able to checkout a revision from my source control, and build that version with the right dependencies AND I want to use the nice packaging features of NuGet. So, I don't want any "automatically update to the latest version during the build."

There's an issue against NuGet (http://nuget.codeplex.com/workitem/2094) about NuGet files not restoring content files. And it's Marked as Closed By Design.

Thinking about how this works a little more, it appears to me (but I'm not 100% sure) that for assemblies NuGet has a different behaviour - it doesn't copy them into the project, instead it references them from the location in the packages folder. It strikes me that js files in the NuGet package should be referenced analogous to how dlls are referenced.

Is there a way to construct a NuGet package so that it references the JS as links in the project (in a similar way to how you can add an existing File as a Link in VS)? And would this solve my problem?

If not then I'll take the advice given by Jeff Handley when closing ticked Nuget Issue 2094 mentioned above:

The option you'd have is to create a new console executable that references NuGet.Core, and you could build a supplemental package restore for your own use that copies package contents into the project.

Writing my own command line tool to copy the contents does seem like I'm pushing water uphill here - am I doing something fundamentally wrong?

like image 232
Daniel James Bryars Avatar asked Oct 14 '13 21:10

Daniel James Bryars


2 Answers

The underlying problem here is Visual Studio's relatively poor support of JavaScript projects and JavaScript's lack of built-in module loader.

For C#, when you install a package it adds a reference in your .csproj file to the assembly on disk. When you build, MSBuild knows to copy the thing referenced to the bin directory. Since you aren't checking in your bin directory, this all works great.

Unfortunately for JavaScript, the build system isn't nearly as matured and there aren't well defined guidelines for NuGet to follow. Ideally (IMO), Visual Studio would not run web sites directly from your source directory. Instead, when you built it would copy the JavaScript files, CSS and HTML files to a bin directory from which they would be executed. When debugging, it would map those back to the original JavaScript or TypeScript files (so if you make a change it isn't to a transient file). If that were to happen then there is now a well-defined build step and presumably a well-defined tag for JavaScript files (rather than just "content"). This means that NuGet would be able to leverage that well-defined MSBuild tag and package authors could leverage the NuGet feature to do the right thing.

Unfortunately, none of the above is true. JavaScript files are run in-place, If you did copy them to bin on build Visual Studio would do the wrong thing and editing from a debugger would edit the transient files (not the originals). NuGet therefore has no well-defined place to put files so it leaves the decision up to the package author. Package authors know that the average user is just going to be running directly from source (no build-step) so they dump files into the source folder where they must be checked in to version control.

The entire system is very archaic if you are coming from a modern ecosystem like C# where someone took time to think these things through a bit.

What you could do is create an MSBuild task that, before build, would go through all of your packages, look for content, and copy that content to the desired location. This wouldn't be terribly difficult, though would take a bit of work.

Also, package authors could include a build-task that does this in their package so that before-build all of their content was copied local. Unfortunately, if only some package authors do this then you end up with weird fragmentation where some packages need to be committed to version control and others do not.

like image 183
Micah Zoltu Avatar answered Sep 18 '22 05:09

Micah Zoltu


When a package is installed into a project, NuGet in fact performs these operations,

  1. Download the package file from source;
  2. Install the package into the so called packages folder, which is $(SolutionDir)\packages by default;
  3. Install the package into the project, which consists of adding references to DLLs, copying content files into the project directory etc.

When a package is restored, only the first two steps are executed. Projects will not be touched by nuget package restore. Which is why the js files in your project will not be "restored".

The only solution for now is to check in the js files in your project.

like image 29
Fei Ling Avatar answered Sep 19 '22 05:09

Fei Ling