Skip to content

[spec] Package version lineups

Nate McMaster edited this page Aug 8, 2017 · 8 revisions

Summary

Package version lineups are a way to manage the versions of NuGet dependencies. This removes the requirement to add an explicit package version on all <PackageReference> nodes. Instead, a lineup, which contains a list of packages and versions, is used to generate the effective <PackageReference> version.

Usage

Contents of the lineup

A lineup file is itself a NuGet package. The <dependencies> section of the package metadata supplies a list of packages and versions that are used. Unlike a normal "dependency" package type (aka PackageReference), any dependency listed is not automatically installed into the project. It is only added if there is also a <PackageReference> for that package.

Any files added into the package should be ignored as only the metadata of the package is used.

Authoring a lineup file

A lineup can be defined as a nuspec and produced using regular nuget pack command.

A lineup file should specify it's package type as "lineup". This will prevent NuGet from installing this as a PackageReference from the Package Manager Console or VS Package Manager.

    <packageTypes>
      <packageType name="lineup" />
    </packageTypes>

Using a lineup file

They are specified using the <PackageLineup> item group. A PackageLineup must also specify the Version attribute.

In v1 of this design, lineup's are applied to an entire git repo, so they are specified in repo.props, not individual *.csproj files. Enhancements in the future may allow for specifying PackageLineup's per project.

When a solution is restored using KoreBuild, the versions from the lineup will be applied to any matching PackageReference element without a explicit Version attribute.

Lineups must not override the "Version" metadata on PackageReference's is already specified unless "IsImplicitlyDefined" is "true". i.e. Lineups can also be used to manage "automatic" PackageReferences such as Microsoft.NETCore.App. However, when a Version is supplied in *.csproj and a lineup, the build should produce a warning unless "NoWarn" is also true.

Example lineup package

<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
  <metadata>
    <id>Example.Lineup</id>
    <version>1.0.0-build-001</version>
    <authors>Test</authors>
    <description>Example lineup package.</description>
    <packageTypes>
      <packageType name="lineup" />
    </packageTypes>
    <dependencies>
        <dependency id="Newtonsoft.Json" verison="9.0.1" />
        <dependency id="Moq" version="4.7.99" />
        <dependency id="xunit" version="2.2.0" />
    </dependencies>
  </metadata>
</package>

This lineup would have the following effect on this example csproj:

<!-- build/repo.props -->
<ItemGroup>
  <PackageLineup Include="Example.Lineup" Version="1.0.0-build-001" />
</ItemGroup>
<!-- src/MyProj.csproj -->
<ItemGroup>
   <PackageReference Include="Newtonsoft.Json" />
   <PackageReference Include="Moq" Version="5.0.0" NoWarn="true" />
   <PackageReference Include="xunit" Version="2.3.0-beta2" />
</ItemGroup>

Result

Package Effective version Source of version
Newtonsoft.Json 9.0.1 Example.Lineup
xunit 2.3.0-beta2 csproj file --> produces warning
Moq 5.0.0 csproj file --> no warning

This example demonstrates that:

  1. Lineup does not override an explicit Version attribute
  2. Lineup does not add a PackageReference
  3. Lineups will warn when a PackageReference could have been updated

Implementation details

Lineup's control the PackageReference version by generating an MSBuild targets file in $(MSBuildProjectExtensionsPath). Microsoft.Common.targets will automatically import from this lcoation. This is usually obj/$(MSBuildProjectName).nugetpolicy.g.targets.

The contents of the file look something like this.

<Project>
  <ItemGroup Condition=" '$(LineupDesignTime)' != 'true' ">
    <PackageReference Update="Newtonsoft.Json" Version="9.0.1" IsImplicitlyDefined="true" />
  </ItemGroup>
</Project>

IsImplicitlyDefined will be set to true. This disables the ability to update the PackageReference from Visual Studio NuGet GUI.

The items are conditioned on LineupDesignTime. KoreBuild set's this property when examining projects so that it can find the original value of the PackageReference items as specified in *.csproj, and not the updated value that a previous restore has generated.

Clone this wiki locally