Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revised proposal for Adding dotnet CLI support for projects onboarded to CPM #11873

Merged
merged 17 commits into from
Jun 17, 2022
133 changes: 110 additions & 23 deletions proposed/2022/cpm-dotnet-cli-add-package-support.md
Original file line number Diff line number Diff line change
@@ -1,29 +1,46 @@
# Adding Dotnet CLI add package command support to projects onboarded with Central Package Management

Author: Pragnya Pandrate <br>
Issue: https://github.com/NuGet/Home/issues/11807 <br>
Status: In Review <br>
- Author: [Pragnya Pandrate](https://github.com/pragnya17), [Kartheek Penagamuri](https://github.com/kartheekp-ms)
- Issue: [11807](https://github.com/NuGet/Home/issues/11807)
- Status: In Review

## Problem Background

The dotnet add package command allows users to add or update a package reference in a project file through the Dotnet CLI. However, when this command is used in a project that has been onboarded to Central Package Management (CPM), it poses an issue as this error is thrown: `error: NU1008: Projects that use central package version management should not define the version on the PackageReference items but on the PackageVersion items: [PackageName]`.

Projects onboarded to CPM use a `Directory.packages.props` file in the root of the repo where package versions are defined centrally. Ideally, when the `dotnet add package` command is used, the package version should only be added to the corresponding package in the `Directory.packages.props` file. However, currently the command attempts to add the package version to the `<PackageReference />` in the project which conflicts with the CPM requirements that package versions must only be in the `Directory.packages.props` file.
Projects onboarded to CPM use a `Directory.packages.props` file in the root of the repo where package versions are defined centrally. Ideally, when the `dotnet add package` command is used, the package version should only be added to the corresponding package in the `Directory.packages.props` file. However, currently the command attempts to add the package version to the `<PackageReference />` in the project which conflicts with the CPM requirements that package versions must only be in the `Directory.packages.props` file.

## Goals

The main goal is to add support for `dotnet add package` to be used with projects onboarded onto CPM. Regardless of whether the package has already been added to the project or not, the command should allow users to add packages or update the package version in the `Directory.packages.props` file.

## Customers

Users wanting to use CPM onboarded projects and dotnet CLI commands.

## Solutions
## Solution

When `dotnet add package` is executed in a project onboarded to CPM (meaning that the `Directory.packages.props` file exists) there are a few scenarios that must be considered.

| Scenario # | PackageReference exists? | Directory.Packages.Props file exists? | Is ManagePackageVersionsCentrally property set to true? | Is VersionOverride? | Current behavior | New behavior in dotnet CLI | In Scope |
| ---- |-----| -----|-----|----------|----------|----------|----------|
| 1 | ❌ | ✔️ | ✔️ (in Directory.Packages.Props or .csproj file) | ❌ | Restore failed with NU1008 error and NO edits were made to the csproj file (same in VS and dotnet CLI) | `PackageReference` should be added to .(cs/vb)proj file and `PackageVersion` should be added to the closest `Directory.Packages.Props` file | ✔️ |
| 2 | ❌ | ❌ | ✔️ in (cs/vb)proj file | ❌ | Restore failed with NU1008 error and NO edits were made to the csproj file (same in VS and dotnet CLI) | `PackageReference` and `Version` should be added to .(cs/vb)proj file.| ✔️ |
nkolev92 marked this conversation as resolved.
Show resolved Hide resolved
| 3 | ❌ | ✔️ | ❌ | ❌ | `PackageReference` and `Version` added to .(cs/vb)proj file. | In addition to the current behavior `Directory.packages.props` file should be deleted if it exists in the project folder | ❌ |
nkolev92 marked this conversation as resolved.
Show resolved Hide resolved
| 4 | ✔️ | ✔️ | ✔️ | ❌ | **dotnet CLI** - Restore failed with NU1008 error and NO edits were made to the csproj file. **VS** - Clicked on Update package in PM UI. New `PackageVersion` added to csproj file and `Directory.packages.props` file was not updated | No changes should be made to the `Directory.Packages.Props` file. Add `VersionOverride` attribute to the existing `PackageReference` item in .(cs/vb)proj file. If `VersionOverride` is already specified then the value should be updated.|✔️ [More Info](https://github.com/NuGet/Home/pull/11849#discussion_r890639808) |
| 5 | ✔️ | ✔️ | ✔️ | ✔️ | **dotnet CLI** - Restore failed with NU1008 error and NO edits were made to the csproj file. **VS** - Clicked on Update package in PM UI. New `PackageVersion` added to csproj file and `VersionOverride` in .csproj file was not updated | Update `VersionOverride` attribute value in the corresponding `PackageReference` item in .(cs/vb)proj file. | ✔️ |

> [!NOTE]
> Scenarios with multiple Directory.Packages.props are out of scope for now.

### 1. The package reference does not exist

If the package does not already exist, it should be added along with the appropriate package version to `Directory.packages.props`. The package version should either be the latest version or the one specified in the CLI command. Only the package name (not the version) should be added to `<PackageReference>` in the project file.

#### Before `add package` is executed:<br>
#### Before `add package` is executed

The props file:

The props file: <br>
```xml
<Project>
<PropertyGroup>
Expand All @@ -33,7 +50,9 @@ The props file: <br>
</ItemGroup>
</Project>
```
The .csproj file: <br>

The .csproj file:

```xml
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
Expand All @@ -47,9 +66,12 @@ The .csproj file: <br>
</Project>
```

#### After `add package` is executed:<br>
#### After `add package` is executed

`dotnet add ToDo.csproj package Newtonsoft.Json -v 13.0.1`

The props file:

The props file: <br>
```xml
<Project>
<PropertyGroup>
Expand All @@ -60,7 +82,9 @@ The props file: <br>
</ItemGroup>
</Project>
```
The .csproj file: <br>

The .csproj file:

```xml
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
Expand Down Expand Up @@ -88,15 +112,19 @@ Repository
```

In the above example, the following scenarios are possible:

1. Project1 will evaluate the Directory.Packages.props file in the Repository\Solution1\ directory.
2. Project2 will evaluate the Directory.Packages.props file in the Repository\ directory.

***Sourced from https://devblogs.microsoft.com/nuget/introducing-central-package-management/
***Sourced from <https://devblogs.microsoft.com/nuget/introducing-central-package-management/>

### 2. The package reference does exist

If the package already exists in `Directory.packages.props` the version should be updated in `Directory.packages.props`. The package version should either be the latest version or the one specified in the CLI command. The `<PackageReference>` in the project file should not change.

#### Before `add package` is executed:<br>
#### Before `add package` is executed
The props file:

```xml
<Project>
<PropertyGroup>
Expand All @@ -108,27 +136,86 @@ If the package already exists in `Directory.packages.props` the version should b
</Project>
```

#### After `add package` is executed:<br>
The .csproj file:

```xml
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Newtonsoft.Json"/>
</ItemGroup>
</Project>
```

#### After `add package` is executed

`dotnet add ToDo.csproj package Newtonsoft.Json -v 13.0.1`

No changes should be made to the `Directory.Packages.Props` file. Add `VersionOverride` attribute to the existing `PackageReference` item in .(cs/vb)proj file. If `VersionOverride` is already specified then the value should be updated.

The .csproj file:

```xml
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Newtonsoft.Json" VersionOverride="13.0.1"/>
</ItemGroup>
</Project>
```

### 3. The package reference does exist with an VersionOverride

If the package already exists in `Directory.packages.props` the version should be updated in `Directory.packages.props`. The package version should either be the latest version or the one specified in the CLI command. The `<PackageReference>` in the project file should not change.

#### Before `add package` is executed

```xml
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Newtonsoft.Json" Version="13.0.1"/>
<PackageVersion Include="Newtonsoft.Json" Version="11.0.1"/>
</ItemGroup>
</Project>
```

In case there are multiple `Directory.packages.props` files in the repo, all of them should be searched for a matching package reference. Once a matching package reference is found, its package version can be updated appropriately.
```xml
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Newtonsoft.Json" VersionOverride="12.0.1"/>
</ItemGroup>
</Project>
```

### Other Scenarios that can be considered
#### After `add package` is executed

While the above cases must be considered to solve the issue, there are a few other cases that can be considered to make the product more usable:
- No changes should be made to the `Directory.packages.props` file.
- The version specified in the `VersionOverride` atribute value of `PackageReference` element in the project file should be updated.

1. If the project has the `<ManagePackageVersionsCentrally>` set to true, but the `Directory.packages.props` file has not been created yet the file should be created. At this point, package references can be updated according to the cases discussed above.
2. If the `Directory.packages.props` file has been created but the `<ManagePackageVersionsCentrally>` property has not been set to true, it should be set to true.
3. If the project has the `<ManagePackageVersionsCentrally>` set to false, the project should not be considered as onboarded to CPM. Therefore the `Directory.packages.props` file should be deleted if it exists and only `.csproj` files should be modified.
4. If there are `Directory.packages.props` files that inherit other props files, all inherited files should be considered when `add package` is executed.
5. If the project has a `VersionOverride` attribute defined in the `csproj` file and the `add package` command is run in order to update a package version, then the `VersionOverride` attribute should be updated with the appropriate package version.
`dotnet add ToDo.csproj package Newtonsoft.Json -v 13.0.1`

```xml
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Newtonsoft.Json" VersionOverride="13.0.1"/>
</ItemGroup>
</Project>
```