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 developer testing design proposal #10386

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from
Open

Conversation

samcragg
Copy link

No description provided.

@zivkan
Copy link
Member

zivkan commented Feb 6, 2021

@samcragg I'm sorry that this slipped below my radar :(

What you've described is functionality similar to "fallback folders", with the exception that fallback folders have lower precedence than the global packages folder. I can think of at least two differences. First, only PackageReference supports fallback folders, not packages.config. Second, packages are not deleted from the global packages folder when they exist in the fallback folder. Instead, they're just used from the fallback folder, in the same way that packages in the global packages folder are used.

Given the scenario for this proposed feature is for new package/version development and testing, it does not seem to be a valid scenario that the package should already exist in the global packages folder.

Therefore, I propose that this design's outcome could be achieved with existing features:

  1. nuget.config, or RestoreFallbackFolders MSBuild property, to set fallback folder.
  2. After packing the nupkg to be tested, use nuget.exe add to extract the package to a folder in the "v3 folder structure" that the global packages folder & fallback folder uses
  3. Have the test project use a PackageReference with the appropriate version of the package.

Currently, there is no dotnet cli equivalent to nuget.exe add. Once a folder contains a package in the v3 structure, dotnet nuget push will add new packages in the same structure, but unfortunately dotnet nuget push on an empty folder defaults to a v2 folder structure, which does not work as a fallback folder.

Would this workflow make this proposal redundant?

@zkat as you're the current pack area owner, I'm adding assigning this to you for community PR shepherding. Please help us avoid forgetting about this again.

@zivkan zivkan added the Community PRs (and linked Issues) created by someone not in the NuGet team label Feb 6, 2021
@samcragg
Copy link
Author

samcragg commented Feb 7, 2021

Thanks for the feedback, if no changes are required then all the better!

Just zooming out from what we (as in me and the other developers in the linked issues in the proposal) are trying to achieve is to be able to test a package before publishing it. This needs to take into account that a version of the package already exists on nuget.org and that the exact version number cannot be specified in the project used to test it (we want to test the package we just built and not have to keep updating a version number in a project every time we build).

If I understand your work around then I don't think it would meet that criteria, as you need to specify the version of the package that was just built. It also requires quite a bit of effort to manually keep the fallback folder with the correct files in it. It also means immediately after publishing a package on nuget you would then have to make another commit to bump the version number again to avoid the locally built package being ignored because it has the same version as the nuget.org one.

I feel this will fit in nicely with the Improve inner-loop performance for .NET developers theme for .NET 6. At the moment, there are a handful of NuGet issues all around the same theme of using the local version of the package over NuGet and none of them have a satisfactory solution yet. If there really is a lack of desire for any changes in this area to help package developers, could a sample be created and documented of the best practices for testing a package locally?

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.

Thanks for starting this discussion!

Just zooming out from what we (as in me and the other developers in the linked issues in the proposal) are trying to achieve is to be able to test a package before publishing it. This needs to take into account that a version of the package already exists on nuget.org and that the exact version number cannot be specified in the project used to test it (we want to test the package we just built and not have to keep updating a version number in a project every time we build).

I wonder if adding PackageReference metadata that points to the local nupkg file is a better alternative?
NuGet could expand that in the obj directory instead of the global packages folder. Changing things in the global packages folder doesn't make it project local anymore.

in the context of trying to test a newly created package:

* NuGet caches packages by ID and version
* The order of multiple sources are searched through is non-deterministic
Copy link
Member

Choose a reason for hiding this comment

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

https://docs.microsoft.com/en-us/nuget/concepts/package-installation-process covers this.

File feeds are preferred over http feeds.

Copy link
Author

@samcragg samcragg Feb 13, 2021 •

Choose a reason for hiding this comment

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

True if the version is specified, however, when no version is specified then both are still checked. See #10367

searching any other sources.
* Otherwise, normal resolution happens.

When the override package is used, it is restored to the cache as normal (taking
Copy link
Member

Choose a reason for hiding this comment

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

Note that no-cache has nothing to do with the global packages folder, which is an installation directory.
It refers to the http-cache.

Copy link
Author

Choose a reason for hiding this comment

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

Here I was trying to refere to the packages folder (either global or where specifed by the --packages option). Is there a better term for this (packages folder perhaps, though that could be the taken as the source packages folder)?

Copy link
Member

Choose a reason for hiding this comment

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

When you specify --packages you are overriding the global packages folder value, so I think using global packages folder and packages folder in this context is good.

etc). This prevents changes to the rest of the restore process and doesn't
affect being able to publish a different package of the same version to the
override folder, as the version will be automatically cleaned during each
restore (i.e. although it's installed in the cache, it will be removed from it
Copy link
Member

Choose a reason for hiding this comment

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

Given that restore is an implicit gesture in so many workflows, this could be un-performant.

Restore has a quick and cheap concept of no-op where the up to dateness of the project is affected by project versions + extracted packages only (warm VS optimistically skips the extracted packages check).

Alternatively it could be something that it neatly implemented based on pacakge timestamps? I'd need to mull over this.

Copy link
Author

Choose a reason for hiding this comment

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

Good point, would it help if I limited the scope that this solution is only meant to allow testing of a local package (i.e. should be used as part of a build, package, test-pacakge cycle)?

(i.e. there cannot be multiple override folders to avoid ordering issues). Note
that file shares _should_ be supported, however.

Also, only a single version of a package (based on its ID) may exist in the
Copy link
Member

Choose a reason for hiding this comment

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

Is the idea to overwrite many packages at once?

Copy link
Member

Choose a reason for hiding this comment

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

How do we handle transitive dependencies?
Would those be expected to be in that local folder?

I think package versions have to matter for transitive dependencies, because this behavior breaks when you have 3rd party dependencies to the package being developed.

Copy link
Author

Choose a reason for hiding this comment

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

I was hoping this would be handled for free by limiting it to a single version of that package in the hypothetical override folder. When looking for dependencies, the same logic would apply, look in the override folder first and then fallback to others. I don't think versioning would matter in this case - if you've specified a version and it doesn't exist in the override folder then the normal logic applies. If you specify a version that does exist in the override folder, then that is used without checking any other sources. If you're not using an exact version but the version that exists in the override folder exists fits the bill, it will be used.

Bear in mind the override folder is really only meant to be used for testing a package that you've just built locally. I don't think other uses should be supported as it will add to complexity. But obviously, my opinion is biased 🙂

affect being able to publish a different package of the same version to the
override folder, as the version will be automatically cleaned during each
restore (i.e. although it's installed in the cache, it will be removed from it
again the next time the restore process starts).
Copy link
Member

Choose a reason for hiding this comment

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

I think a neater approach instead of deleting packages would be to have an additional folder where these packages are consumed from.

Deleting packages from the global packages folder automatically can be tricky because multiple projects share the global packages folder.

Copy link
Author

Choose a reason for hiding this comment

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

Yep, that sounds good. I personally use a different folder to restore to (via the --packages option), so we could further limit the override to only work under that scenario if it helps?


| Property | Description |
|-----------------------|--------------|
| RestoreOverrideFolder | Specifies a folder that contains `nupkg` files that take precedence over any other source and are not cached. |
Copy link
Member

Choose a reason for hiding this comment

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

The one concern I have with a project, instead of a package metadata driven solution is this doesn't work transitively.

Not sure it should be a complete blocker, but it is something that's a concern.

Copy link
Author

Choose a reason for hiding this comment

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

To be honest, I'll let you lot specify the exact mechanism 🙂 This way seemed the least impacting, but if you want another option when running the restore command or to have it in a separate file then I certainly wouldn't be offended.

@zivkan
Copy link
Member

zivkan commented Feb 10, 2021 •

If there really is a lack of desire for any changes in this area to help package developers, could a sample be created and documented of the best practices for testing a package locally?

Claiming there's a "lack of desire for any changes" is a completely unfair characterization. Feels like a strawman argument. I'm trying to have a conversation to see how we can improve the spec/feature/workflow/experience, rather than accepting the first proposal as-is.

The spec doesn't say what the expected "end-to-end" workflow would be, but if you want to be able to edit a line of code in the product, and then run dotnet test on the test project, this proposal wouldn't work with the rest of the design for how the .NET build process works. When dotnet test is run, or running a test in VS's Test Explorer, first NuGet restore on the affected projects/solution. At this time the proposal's implementation would check for the package in the global packages folder, delete it, then restore from the previous build's package. Then the product (& test) is compiled, and finally the test project is executed. If this test is run on a CI system, or a clean machine where the nupkg doesn't already exist, the restore might fail, preventing the build & execute from even happening.

This proposal only works in the "edit code, then run test" scenario if the build chain is changed to restore projects after project references are compiled, which would be a massive design change in NuGet and would have significant performance impact given PackageReference's transitive nature, not just on packages, but also project references. Being able to restore all projects in the solution in parallel is a significant performance gain.

If we're only trying to improve "edit code, pack nupkg, then run test" workflow, then we can investigate whether fallback folders, something equivalent to nuget.exe add or nuget.exe init after the "pack" and before "run test" will be acceptable to populate the test project's fallback folder. This can probably be automated today using a MSBuild target with RunAfter="Pack". With the exception that today fallback folders have lower precedence than the global packages folder (which shouldn't be an issue when trying to develop a new version of a package that doesn't exist on any package source yet), I believe it's all possible with existing features. It's a question of how to make it easier to use.

Regarding not needing to update version numbers in the test project all the time, I believe there are existing features, at least for PackageReference. How ever you're generating the version number for the package version, whether that's Nerdback.GitVersioning, or a custom script that sets a MSBuild property on the command line, you can use <PackageReference Include="MyPackage" Version="$(MyPackageVersion)" />. Some thought needs to go into how this would work in Visual Studio, but from a CI build script point of view, this solution already works "out of the box".

@zkat
Copy link
Contributor

zkat commented Feb 10, 2021

cc @JonDouglas @anangaur for visibility :)

