diff --git a/.gitignore b/.gitignore index 1c9a181..0d415a3 100644 --- a/.gitignore +++ b/.gitignore @@ -240,3 +240,5 @@ ModelManifest.xml # FAKE - F# Make .fake/ + +.idea/ diff --git a/NugetDependencyDownloader/Logger.cs b/NugetDependencyDownloader/Logger.cs new file mode 100644 index 0000000..b1e7397 --- /dev/null +++ b/NugetDependencyDownloader/Logger.cs @@ -0,0 +1,27 @@ +using System; +using System.Threading.Tasks; +using NuGet.Common; + +namespace NuGetDependencyDownloader +{ + public class Logger : ILogger + { + public void LogDebug(string data) => Console.WriteLine($@"DEBUG: {data}"); + public void LogVerbose(string data) => Console.WriteLine($@"VERBOSE: {data}"); + public void LogInformation(string data) => Console.WriteLine($@"INFORMATION: {data}"); + public void LogMinimal(string data) => Console.WriteLine($@"MINIMAL: {data}"); + public void LogWarning(string data) => Console.WriteLine($@"WARNING: {data}"); + public void LogError(string data) => Console.WriteLine($@"ERROR: {data}"); + public void LogInformationSummary(string data) => Console.WriteLine($@"DEBUG: {data}"); + + public void Log(LogLevel level, string data) => Console.WriteLine($@"{level}: {data}"); + + public async Task LogAsync(LogLevel level, string data) => Console.WriteLine($@"{level.ToString()}: {data}"); + + public void Log(ILogMessage message) => Console.WriteLine(message); + + public async Task LogAsync(ILogMessage message) => Console.WriteLine(message); + + public void LogSummary(string data) => Console.WriteLine($@"SUMMARY: {data}"); + } +} \ No newline at end of file diff --git a/NugetDependencyDownloader/MachineWideSettings.cs b/NugetDependencyDownloader/MachineWideSettings.cs new file mode 100644 index 0000000..ffeeea3 --- /dev/null +++ b/NugetDependencyDownloader/MachineWideSettings.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using NuGet.Common; +using NuGet.Configuration; + +namespace NuGetDependencyDownloader +{ + public class MachineWideSettings : IMachineWideSettings + { + private readonly Lazy> _settings; + + public MachineWideSettings() + { + var baseDirectory = NuGetEnvironment.GetFolderPath(NuGetFolderPath.MachineWideConfigDirectory); + _settings = new Lazy>( + () => new List {global::NuGet.Configuration.Settings.LoadMachineWideSettings(baseDirectory)}); + } + + public IEnumerable Settings => _settings.Value; + + ISettings IMachineWideSettings.Settings => _settings.Value.FirstOrDefault(); + } +} \ No newline at end of file diff --git a/NugetDependencyDownloader/MainForm.Designer.cs b/NugetDependencyDownloader/MainForm.Designer.cs index 3b62210..944df4e 100644 --- a/NugetDependencyDownloader/MainForm.Designer.cs +++ b/NugetDependencyDownloader/MainForm.Designer.cs @@ -148,7 +148,6 @@ private void InitializeComponent() this.MinimumSize = new System.Drawing.Size(600, 500); this.Name = "MainForm"; this.Text = "NuGet Dependency Scraper"; - this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MainForm_FormClosing); this.ResumeLayout(false); this.PerformLayout(); diff --git a/NugetDependencyDownloader/MainForm.cs b/NugetDependencyDownloader/MainForm.cs index f03c144..0135428 100644 --- a/NugetDependencyDownloader/MainForm.cs +++ b/NugetDependencyDownloader/MainForm.cs @@ -1,32 +1,23 @@ using System; -using System.ComponentModel; -using System.Linq; +using System.IO; +using System.Text; +using System.Threading; +using System.Threading.Tasks; using System.Windows.Forms; namespace NuGetDependencyDownloader { public partial class MainForm : Form { - private SlidingBuffer _consoleBuffer = new SlidingBuffer(1000); - private BackgroundWorker _worker; - private PackageTool _packageTool; - private bool _closePending; + private Task _downloadNuGet = null; + private CancellationTokenSource _downloadCancelToken = null; public MainForm() { InitializeComponent(); - } - private void MainForm_FormClosing(object sender, FormClosingEventArgs e) - { - if (_worker != null && _worker.IsBusy) - { - _closePending = true; - _worker.CancelAsync(); - e.Cancel = true; - Enabled = false; - return; - } + TextBoxWriter writer = new TextBoxWriter(textBoxActivity); + Console.SetOut(writer); } private void btnStart_Click(object sender, EventArgs e) @@ -39,61 +30,51 @@ private void btnStart_Click(object sender, EventArgs e) private void btnStop_Click(object sender, EventArgs e) { - ShowActivity("Stop requested."); + Console.WriteLine(@"Stop requested."); btnStop.Enabled = false; - _worker.CancelAsync(); + _downloadCancelToken.Cancel(); } - private void StartWork() + private async void StartWork() { - _worker = new BackgroundWorker - { - WorkerSupportsCancellation = true, - WorkerReportsProgress = true - }; - _worker.DoWork += new DoWorkEventHandler(DoWork); - _worker.ProgressChanged += new ProgressChangedEventHandler(Progress); - _worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(EndWork); + _downloadCancelToken = new CancellationTokenSource(); + _downloadNuGet = NuGetManager.Downloader(textBoxPackage.Text, textBoxVersion.Text, checkBoxPrerelease.Checked, _downloadCancelToken.Token); - _packageTool = new PackageTool(); - _packageTool.StopRequested = () => _worker.CancellationPending; - _packageTool.Progress = (x) => _worker.ReportProgress(0, x); - - _worker.RunWorkerAsync(); + try { + await _downloadNuGet; + } catch(InvalidOperationException) + { + } catch(OperationCanceledException) + { + } + btnStart.Enabled = true; + btnStop.Enabled = false; } + } - private void Progress(object sender, ProgressChangedEventArgs e) + public class TextBoxWriter : TextWriter + { + // The control where we will write text. + private Control MyControl; + public TextBoxWriter(Control control) { - ShowActivity((string)e.UserState); + MyControl = control; } - private void EndWork(object sender, RunWorkerCompletedEventArgs e) + public override void Write(char value) { - btnStop.Enabled = false; - btnStart.Enabled = true; - - _packageTool = null; - _worker = null; - - if (_closePending) Close(); - _closePending = false; + MyControl.Text += value; } - private void DoWork(object sender, DoWorkEventArgs e) + public override void Write(string value) { - _packageTool.ProcessPackage(textBoxPackage.Text, textBoxVersion.Text, checkBoxPrerelease.Checked); + MyControl.Text += value; } - private void ShowActivity(string text) + public override Encoding Encoding { - _consoleBuffer.Add(text); - - textBoxActivity.Lines = _consoleBuffer.ToArray(); - textBoxActivity.Focus(); - textBoxActivity.SelectionStart = textBoxActivity.Text.Length; - textBoxActivity.SelectionLength = 0; - textBoxActivity.ScrollToCaret(); - textBoxActivity.Refresh(); + get => Encoding.Unicode; } + } } diff --git a/NugetDependencyDownloader/NuGetDependencyDownloader.csproj b/NugetDependencyDownloader/NuGetDependencyDownloader.csproj index ee48f48..4f3dc16 100644 --- a/NugetDependencyDownloader/NuGetDependencyDownloader.csproj +++ b/NugetDependencyDownloader/NuGetDependencyDownloader.csproj @@ -9,7 +9,7 @@ Properties NuGetDependencyDownloader NuGetDependencyDownloader - v4.6.2 + v4.7.2 512 true @@ -34,16 +34,84 @@ 4 - - ..\packages\Microsoft.Web.Xdt.2.1.1\lib\net40\Microsoft.Web.XmlTransform.dll + + + ..\packages\Microsoft.Web.Xdt.2.1.2\lib\net40\Microsoft.Web.XmlTransform.dll True - - ..\packages\NuGet.Core.2.12.0\lib\net40-Client\NuGet.Core.dll + + ..\packages\morelinq.3.2.0\lib\net451\MoreLinq.dll + True + + + + ..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll + True + + + ..\packages\NuGet.Commands.5.3.0\lib\net472\NuGet.Commands.dll + True + + + ..\packages\NuGet.Common.5.3.0\lib\net472\NuGet.Common.dll + True + + + ..\packages\NuGet.Configuration.5.3.0\lib\net472\NuGet.Configuration.dll + True + + + ..\packages\NuGet.Credentials.5.3.0\lib\net472\NuGet.Credentials.dll + True + + + ..\packages\NuGet.DependencyResolver.Core.5.3.0\lib\net472\NuGet.DependencyResolver.Core.dll + True + + + ..\packages\NuGet.Frameworks.5.3.0\lib\net472\NuGet.Frameworks.dll + True + + + ..\packages\NuGet.LibraryModel.5.3.0\lib\net472\NuGet.LibraryModel.dll + True + + + ..\packages\NuGet.PackageManagement.5.3.0\lib\net472\NuGet.PackageManagement.dll + True + + + ..\packages\NuGet.Packaging.5.3.0\lib\net472\NuGet.Packaging.dll + True + + + ..\packages\NuGet.ProjectModel.5.3.0\lib\net472\NuGet.ProjectModel.dll + True + + + ..\packages\NuGet.Protocol.5.3.0\lib\net472\NuGet.Protocol.dll + True + + + ..\packages\NuGet.Resolver.5.3.0\lib\net472\NuGet.Resolver.dll + True + + + ..\packages\NuGet.Versioning.5.3.0\lib\net472\NuGet.Versioning.dll True + + + + + + + + ..\packages\System.ValueTuple.4.4.0\lib\net47\System.ValueTuple.dll + True + @@ -55,16 +123,17 @@ + + Form MainForm.cs + - - MainForm.cs diff --git a/NugetDependencyDownloader/NuGetManager.cs b/NugetDependencyDownloader/NuGetManager.cs new file mode 100644 index 0000000..88975c8 --- /dev/null +++ b/NugetDependencyDownloader/NuGetManager.cs @@ -0,0 +1,118 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using MoreLinq.Extensions; +using NuGet.Configuration; +using NuGet.PackageManagement; +using NuGet.Packaging; +using NuGet.Packaging.Core; +using NuGet.ProjectManagement; +using NuGet.Protocol.Core.Types; +using NuGet.Resolver; +using NuGet.Protocol; +using NuGet.Versioning; +using NuGet.Packaging.Signing; + +namespace NuGetDependencyDownloader +{ + public static class NuGetManager + { + private static readonly Logger Logger = new Logger(); + private static readonly PackageSource PackageSource = new PackageSource("https://api.nuget.org/v3/index.json"); + private static readonly SourceCacheContext SourceCacheContext = new SourceCacheContext(); + private static readonly string RootPath = $"{Directory.GetCurrentDirectory()}\\Download"; + private static readonly string PackagesPath = RootPath; + + private static readonly ISettings ThisSettings = + Settings.LoadDefaultSettings(RootPath, null, new MachineWideSettings()); + + private static readonly NuGetProject ThisProject = new FolderNuGetProject(RootPath); + private static SourceRepository SourceRepository { get; } + private static ISourceRepositoryProvider SourceRepositoryProvider { get; } + private static NuGetPackageManager PackageManager { get; } + private static SourceRepository SourceRepositories { get; } + + static NuGetManager() + { + var providers = new List>(); + providers.AddRange(Repository.Provider.GetCoreV3()); + SourceRepository = new SourceRepository(PackageSource, providers); + + SourceRepositoryProvider = new SourceRepositoryProvider(new PackageSourceProvider(ThisSettings), providers); + + PackageManager = + new NuGetPackageManager(SourceRepositoryProvider, ThisSettings, PackagesPath) + { + PackagesFolderNuGetProject = (FolderNuGetProject) ThisProject + }; + + + SourceRepositories = SourceRepositoryProvider.CreateRepository(PackageSource); // See part 2 + } + + + public static async Task Downloader(string id, string version, bool includePrerelease, + CancellationToken downloadCancelToken) + { + Console.WriteLine($@"Download directory: {Directory.GetCurrentDirectory()}\Download"); + + const bool allowUnlisted = false; + + var resolutionContext = new ResolutionContext( + DependencyBehavior.HighestMinor, includePrerelease, allowUnlisted, VersionConstraints.None); + + var packageMetadataResource = + await SourceRepository.GetResourceAsync(downloadCancelToken); + + var searchMetadata = await packageMetadataResource.GetMetadataAsync(id, includePrerelease, true, + SourceCacheContext, Logger, downloadCancelToken); + + if (searchMetadata.Count() == 0) + { + Console.WriteLine("None was found"); + return; + } + + IPackageSearchMetadata package; + try + { + var nugetVersion = NuGetVersion.Parse(version); + package = searchMetadata.First(a => a.Identity.Version == nugetVersion); + } + catch (ArgumentException) + { + package = searchMetadata.MaxBy(a => a.Identity.Version).First(); + } + + Console.WriteLine($@"Start downloading {package.Identity.Id} {package.Identity.Version}"); + + var identity = new PackageIdentity(package.Identity.Id, package.Identity.Version); + + var projectContext = new EmptyNuGetProjectContext + { + OperationId = new Guid(), + ActionType = NuGetActionType.Install, + PackageExtractionContext = new PackageExtractionContext( + PackageSaveMode.Nupkg, + XmlDocFileSaveMode.Skip, + ClientPolicyContext.GetClientPolicy(ThisSettings, Logger), + Logger + ) + }; + + var downloadContext = new PackageDownloadContext(SourceCacheContext, PackagesPath, true); + + await PackageManager.InstallPackageAsync(PackageManager.PackagesFolderNuGetProject, + identity, resolutionContext, projectContext, downloadContext, SourceRepositories, + Array.Empty(), // This is a list of secondary source respositories, probably empty + downloadCancelToken); + if (!downloadCancelToken.IsCancellationRequested) + { + Console.WriteLine($@"{package.Identity.Id} {package.Identity.Version} downloaded"); + } + } + } +} \ No newline at end of file diff --git a/NugetDependencyDownloader/PackageTool.cs b/NugetDependencyDownloader/PackageTool.cs deleted file mode 100644 index 4fdaf10..0000000 --- a/NugetDependencyDownloader/PackageTool.cs +++ /dev/null @@ -1,203 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net; -using NuGet; - -namespace NuGetDependencyDownloader -{ - // Good examples - // http://blog.nuget.org/20130520/Play-with-packages.html - - public class PackageTool - { - private IList _packages = new List(); - - public Func StopRequested { get; set; } - public Action Progress { get; set; } - - public void ProcessPackage(string packageId, string packageVersion, bool preRelease) - { - CollectPackages(packageId, packageVersion, preRelease); - - if (StopRequested()) - { - Progress("Stopped."); - return; - } - - Progress(string.Format("{0} packages to download.", _packages.Count)); - - DownloadPackages(); - - if (StopRequested()) - { - Progress("Stopped."); - return; - } - - Progress("Done."); - } - - private void CollectPackages(string packageId, string packageVersion, bool preRelease) - { - IPackage package; - if (string.IsNullOrWhiteSpace(packageVersion)) - { - package = GetLatestPackage(packageId, preRelease); - } - else - { - SemanticVersion version; - try - { - version = SemanticVersion.Parse(packageVersion); - } - catch (ArgumentException) - { - Progress("Unable to parse package version."); - return; - } - package = GetPackages(packageId, preRelease) - .Where(o => o.Version == version) - .FirstOrDefault(); - } - - if (package == null) - { - Progress("Package not found."); - return; - } - - _packages.Add(package); - Progress(package.GetFullName()); - LoadDependencies(package, preRelease); - } - - private void LoadDependencies(IPackage package, bool preRelease) - { - if (package.DependencySets != null) - { - var dependencies = package.DependencySets - .SelectMany(o => o.Dependencies.Select(x => new Dependency { Id = x.Id, VersionSpec = x.VersionSpec })) - .ToList(); - - foreach (Dependency dependency in dependencies) - { - if (StopRequested()) - return; - - IQueryable packages = GetPackages(dependency.Id, preRelease); - IPackage depPackage = GetRangedPackageVersion(packages, dependency.VersionSpec); - Progress(string.Format("{0} -> {1}", package.GetFullName(), depPackage.GetFullName())); - - if (!IsPackageKnown(depPackage)) - { - _packages.Add(depPackage); - LoadDependencies(depPackage, preRelease); - } - } - } - } - - private void DownloadPackages() - { - if (!Directory.Exists("download")) - Directory.CreateDirectory("download"); - - foreach (IPackage package in _packages) - { - if (StopRequested()) - return; - - string fileName = string.Format("{0}.{1}.nupkg", package.Id.ToLower(), package.Version); - if (File.Exists("download\\" + fileName)) - { - Progress(string.Format("{0} already downloaded.", fileName)); - continue; - } - - Progress(string.Format("downloading {0}", fileName)); - using (var client = new WebClient()) - { - DataServicePackage dsp = (DataServicePackage)package; - client.DownloadFile(dsp.DownloadUrl, "download\\" + fileName); - } - } - } - - private IQueryable GetPackages(string packageId, bool includePreRelease) - { - IPackageRepository repo = PackageRepositoryFactory.Default.CreateRepository("https://packages.nuget.org/api/v2"); - IQueryable packages = repo.FindPackagesById(packageId).AsQueryable(); - - if (!includePreRelease) - { - packages = packages.Where(item => (item.IsReleaseVersion() == true)); - } - - return packages; - } - - private IPackage GetLatestPackage(string packageId, bool includePrerelease) - { - IQueryable packages = GetPackages(packageId, includePrerelease); - - if (!includePrerelease) - { - packages = packages - .Where(item => item.IsReleaseVersion() == true) - .Where(o => o.IsLatestVersion); - } - - IPackage latest = packages.OrderByDescending(o => o.Version).FirstOrDefault(); - - return latest; - } - - private IPackage GetRangedPackageVersion(IQueryable packages, IVersionSpec versionSpec) - { - if (versionSpec.MinVersion != null) - { - if (versionSpec.IsMinInclusive) - { - packages = packages.Where(o => o.Version >= versionSpec.MinVersion); - } - else - { - packages = packages.Where(o => o.Version > versionSpec.MinVersion); - } - } - - if (versionSpec.MaxVersion != null) - { - if (versionSpec.IsMaxInclusive) - { - packages = packages.Where(o => o.Version <= versionSpec.MaxVersion); - } - else - { - packages = packages.Where(o => o.Version < versionSpec.MaxVersion); - } - } - - IPackage package = packages - .OrderByDescending(o => o.Version) - .FirstOrDefault(); - - return package; - } - - private bool IsPackageKnown(IPackage package) - { - return _packages.Any(o => o.Title == package.Title && o.Version == package.Version); - } - - private class Dependency - { - public string Id { get; set; } - public IVersionSpec VersionSpec { get; set; } - } - } -} diff --git a/NugetDependencyDownloader/SlidingBuffer.cs b/NugetDependencyDownloader/SlidingBuffer.cs deleted file mode 100644 index 019ecc9..0000000 --- a/NugetDependencyDownloader/SlidingBuffer.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System.Collections; -using System.Collections.Generic; - -namespace NuGetDependencyDownloader -{ - // Taken from http://stackoverflow.com/questions/6392516 - - public class SlidingBuffer : IEnumerable - { - private readonly Queue _queue; - private readonly int _maxCount; - - public SlidingBuffer(int maxCount) - { - _maxCount = maxCount; - _queue = new Queue(maxCount); - } - - public void Add(T item) - { - if (_queue.Count == _maxCount) - _queue.Dequeue(); - _queue.Enqueue(item); - } - - public IEnumerator GetEnumerator() - { - return _queue.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - public int Count - { - get { return _queue.Count; } - } - } -} diff --git a/NugetDependencyDownloader/packages.config b/NugetDependencyDownloader/packages.config index 4eb1f4b..596d8f3 100644 --- a/NugetDependencyDownloader/packages.config +++ b/NugetDependencyDownloader/packages.config @@ -1,5 +1,19 @@  - - + + + + + + + + + + + + + + + + \ No newline at end of file