diff --git a/src/Cake.NuGet/Cake.NuGet.csproj b/src/Cake.NuGet/Cake.NuGet.csproj index b7c5eaaaf0..62477d9f91 100644 --- a/src/Cake.NuGet/Cake.NuGet.csproj +++ b/src/Cake.NuGet/Cake.NuGet.csproj @@ -19,6 +19,7 @@ + diff --git a/src/Cake.NuGet/Install/NuGetExtensionLocator.cs b/src/Cake.NuGet/Install/NuGetExtensionLocator.cs new file mode 100644 index 0000000000..bd0698b34c --- /dev/null +++ b/src/Cake.NuGet/Install/NuGetExtensionLocator.cs @@ -0,0 +1,120 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +using Cake.Core; +using Cake.Core.Configuration; + +using NuGet.Configuration; + +namespace Cake.NuGet.Install +{ + /// + /// Provides a common facility for locating extensions + /// + internal sealed class NuGetExtensionLocator : IExtensionLocator // shamelessly copied from nuget source repo + { + private readonly ICakeConfiguration _config; + private readonly ICakeEnvironment _environment; + + private static readonly string _extensionsDirectoryRoot = + Path.Combine(Environment.GetFolderPath( + Environment.SpecialFolder.LocalApplicationData), + "NuGet", + "Commands"); + + private static readonly string _credentialProvidersDirectoryRoot = + Path.Combine(Environment.GetFolderPath( + Environment.SpecialFolder.LocalApplicationData), + "NuGet", + "CredentialProviders"); + + private static readonly string CredentialProviderPattern = "CredentialProvider*.exe"; + + /// + /// PATH Enviroment variable name for path to nuget extensions + /// + public static readonly string ExtensionsEnvar = "NUGET_EXTENSIONS_PATH"; + + /// + /// PATH Enviroment variable name for path to nuget credentials providers + /// + public static readonly string CredentialProvidersEnvar = "NUGET_CREDENTIALPROVIDERS_PATH"; + + public NuGetExtensionLocator(ICakeEnvironment environment, ICakeConfiguration config) + { + _environment = environment ?? throw new ArgumentNullException(nameof(environment)); + _config = config ?? throw new ArgumentNullException(nameof(config)); + } + + /// + public IEnumerable FindCredentialProviders() + { + var customPaths = ReadPathsFromEnvar(CredentialProvidersEnvar); + return FindAll( + _credentialProvidersDirectoryRoot, + customPaths, + CredentialProviderPattern, + CredentialProviderPattern + ); + } + + /// + public IEnumerable FindExtensions() + { + var customPaths = ReadPathsFromEnvar(ExtensionsEnvar); + return FindAll( + _extensionsDirectoryRoot, + customPaths, + "*.dll", + "*Extensions.dll" + ); + } + + private IEnumerable FindAll( + string globalRootDirectory, + IEnumerable customPaths, + string assemblyPattern, + string nugetDirectoryAssemblyPattern) + { + var directories = new List(); + + // Add all directories from the environment variable if available. + directories.AddRange(customPaths); + + // add the global root + directories.Add(globalRootDirectory); + + var paths = new List(); + foreach (var directory in directories.Where(Directory.Exists)) + { + paths.AddRange(Directory.EnumerateFiles(directory, assemblyPattern, SearchOption.AllDirectories)); + } + + // not sure if the working directory is an appropriate fallback for locating the tools/folder nuget/folder + var nugetDirectory = Path.GetDirectoryName(_config.GetToolPath(_environment.WorkingDirectory, _environment).FullPath); // directory for locating extensions and credentials providers, such as VSTS CredentialProvider + if (nugetDirectory == null) + { + return paths; + } + + paths.AddRange(Directory.EnumerateFiles(nugetDirectory, nugetDirectoryAssemblyPattern)); + + return paths; + } + + private static IEnumerable ReadPathsFromEnvar(string key) + { + var result = new List(); + var paths = Environment.GetEnvironmentVariable(key); + if (!string.IsNullOrEmpty(paths)) + { + result.AddRange( + paths.Split(new[] { ';' }, + StringSplitOptions.RemoveEmptyEntries)); + } + return result; + } + } +} diff --git a/src/Cake.NuGet/Install/NuGetPackageInstaller.cs b/src/Cake.NuGet/Install/NuGetPackageInstaller.cs index 4f4d968553..7e35e6acc2 100644 --- a/src/Cake.NuGet/Install/NuGetPackageInstaller.cs +++ b/src/Cake.NuGet/Install/NuGetPackageInstaller.cs @@ -6,14 +6,16 @@ using System.Collections.Generic; using System.Linq; using System.Threading; -using System.Threading.Tasks; + using Cake.Core; using Cake.Core.Configuration; using Cake.Core.Diagnostics; using Cake.Core.IO; using Cake.NuGet.Install.Extensions; + using NuGet.Common; using NuGet.Configuration; +using NuGet.Credentials; using NuGet.Frameworks; using NuGet.PackageManagement; using NuGet.Packaging; @@ -21,6 +23,7 @@ using NuGet.Protocol.Core.Types; using NuGet.Resolver; using NuGet.Versioning; + using PackageReference = Cake.Core.Packaging.PackageReference; using PackageType = Cake.Core.Packaging.PackageType; @@ -131,6 +134,10 @@ public IReadOnlyCollection Install(PackageReference package, PackageType var resolutionContext = new ResolutionContext(dependencyBehavior, includePrerelease, false, VersionConstraints.None, _gatherCache, _sourceCacheContext); var downloadContext = new PackageDownloadContext(_sourceCacheContext); + // WIP + var extensionLocator = new NuGetExtensionLocator(_environment, _config); + var pluginCredentialProviderBuilder = new PluginCredentialProviderBuilder(extensionLocator, _nugetSettings, _nugetLogger); + var credentialProviders = pluginCredentialProviderBuilder.BuildAll("verbose"); // First get the install actions. // This will give us the list of packages to install, and which feed should be used.