Skip to content

Instantly share code, notes, and snippets.

@emgarten
Created January 22, 2017 20:34
Show Gist options
  • Save emgarten/d2fe22d0cb28d110c23f13980a441770 to your computer and use it in GitHub Desktop.
Save emgarten/d2fe22d0cb28d110c23f13980a441770 to your computer and use it in GitHub Desktop.
Find all packages on nuget.org that depend on the target package id.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using NuGet.CatalogReader;
using NuGet.Common;
using NuGet.Packaging.Core;
using NuGet.Protocol;
using NuGet.Protocol.Core.Types;
namespace ConsoleApp
{
/// <summary>
/// Add this code to a new console app.
/// Install-Package https://www.nuget.org/packages/NuGet.CatalogReader/1.2.0
///
/// This script finds all packages that depend on the given package id.
/// For nuget.org this script runs through all 88K ids which may take
/// some time. To check multiple packages this script could be modified to
/// create an index of all dependencies for easy look up later.
/// </summary>
class Program
{
static void Main(string[] args)
{
// Find all packages that depend on this package.
var targetId = "NuGet.Versioning";
FindParentPackagesAsync(targetId).Wait();
}
static async Task FindParentPackagesAsync(string targetId)
{
var parents = new HashSet<PackageIdentity>();
var feed = new Uri("https://api.nuget.org/v3/index.json");
var repository = Repository.Factory.GetCoreV3(feed.AbsoluteUri, FeedType.HttpV3);
var dependencyInfo = await repository.GetResourceAsync<DependencyInfoResource>();
var threads = 8;
ServicePointManager.DefaultConnectionLimit = 64;
using (var catalog = new CatalogReader(feed, TimeSpan.FromHours(4)))
{
// Read the v3 feed.
Console.WriteLine($"Reading {feed.AbsoluteUri}");
// Find all unique ids
var ids = (await catalog.GetFlattenedEntriesAsync())
.Select(e => e.Id)
.Distinct(StringComparer.OrdinalIgnoreCase)
.OrderBy(s => s, StringComparer.OrdinalIgnoreCase)
.ToArray();
var tasks = new Dictionary<Task<IEnumerable<RemoteSourceDependencyInfo>>, string>();
// Loop through all ids found in the feed.
for (int i = 0; i < ids.Length; i++)
{
// Throttle
if (tasks.Count == threads)
{
await CompleteTaskAsync(targetId, parents, tasks);
}
var id = ids[i];
// Get dependencies for all versions of the package.
var task = Task.Run(async () => await dependencyInfo.ResolvePackages(id, NullLogger.Instance, CancellationToken.None));
tasks.Add(task, id);
Console.WriteLine($"[{i}] Checking {id}");
}
// Wait for all tasks to complete
while (tasks.Count > 0)
{
await CompleteTaskAsync(targetId, parents, tasks);
}
}
Console.WriteLine("=======[Parent packages]=======");
foreach (var parent in parents.Select(e => e.Id)
.Distinct(StringComparer.OrdinalIgnoreCase)
.OrderBy(s => s, StringComparer.OrdinalIgnoreCase))
{
Console.WriteLine($"{parent} -> {targetId}");
}
}
// Read the result
private static async Task CompleteTaskAsync(string targetId,
HashSet<PackageIdentity> parents,
Dictionary<Task<IEnumerable<RemoteSourceDependencyInfo>>, string> tasks)
{
var task = await Task.WhenAny(tasks.Keys);
var packageId = tasks[task];
tasks.Remove(task);
var packages = Enumerable.Empty<RemoteSourceDependencyInfo>();
try
{
packages = task.Result;
}
catch (Exception ex)
{
// Ignore errors due to invalid packages on the feed.
Console.WriteLine($"Feed error: {packageId} {ex.Message}");
}
parents.UnionWith(GetParents(packages, targetId));
}
// Find all packages which depend on the target package.
static IEnumerable<PackageIdentity> GetParents(IEnumerable<RemoteSourceDependencyInfo> packageInfos,
string targetId)
{
return packageInfos.Where(packageInfo => packageInfo.DependencyGroups.Any(group =>
group.Packages.Any(e => targetId.Equals(e.Id, StringComparison.OrdinalIgnoreCase))))
.Select(e => e.Identity);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment