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

add package cli support for CPM projects #4681

Closed
wants to merge 31 commits into from

Conversation

pragnya17
Copy link
Contributor

@pragnya17 pragnya17 commented Jun 15, 2022

Bug

Fixes: NuGet/Home#11890

Regression? No.

Description

This feature is based on the design spec: NuGet/Home#11873. The goal was to allow the dotnet add package command to be executed for projects that were onboarded to CPM. Previously, the command threw a NU1008 error whenever packages were attempted to be added to projects onboarded to CPM. However, with this change users will now be able to add or update package references in projects onboarded to CPM. Directory.Packages.props files and project files are now modified accordingly whenever a package is added/updated.

PR Checklist

  • PR has a meaningful title

  • PR has a linked issue.

  • Described changes

  • Tests

    • Automated tests added
    • Functional test that checks if a package version is added to the props file correctly when there are no existing package versions in the props file already. It also checks that the package reference is added to the project file and that it does not have the package version metadata included in it.
    • Functional test that checks if a package version is added to the props file correctly when there already other existing package versions in the props file. It also checks that the package reference is added to the project file and that it does not have the package version metadata included in it.
    • Functional test that checks if a package version is updated with the correct package version attribute in the Directory.Packages.props file.
  • Documentation

@pragnya17 pragnya17 requested a review from a team as a code owner June 15, 2022 21:40
@kartheekp-ms kartheekp-ms changed the title Dev pragnya17 add package cli support for cpm projects add package cli support for CPM projects Jun 15, 2022
@@ -26,7 +26,7 @@ public static void AddOrUpdateDependency(PackageSpec spec, PackageDependency dep

if (!existing.Any())
{
AddDependency(spec.Dependencies, dependency.Id, range);
AddDependency(spec.Dependencies, dependency.Id, range, spec.RestoreMetadata?.CentralPackageVersionsEnabled ?? false);
Copy link
Contributor Author

@pragnya17 pragnya17 Jun 15, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All the modifications in this file are already in review based on @jeffkl PR (#4642) so I will remove these changes once his PR is merged into dev.

@@ -253,6 +253,102 @@ public void AddOrUpdateDependency_ToSpecificFrameworks_AddsNewDependency()
spec.TargetFrameworks[1].Dependencies[0].LibraryRange.VersionRange.MinVersion);
}

[Fact]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All the modifications in this file are already in review based on @jeffkl PR (#4642) so I will remove these changes once his PR is merged into dev.

AddPackageReferenceIntoPropsItemGroup(propsItemGroup, libraryDependency);

// Save the updated props file.
directoryBuildPropsRootElement.Save();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm very curious if formatting is preserved. I think there might be something special we have to do to get the MSBuild APIs to do this. Can you test it out with a Directory.Packages.props that has some special formatting like:

<Project>
  <PropertyGroup>
    <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
  </PropertyGroup>
  <ItemGroup>
    <!-- Does the Version attribute on the new line get preserved when we update it? -->
    <PackageVersion Include="SomePackage"
                    Version="1.0.0" />
  </ItemGroup>
</Project>

Copy link
Contributor Author

@pragnya17 pragnya17 Jun 20, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we are no longer updating the package version in the props file and rather adding a VersionOverride attribute to the project file instead, I think this no longer needs to be addressed. But please let me know if there are similar concerns with updating the VersionOverride attribute (I don't think there are because we are updating the project file not the props file, but I could be wrong).
EDIT: after discussion during our PR review meeting, we decided that this was still an issue that needed to be investigated and documented.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After further investigation, it seems that formatting is not preserved in the Directory.Packages.props file when something is modified (ex: a PackageVersion is added). I remember you mentioned that there is a flag that you can set in order to preserve the formatting, where might I be able to find that?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using ProjectRootElement.PreserveFormatting to preserve formatting. I didn't try this API :)

jeffkl and others added 24 commits June 21, 2022 14:52
@pragnya17 pragnya17 force-pushed the dev-pragnya17-AddPackageCLISupportForCPMProjects branch from a47da65 to 2be0195 Compare June 21, 2022 22:09
{
// Only add the package reference information using the PACKAGE_REFERENCE_TYPE_TAG.
ProjectItemElement item = itemGroup.AddItem(PACKAGE_REFERENCE_TYPE_TAG, libraryDependency.Name);
string packageVersion = "No package version added because this project is onboarded to CPM";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding strings to the resources file so that they can be localized. We can add a new entry in the resource file for CPM projects. For example, info : Adding PackageReference for package 'Newtonsoft.Json' into project 'C:\Users\user1\source\repos\ConsoleApp9\ConsoleApp9\ConsoleApp9.csproj' and Version into the Directory.Packages.Props file

@kartheekp-ms
Copy link
Contributor

kartheekp-ms commented Jun 22, 2022

There is another scenario we should consider. When add package command is executed with --no-restore switch then we should account for CPM onboarded project. We can also add a test for this scenario.

if (packageReferenceArgs.NoRestore)
{
packageReferenceArgs.Logger.LogWarning(string.Format(CultureInfo.CurrentCulture,
Strings.Warn_AddPkgWithoutRestore));
VersionRange versionRange = default;
if (packageReferenceArgs.NoVersion)
{
versionRange = packageReferenceArgs.Prerelease ?
VersionRange.Parse("*-*") :
VersionRange.Parse("*");
}
else
{
versionRange = VersionRange.Parse(packageReferenceArgs.PackageVersion);
}
var libraryDependency = new LibraryDependency
{
LibraryRange = new LibraryRange(
name: packageReferenceArgs.PackageId,
versionRange: versionRange,
typeConstraint: LibraryDependencyTarget.Package)
};
msBuild.AddPackageReference(packageReferenceArgs.ProjectPath, libraryDependency);
return 0;
}

{
// Get the Directory.Packages.props path.
string directoryPackagesPropsPath = project.GetPropertyValue("DirectoryPackagesPropsPath");
Logger.LogInformation("Modifying the Directory.Packages.props file at: " + directoryPackagesPropsPath);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using resource file for managing strings. The advantage is that the strings in the resource file will be localized.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to expand on @kartheekp-ms's suggestion, the rule is that every string that is user facing should be localized, and come from a resource file.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A suggestion for the message would be Modifying {0}.

It'll be obvious from the name that it's a directory.build.props

else
{
// MOdify the project file with version override.
UpdatePackageReferenceItemsWithVersionOverride(project, libraryDependency);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should pass existingPackageReferences to this method to avoid computing package references again.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

existingPackageReferences is only in a specific project I believe, whereas I am trying to search the whole solution for existing package references. Please let me know if I am misinterpreting that though.

Comment on lines +196 to +202
if (packageVersion == null)
{
// Modifying the props file if project is onboarded to CPM.
AddPackageVersionIntoItemGroupCPM(project, libraryDependency);
//Modify the project file.
AddPackageReferenceIntoItemGroupCPM(itemGroup, libraryDependency);
}
Copy link
Contributor

@kartheekp-ms kartheekp-ms Jun 22, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should split this logic into two categories.

if (packageVersion == null)
{
// add package version to the props file.
}

if (!existingPackageReferences.Any())
{
// add package reference to the csproj file
// add the version override only if package version in the props file is different from the version passed to the add package command. 
}
else
{
// add/update version override for the existing package references 
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a little confused as to what is being proposed. Is the change meant to be for all the if/else branches present?

Copy link
Member

@nkolev92 nkolev92 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did one quick pass.

I'll a more in depth review once the spec is updated so I know what to review :D

AddPackageReferenceIntoItemGroup(itemGroup, libraryDependency);
if (!existingPackageReferences.Any())
{
//Modify the project file.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some of these seem like helper comments but sometimes add clutter, especially when they are describe 1 method call.

/// </summary>
/// <param name="libraryDependency">Package Dependency of the package to be added.</param>
/// <param name="item">Item to add the metadata to.</param>
private void AddExtraMetadata(LibraryDependency libraryDependency, ProjectItemElement item)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

naming: Probably want to call out what metadata that is.

Example: AddFlagsMetadata.

{
// Get the Directory.Packages.props path.
string directoryPackagesPropsPath = project.GetPropertyValue("DirectoryPackagesPropsPath");
Logger.LogInformation("Modifying the Directory.Packages.props file at: " + directoryPackagesPropsPath);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to expand on @kartheekp-ms's suggestion, the rule is that every string that is user facing should be localized, and come from a resource file.

{
// Get the Directory.Packages.props path.
string directoryPackagesPropsPath = project.GetPropertyValue("DirectoryPackagesPropsPath");
Logger.LogInformation("Modifying the Directory.Packages.props file at: " + directoryPackagesPropsPath);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A suggestion for the message would be Modifying {0}.

It'll be obvious from the name that it's a directory.build.props

@pragnya17
Copy link
Contributor Author

Closing this PR as the design spec has changed.

@pragnya17 pragnya17 closed this Jun 23, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Feature]: add or update package versions in the Directory.Packages.props file for CPM projects
5 participants