Home: InstallPackageAsync not installing the packages

Created on 26 Oct 2020  路  7Comments  路  Source: NuGet/Home

await CapNuGetPackageManager.InstallPackageAsync (CapNuGetPackageManager.PackagesFolderNuGetProject , identity , resolutionContext , projectContext , CapNugetFeed , Array.Empty(), CancellationToken.None);
This does not install the nuget project in the target project which I am calling from another windows form setting the path in FolderNuGetProject. It does download the nuget packages in packages folder but does not install it

Below is the sample code after this comment

SDK Question NeedsRepro WaitingForCustomer

Most helpful comment

@bksmart Please do not mention a list of folks just to get attention to your github issue (particularly after just 4 hours). You may mention one or two people perhaps if you know they are particularly relevant to the issue.
I for one do not work on the nuget feature. Please start by trusting the nuget team's triage of new issues and wait to see what happens. It can take several days sometimes for an active team to triage an issue on an active repo.

All 7 comments

using NuGet.Common;
using NuGet.Configuration;
using NuGet.PackageManagement;
using NuGet.Packaging;
using NuGet.Packaging.Core;
using NuGet.Packaging.Signing;
using NuGet.ProjectManagement;
using NuGet.Protocol;
using NuGet.Protocol.Core.Types;
using NuGet.Resolver;
using NuGet.Versioning;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Xml.Linq;

namespace WindowsFormsApp1
{
public partial class PackageInstallation : Form
{
public PackageInstallation()
{
InitializeComponent();
}

    private void btnViewPackages_Click(object sender, EventArgs e)
    {
        CapNuGetManager capNugetManager = new CapNuGetManager();
        Dictionary<string, List<NuGetVersion>> capPackages = Task.Run(() => capNugetManager.ListPackagesAsync()).Result;

        foreach (KeyValuePair<string, List<NuGetVersion>> item in capPackages)
        {
            lstPackates.Items.Add(item.Key);
        }
    }

    private void btnInstallSelected_Click(object sender, EventArgs e)
    {
        foreach (object item in lstPackates.SelectedItems)
        {
            InstallCapPackage(item.ToString());
        }
    }

    private void InstallCapPackage(string packageId)
    {
        CapNuGetManager capNugetManager = new CapNuGetManager();
        PackageIdentity package = Task.Run(() => capNugetManager.GetPackage(packageId)).Result;
        List<PackageIdentity> packageIdentities = new List<PackageIdentity>
        {
            package
        };
        IEnumerable<PackageIdentity> installPackageResult = Task.Run(() => capNugetManager.TryInstallPackagesAsync(packageIdentities)).Result;

        //var packageReferenceFile = new PackageReferenceFile(string.Format("{0}/{1}", packageConfigPath, "packages.config"));

        foreach (var item in packageIdentities)
        {
            try
            {

            }
            catch (Exception ex)
            {

            }

        }

    }


}
public class CapNuGetManager
{
    internal const string capPrivateRepoUrl = "http://localhost/Cap.NugetServer/nuget";
    internal const string packageConfigPath = @"C:\Users\ravi.keshwani\source\repos\CapNugetConsumerTest\";

    public SourceRepository CapNugetFeed { get; private set; }
    public NuGetPackageManager CapNuGetPackageManager { get; private set; }

    public NugetSettings Settings { get; private set; }

    private readonly List<Lazy<INuGetResourceProvider>> providers = new List<Lazy<INuGetResourceProvider>>(Repository.Provider.GetCoreV3());

    public CapNuGetManager()
    {
        Settings = new NugetSettings();
        SourceItem source = Settings.UserSettings.GetSection("packageSources").Items
            .OfType<SourceItem>()
            .FirstOrDefault(item => item.Key.Contains("Cap.PrivateServer")) as SourceItem;

        PackageSource packageSource = new PackageSource(source.Value);

        CapNugetFeed = new SourceRepository(packageSource, providers);
        PackageSourceProvider packageSourceProvider = new PackageSourceProvider(Settings.Settings);
        SourceRepositoryProvider sourceRepositoryProvider = new SourceRepositoryProvider(packageSourceProvider, providers);
        string packagesPath = @"C:\Users\ravi.keshwani\source\repos\CapNugetConsumerTest\packages";
        string localPackageCachePath = packagesPath;// Path.Combine(NuGetEnvironment.GetFolderPath(NuGetFolderPath.NuGetHome), "packages");

        CapNuGetPackageManager = new NuGetPackageManager(sourceRepositoryProvider
            , Settings.Settings, packagesPath)
        {
            PackagesFolderNuGetProject = new FolderNuGetProject(packagesPath),

        };



    }

    public async Task<PackageIdentity> GetPackage(string packageId)
    {
        List<IPackageSearchMetadata> packageResults = await PackageSearchResults(packageId);
        foreach (IPackageSearchMetadata result in packageResults)
        {
            if (result.Identity.Id == packageId)
            {
                return result.Identity;
            }
        }

        return null;
    }

    public async Task<List<IPackageSearchMetadata>> PackageSearchResults(string packageId)
    {
        ListResource listResource = await CapNugetFeed.GetResourceAsync<ListResource>();

        IEnumerableAsync<IPackageSearchMetadata> allPackages = await listResource.ListAsync(packageId, false, true, false, NullLogger.Instance, CancellationToken.None);
        IEnumeratorAsync<IPackageSearchMetadata> enumerator = allPackages.GetEnumeratorAsync();
        List<IPackageSearchMetadata> searchResults = new List<IPackageSearchMetadata>();
        while (true)
        {
            bool moved = await enumerator.MoveNextAsync();
            if (!moved)
            {
                break;
            }

            if (enumerator.Current == null)
            {
                break;
            }

            searchResults.Add(enumerator.Current);
        }

        return searchResults;
    }

    public async Task<Dictionary<string, List<NuGetVersion>>> ListPackagesAsync()
    {
        Dictionary<string, List<NuGetVersion>> retVal = new Dictionary<string, List<NuGetVersion>>();
        List<IPackageSearchMetadata> searchResults = await PackageSearchResults(string.Empty);

        foreach (IPackageSearchMetadata result in searchResults)
        {
            if (!retVal.ContainsKey(result.Identity.Id))
            {
                retVal.Add(result.Identity.Id, new List<NuGetVersion>());
            }

            retVal[result.Identity.Id].Add(result.Identity.Version);
        }

        return retVal;
    }

    public async Task<bool> PerformInstallation(PackageIdentity identity)
    {
        try
        {
            ResolutionContext resolutionContext = new ResolutionContext(
                DependencyBehavior.Highest,
                true,
                false,
                VersionConstraints.ExactMajor | VersionConstraints.ExactMinor);

            INuGetProjectContext projectContext = new BlankProjectContext(Settings.Settings, NullLogger.Instance);
            projectContext.PackageExtractionContext.CopySatelliteFiles = true;


            await CapNuGetPackageManager.InstallPackageAsync
                  (CapNuGetPackageManager.PackagesFolderNuGetProject
                  , identity
                  , resolutionContext
                  , projectContext
                  , CapNugetFeed
                  , Array.Empty<SourceRepository>(),
                  CancellationToken.None);

            return true;
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }

    public async Task<IEnumerable<PackageIdentity>> TryInstallPackagesAsync(IEnumerable<PackageIdentity> packageIdentities)
    {
        HashSet<PackageIdentity> allPackagesInstalled = new HashSet<PackageIdentity>();
        await Task.WhenAll(packageIdentities.Select(async installIdentity =>
        {
            HashSet<PackageIdentity> installedPkgs = new HashSet<PackageIdentity>();
            try
            {

                installedPkgs = await InstallPackageAndDependenciesAsync(installIdentity);

            }
            catch (Exception)
            { }


            foreach (PackageIdentity identity in installedPkgs)
            {
                try
                {
                    LocalPackageInfo pkg = LocalFolderUtility.GetPackage(new Uri(CapNuGetPackageManager.PackagesFolderNuGetProject.GetInstalledPackageFilePath(identity)), NullLogger.Instance);

                    if (pkg == null)
                    {
                        continue;
                    }

                    string directoryname = new FileInfo(pkg.Path).Directory.FullName;
                    allPackagesInstalled.Add(identity);


                }
                catch (Exception) { }
            }
        }));
        return allPackagesInstalled;
    }


    private async Task<HashSet<PackageIdentity>> InstallPackageAndDependenciesAsync(PackageIdentity identity)
    {
        bool allowUnlisted = false;
        bool includePrelease = true;
        try
        {
            if (CapNuGetPackageManager.PackageExistsInPackagesFolder(identity))
            {
                CapNuGetPackageManager.PackagesFolderNuGetProject.GetInstalledPath(identity);
                LocalPackageInfo pkg = LocalFolderUtility.GetPackage(new Uri(CapNuGetPackageManager.PackagesFolderNuGetProject.GetInstalledPackageFilePath(identity)), NullLogger.Instance);
                foreach (NuGet.Packaging.PackageDependencyGroup dep in pkg.GetReader().GetPackageDependencies())
                {
                    IEnumerable<string> ids = dep.Packages.Select(pkDep => pkDep.Id);
                }
            }
        }
        catch (Exception) { }

        try
        {
            ResolutionContext resolutionContext = new ResolutionContext(
                DependencyBehavior.Highest,
                includePrelease,
                allowUnlisted,
                VersionConstraints.ExactMajor | VersionConstraints.ExactMinor);

            INuGetProjectContext projectContext = new BlankProjectContext(Settings.Settings, NullLogger.Instance);
            projectContext.ActionType = NuGetActionType.Install;

            PackageSource nugetSource = new PackageSource("https://api.nuget.org/v3/index.json");
            SourceRepository additionalRepo = new SourceRepository(nugetSource, providers);

            IEnumerable<NuGetProjectAction> installActions =
            await CapNuGetPackageManager.PreviewInstallPackageAsync(
                CapNuGetPackageManager.PackagesFolderNuGetProject, identity,
                resolutionContext, projectContext,
                new[] { CapNugetFeed }, new[] { additionalRepo },
                CancellationToken.None);

            SourceCacheContext sourceCache = new SourceCacheContext();
            await CapNuGetPackageManager.ExecuteNuGetProjectActionsAsync
                (CapNuGetPackageManager.PackagesFolderNuGetProject
                    , installActions
                    , projectContext, sourceCache, CancellationToken.None);

            await CapNuGetPackageManager.InstallPackageAsync
                  (CapNuGetPackageManager.PackagesFolderNuGetProject
                  , identity
                  , resolutionContext
                  , projectContext
                  , CapNugetFeed
                  ,new[] { additionalRepo },
                  CancellationToken.None);

            return new HashSet<PackageIdentity>(installActions.Select(action => action.PackageIdentity));
        }
        catch (Exception)
        {
            throw;
        }
    }
}

public class NugetSettings : IMachineWideSettings
{
    private readonly Lazy<ISettings> _settings;
    public ISettings Settings => _settings.Value;

    private readonly Lazy<ISettings> _userSettings;
    public ISettings UserSettings => _userSettings.Value;


    public NugetSettings()
    {
        string baseDirectory = NuGetEnvironment.GetFolderPath(NuGetFolderPath.MachineWideConfigDirectory);
        _settings = new Lazy<ISettings>(() => NuGet.Configuration.Settings.LoadMachineWideSettings(baseDirectory));

        string userDirectory = NuGetEnvironment.GetFolderPath(NuGetFolderPath.UserSettingsDirectory);
        _userSettings = new Lazy<ISettings>(() => NuGet.Configuration.Settings.LoadMachineWideSettings(userDirectory));
    }
}

public class BlankProjectContext : INuGetProjectContext
{
    private NuGet.Common.ILogger Logger;

    public BlankProjectContext(ISettings settings, NuGet.Common.ILogger logger)
    {
        Logger = logger;

        ClientPolicyContext clientPolicy = ClientPolicyContext.GetClientPolicy(settings, logger);
        PackageExtractionContext = new PackageExtractionContext(PackageSaveMode.Defaultv2, XmlDocFileSaveMode.None, clientPolicy, logger);

    }

    public void Log(MessageLevel level, string message, params object[] args)
    {
        string formattedMessage = string.Format(message, args);
        switch (level)
        {
            case MessageLevel.Info: Logger.LogInformation(formattedMessage); break;
            case MessageLevel.Warning: Logger.LogWarning(formattedMessage); break;
            case MessageLevel.Debug: Logger.LogDebug(formattedMessage); break;
            case MessageLevel.Error: Logger.LogError(formattedMessage); break;
            default: Logger.LogInformationSummary(formattedMessage); break;
        }
    }

    public FileConflictAction ResolveFileConflict(string message)
    {
        Log(MessageLevel.Warning, message);
        return FileConflictAction.Ignore;
    }

    public void ReportError(string message)
    {
        Log(MessageLevel.Error, message);
    }

    public void Log(ILogMessage message)
    {
        string lg = message.ToString();
        switch (message.Level)
        {
            case LogLevel.Debug:
                Logger.LogDebug(lg); break;
            case LogLevel.Verbose:
                Logger.LogVerbose(lg); break;
            case LogLevel.Information:
                Logger.LogInformation(lg); break;
            case LogLevel.Minimal:
                Logger.LogMinimal(lg); break;
            case LogLevel.Warning:
                Logger.LogWarning(lg); break;
            case LogLevel.Error:
                Logger.LogError(lg); break;
            default:
                break;
        }
        return;
    }

    public void ReportError(ILogMessage message)
    {
        Log(message);
    }

    public PackageExtractionContext PackageExtractionContext { get; set; }
    public ISourceControlManagerProvider SourceControlManagerProvider { get; }
    public NuGet.ProjectManagement.ExecutionContext ExecutionContext { get; }
    public XDocument OriginalPackagesConfig { get; set; }
    public NuGetActionType ActionType { get; set; }
    public Guid OperationId { get; set; }
}

}

@zivkan please get this resolved as we want to achieve the installation of the nugets from the private nuget server setup from a tool to a target project where calling the InstallPackageAsync is not installing the package , nor it executes the transform files.

It downloads the packages and puts into the target packages folder but not performing the installs

@bksmart Please do not mention a list of folks just to get attention to your github issue (particularly after just 4 hours). You may mention one or two people perhaps if you know they are particularly relevant to the issue.
I for one do not work on the nuget feature. Please start by trusting the nuget team's triage of new issues and wait to see what happens. It can take several days sometimes for an active team to triage an issue on an active repo.

@bksmart As Andrew requested, please don't mass spam people with mentions. Most of the people you mentioned don't work on the NuGet team and therefore are very unlikely to know about this topic, which frankly nobody outside the NuGet team are likely to know. It's extremely specific and deep, and something very few people attempt.

A few comments:

  1. You're reporting that CapNuGetPackageManager.InstallPackageAsync doesn't work as you desire, but CapNuGetPackageManager is not a class that NuGet provides. I nearly replied that we don't support 3rd party APIs, but I noticed in the code you provided that it's the name of your property, which uses our NuGetPackageManager class.
  2. You posted a lot of code, so I can't quickly and easily see what NuGet APIs you're trying to use, but the code also don't compile as-is. GitHub allows you to attach zip files, so you can create a sample project that compiles and runs, delete any temporary files (bin\, obj\, .vs\, etc), and zip that up and attach it. If you can follow StackOverflow's suggestion about a minimal reproducible sample, that will make it much quicker for us to investigate, and therefore we're more likely to find the time to do so. A console app with a small Program.Main is the clearest to us what you're trying to do.
  3. FolderNuGetProject represents a solution packages folder when a project in a VS solution uses packages.config. FolderNuGetProject is unfortunately named, since it's not a project from a customer perspective, it just uses NuGet's "internal" project abstraction, so more of NuGet's internal code can be re-used. It's by design that it only extracts the package to the folder and does not modify any project file.
  4. NuGet uses BuildIntegratedNuGetProject as a general abstraction for projects that use PackageReference. It looks like we have PackagesConfigNuGetProject that represents just the packages.config file, but not the project using packages.config. From a quick search, I can't find the class that represents a csproj for a project that uses packages.config. However, none of the classes in NuGet.PackageManagement.dll modify project files. Visual Studio's design is such that only the project system component is "allowed" to modify project files. Therefore, NuGet has VS-specific classes that implement/extend our NuGet.PackageManagement.* project abstractions that use project system APIs to modify the project on package install/uninstall. Therefore, NuGet does not have code to modify project files. If you want to use NuGet.PackageManagement, you'll be responsible for writing your own code to modify the project file.
  5. It's not obvious to me if you're writing a Visual Studio extension, or a stand-alone app. If you're writing a stand-alone app, as mentioned in point 4 above, you need to write your own code to modify project files. If you're writing a VS extension, you should not use any of NuGet's dlls directly, you should use our VS Extensibility APIs. IVsPackageInstaller has APIs to tell NuGet to install a package in a project.

So, I think that this is "by design", but I'm not confident I understand your scenario. I my best guess is wrong and you can clarify, we can try to resolve it.

Thanks for the response @zivkan. I will take care on not mentioning people and I apologize for that. Ok let me explain you my goals and scope of the implementation I want to achieve.

  1. I have my features specific NuGet packages created with transform files attached to it which adds the entries in config files which work perfectly when adding the packages from the package manager IDE.

  2. I have hosted the packages on my private nuget server using Nuget.Server API.

  3. I want to distribute the packages based on the selection of reusable features of my custom development framework one needs to add in a Web , Web API , Console OR any of the project developed using dotnet frameworks.

  4. The selection of the features would be from a windows application and post selecting the features the associated NuGet packages whose mapping I have created should be installed automatically in the target project along with updating the project references , packages.config file and the entries in the config files respectively based on the target project one has opt to develop.

Eg. If a developer opts for "Authentication with Aspnet Identity" it should install my custom packages for Authentication, Token , Claims and should add the config entries for my sections along with references of assemblies as if it installs from the IDE.

  1. I have tried to build the implementation using Nuget.Core package which is deprecated now and is no longer supported but it has some of the issues which I could not fix which throws exceptions when the version of nuget.exe is conflicted when installing some of the external dependent nuget packages like Azure.BlobStorage etc.

  2. When studying more I figured out https://docs.microsoft.com/en-us/nuget/reference/nuget-client-sdk which provides such features and I tried to implement the code which I have shared above which downloads the packages into the target project but does not install it using InstallPackageAsync. I am struggling as there is very limited documentation and examples related to same on internet.

  3. My custom windows application will be used within my team and also by the external teams who wants to consume my packages to integrate in their projects with facilities like installing, updating and removing packages without getting more into understanding which package and its dependencies needs to be installed to implement a feature of our framework.

  4. Apart from this when over the time if any of the developer opts to update the package or wants to use restore feature of VS then a custom authentication UI should pop up where the developer needs to provide the credentials which should authenticate the developer on my custom authentication portal and will allow to perform the operation or restrict accordingly.

I would be really thankful to the Nuget team to guide me how to achieve this goal.

Thanks and Regards

@bksmart As mentioned in this doc (which maybe I should link to from our docs.microsoft.com page on the NuGet SDK), the NuGet's team's primary concern is the tooling experience. This is how the vast, vast majority of our customers interact with NuGet. The SDK for other people to add NuGet functionality into their own apps, is basically a side-product, for which we seem to have very few customers such as yourself. When prioritizing work by customer impact, the NuGet SDK always rates low, hence our lack of docs and samples. This year I've been trying to minimise API breaking changes and do new API design reviews, but there's only so much that can be done with the time provided.

Having said that, I don't understand your needs from a technical point of view. What you wrote in parts 1-7 make it sound like you're using NuGet packages at runtime in your own app, but then part 8 makes it sound like actually it's a .NET project, and you're trying to emulate Visual Studio's new project wizards in your windows app, so the packages are actually only used as part of a "normal" .NET project restore. This is also more in line with the sample code you provided, and trying to call NuGetPackageManager.InstallPackageAsync.

I have no experience writing new project wizards in VS, but I know that ASP.NET, ASP.NET Core and Service Fabric all have "custom" wizards. Perhaps if you can figure out how to make your own custom new project wizard in VS, you won't need to duplicate a bunch of what VS does. NuGet has some docs on how to get a new project template to automatically install packages on install: https://docs.microsoft.com/en-us/nuget/visual-studio-extensibility/visual-studio-templates. As I linked previously, we also have APIs in VS to install packages, which allows you to show a dialog to customers to choose and programatically get only the relevant packages installed. Finally, we also have docs on how to write a custom NuGet credential provider for VS. There are also docs on how to write a credential provider for nuget.exe and the dotnet cli, in case you want to go down that path, if you want to give your customers a more integrated experience than having to enter usernames and passwords in nuget.config files.

If you do want to use the NuGet SDK in your own app, my guess is what NuGet doesn't provide what you want: we don't have APIs in the NuGet SDK to modify a csproj, vbproj, fsproj or other project files.

For [cs|vb]proj that use packages.config, we have MSBuildNuGetProject. If you look at the implementation of InstallPackageAsync, you'll see it does modify the project to add references, content files and so on. However, it does so via a IMSBuildProjectSystem that you'll need to implement yourself to actually do the XML modifications to the MSBuild project file.

For PackageReference projects, we only have BuildIntegratedNuGetProject, which only has an abstract InstallPackagesAsync, so again, you'll need to implement the modification of the XML project file yourself.

It appears that in your sample code, you were only using FolderNuGetProject, which as I previously mentioned, is not actually a project, it's the solution packages folder, which MSBuildNuGetProject (which does represent a project file) contains an instance of.

Please reply if my previous comment didn't answer your questions, but as stated, I believe this is "by design". NuGet's SDK doesn't provide classes to modify csproj files.

Was this page helpful?
0 / 5 - 0 ratings