@samcragg
Copy link
Author

Thanks for the response @zivkan . First of all, appologies if my comment came off as argumentative, that wasn't my intention. To clarify, if no changes are required to NuGet than all the better as far as I'm concerned 🙂 That sentance was my attempt to offer an olive branch of what I would consider an acceptable alternative solution; since there have been multiple issues raised around a similar theme and they all have workarounds proposed (including this PR has a proposed workaround), I would happily accept just a sample project that I could look at that showcases existing functionality. Hope that helps to clairify a little.

For your question around the scenario I'm considering, it's specifically there doesn't appear to be a good way of doing the "edit code, pack nupkg, then run test" cycle. This is what I really want to achieve - given a package that I've just produced, can I be certain that when an end user restores it that the relavant MSBuild tasks that I've packaged will do the correct thing on the destination project?

Would it help if I created a minimal solution to demonstrate this? I appreciate it is quite a niche requirement, but it does appear I'm not the only one wanting to do this.

@jeffkl
Copy link
Contributor

jeffkl commented Feb 15, 2022

Team Review: How much does this new package provide in this area?
https://devblogs.microsoft.com/dotnet/package-validation/

@nkolev92
Copy link
Member

@jeffkl

I think that feature is more complementary instead of replacing the ask here.

@dominoFire dominoFire added the internal-debugging For bot related testing label May 19, 2022
@msftbot msftbot bot added the Status:No recent activity No recent activity. label Sep 26, 2023
@msftbot
Copy link

msftbot bot commented Sep 26, 2023

This PR has been automatically marked as stale because it has no activity for 30 days. It will be closed if no further activity occurs within another 330 days of this comment. If it is closed, you may reopen it anytime when you're ready again, as long as you don't delete the branch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Community PRs (and linked Issues) created by someone not in the NuGet team internal-debugging For bot related testing Status:No recent activity No recent activity.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants