Пример #1
0
        static void AnalyzePackage(
            string packageId,
            string version,
            string framework,
            //ILogger logger,
            PackageSourceEnvironment packageSourceEnvironment)
        {
            var rootPackageIdentity = new PackageIdentity(packageId, NuGetVersion.Parse(version));
            var rootNuGetFramework  = NuGetFramework.ParseFolder(framework);

            // If configFileName is null, the user specific settings file is %AppData%\NuGet\NuGet.config.
            // After that, the machine wide settings files are added (c:\programdata\NuGet\Config\*.config).
            //var settings = Settings.LoadDefaultSettings(root: null, configFileName: null, machineWideSettings: null);
            //var sourceRepositoryProvider = new SourceRepositoryProvider(settings, Repository.Provider.GetCoreV3());

            // TODO: Configure packageSources from external config
            // TODO: Use environment variables for MyGet username/password!!!!!!
            // TODO: Make 3 different environments
            //      1. MyGetCi
            //      2. MyGet
            //      3. Brf

            string username = null, password = null;

            if (packageSourceEnvironment == PackageSourceEnvironment.MyGetCi ||
                packageSourceEnvironment == PackageSourceEnvironment.MyGet)
            {
                username = Environment.GetEnvironmentVariable("MYGET_USERNAME");
                if (string.IsNullOrEmpty(username))
                {
                    username = "******";
                }
                password = Environment.GetEnvironmentVariable("MYGET_PASSWORD");
                if (string.IsNullOrEmpty(password))
                {
                    throw new InvalidOperationException("Missing MYGET_PASSWORD environment variable.");
                }
            }

            PackageSourceProvider packageSourceProvider;

            switch (packageSourceEnvironment)
            {
            case PackageSourceEnvironment.MyGetCi:
                packageSourceProvider = new PackageSourceProvider(NullSettings.Instance, new []
                {
                    CreatePackageSource("https://api.nuget.org/v3/index.json", "NuGet.org v3"),
                    CreatePackageSource("https://www.myget.org/F/maxfire-ci/api/v3/index.json", "MaxfireCi"),
                    CreateAuthenticatedPackageSource("https://www.myget.org/F/brf-ci/api/v3/index.json", "BrfCiMyGet", username, password)
                });
                break;

            case PackageSourceEnvironment.MyGet:
                packageSourceProvider = new PackageSourceProvider(NullSettings.Instance, new []
                {
                    CreatePackageSource("https://api.nuget.org/v3/index.json", "NuGet.org v3"),
                    CreateAuthenticatedPackageSource("https://www.myget.org/F/brf/api/v3/index.json", "BrfMyGet", username, password)
                });
                break;

            case PackageSourceEnvironment.Brf:
                packageSourceProvider = new PackageSourceProvider(NullSettings.Instance, new []
                {
                    CreatePackageSource("https://api.nuget.org/v3/index.json", "NuGet.org v3"),
                    CreatePackageSource("http://pr-nuget/nuget", "Brf", protocolVersion: 2)
                });
                break;

            case PackageSourceEnvironment.NugetOrg:
            default:
                packageSourceProvider = new PackageSourceProvider(NullSettings.Instance, new []
                {
                    CreatePackageSource("https://api.nuget.org/v3/index.json", "NuGet.org v3")
                });
                break;
            }

            var sourceRepositoryProvider = new SourceRepositoryProvider(packageSourceProvider, Repository.Provider.GetCoreV3());

            Console.WriteLine("Feeds used:");
            foreach (var packageSource in packageSourceProvider.LoadPackageSources())
            {
                Console.WriteLine($"    {packageSource}");
            }
            Console.WriteLine();

            //var nugetLogger = logger.AsNuGetLogger();
            var nugetLogger = NullLogger.Instance;

            var tmpDirToRestoreTo = Path.Combine(Path.GetTempPath(), "packages");

            using (var cacheContext = new SourceCacheContext {
                NoCache = true
            })
            {
                var repositories     = sourceRepositoryProvider.GetRepositories();
                var resolvedPackages = new ConcurrentDictionary <PackageIdentity, SourcePackageDependencyInfo>(PackageIdentityComparer.Default);

                // Builds transitive closure
                // TODO: is Wait okay?
                ResolvePackageDependencies(rootPackageIdentity, rootNuGetFramework, cacheContext, nugetLogger, repositories, resolvedPackages).Wait();

                var resolverContext = new PackageResolverContext(
                    DependencyBehavior.Lowest,
                    targetIds: new[] { packageId },
                    availablePackages: new HashSet <SourcePackageDependencyInfo>(resolvedPackages.Values),
                    packageSources: sourceRepositoryProvider.GetRepositories().Select(s => s.PackageSource),
                    log: nugetLogger,
                    requiredPackageIds: Enumerable.Empty <string>(),
                    packagesConfig: Enumerable.Empty <PackageReference>(),
                    preferredVersions: Enumerable.Empty <PackageIdentity>());

                var resolver = new PackageResolver();
                List <SourcePackageDependencyInfo> prunedPackages;
                //try
                //{
                prunedPackages = resolver.Resolve(resolverContext, CancellationToken.None)
                                 .Select(id => resolvedPackages[id]).ToList();
                //}
                //catch (Exception ex)
                //{
                //    Console.WriteLine($"ERROR: {ex.Message}");
                //    return;
                //}

                Console.WriteLine($"root package identity: {rootPackageIdentity}");
                Console.WriteLine($"root target framework: {rootNuGetFramework.DotNetFrameworkName} ({rootNuGetFramework.GetShortFolderName()})");
                Console.WriteLine();

                var packageNodes = new Dictionary <string, PackageReferenceNode>(StringComparer.OrdinalIgnoreCase);

                //var builder = new DependencyGraph.Builder(rootNode);

                // TODO: problem that the graph is flattened!!!!!
                // TODO: Should we inspect the items (assemblies of each package). remember meta-packages contain other packages
                // TODO: dependencies are important

                Console.WriteLine("Vertices of dependency package graph:");
                Console.WriteLine();

                PackageReferenceNode rootPackage = null;

                // resolve contained assemblies of packages
                foreach (SourcePackageDependencyInfo target in prunedPackages)
                {
                    //target.Id
                    //target.Version
                    //target.Dependencies

                    // TODO: --no-cache, --packages $tmpDirToRestoreTo
                    var downloadResource = target.Source.GetResource <DownloadResource>();

                    // TODO: .Result of Async
                    var downloadResult = downloadResource.GetDownloadResourceResultAsync(
                        new PackageIdentity(target.Id, target.Version),
                        new PackageDownloadContext(cacheContext, directDownloadDirectory: tmpDirToRestoreTo, directDownload: true),
                        SettingsUtility.GetGlobalPackagesFolder(NullSettings.Instance),
                        nugetLogger,
                        CancellationToken.None).Result;

                    // items in lib folder of target (a package is a collection of assemblies)
                    var packageReader = downloadResult.PackageReader;
                    if (packageReader == null)
                    {
                        downloadResult.PackageStream.Seek(0, SeekOrigin.Begin);
                        packageReader = new PackageArchiveReader(downloadResult.PackageStream);
                    }

                    var libItems = packageReader.GetLibItems();

                    NuGetFramework nearest = libItems.Select(x => x.TargetFramework).GetNearestFrameworkMatching(rootNuGetFramework);

                    // assembly references is a sequence of assembly names (file name including the extension)
                    var assemblyReferences = libItems
                                             .Where(group => group.TargetFramework.Equals(nearest))
                                             .SelectMany(group => group.Items)
                                             .Where(itemRelativePath => Path.GetExtension(itemRelativePath).Equals(".dll", StringComparison.OrdinalIgnoreCase))
                                             .Select(Path.GetFileName);
                    //.Select(assemblyName => new AssemblyReferenceNode(assemblyName)); // we do not include assembly references in the graph

                    // TODO we ignore framework references in nuspec (only used by MS)
                    //var frameworkItems = packageReader.GetFrameworkItems();
                    //nearest = reducer.GetNearest(nugetFramework, frameworkItems.Select(x => x.TargetFramework));

                    //// TODO: Why not use Path.GetFileName here?
                    //var frameworkAssemblyReferences = frameworkItems
                    //    .Where(@group => @group.TargetFramework.Equals(nearest))
                    //    .SelectMany(@group => @group.Items)
                    //    .Select(Path.GetFileName); // Why
                    //    //.Select(assemblyName => new AssemblyReferenceNode(assemblyName)); // we do not include assembly references in the graph

                    //assemblyReferences = assemblyReferences.Concat(frameworkAssemblyReferences);

                    var packageReferenceNode = new PackageReferenceNode(target.Id, target.Version.ToString(),
                                                                        nearest.DotNetFrameworkName, nearest.GetShortFolderName(), assemblyReferences);

                    if (rootPackageIdentity.Equals(new PackageIdentity(target.Id, target.Version)))
                    {
                        if (rootPackage != null)
                        {
                            throw new InvalidOperationException("UNEXPECTED: Root package should be unique.");
                        }
                        rootPackage = packageReferenceNode;
                    }

                    Console.WriteLine($"    {packageReferenceNode}");

                    //builder.WithNode(packageReferenceNode);
                    //builder.WithNodes(assemblyReferences);

                    // TODO: Target package has edges to assembly nodes
                    //builder.WithEdges(assemblyReferences.Select(x => new Edge(packageReferenceNode, x)));

                    // TODO: Pack2Pack reference (directed vertex)
                    packageNodes.Add(target.Id, packageReferenceNode);
                }
                Console.WriteLine();

                // NOTE: We have transitive closure so all references are resolved!!!!
                // NOTE: The relation is A 'depends on' B shown like A ---> B
                // NOTE: The inverse relation is 'used by'....

                // TODO: How to represent digraph (DAG)
                // TODO: How to represent the topological order (evaluation order, traversal order)
                // TODO: A directed acyclic graph (DAG) with a single root is not a tree!!!!!
                // NOTE: Both trees and DAGs are connected, directed, rooted, and have no cycles
                //       so this means that starting from any node and going up the parents you will
                //       eventually work your way up to the top (root).
                //       However, since DAG nodes have multiple parents, there will be multiple paths
                //       on the way up (that eventually merge). This is like GIT history (DAG)
                // Another way to see it is Tree is like single class inheritance, and DAG is like multiple class inheritance.
                // A (successor, downstream, core) package can be depended on by many (predecessor, upstream) packages

                Console.WriteLine("Edges of dependency package graph:");
                Console.WriteLine();

                // resolve dependencies of packages
                foreach (SourcePackageDependencyInfo target in prunedPackages)
                {
                    // TODO: predecessor node in dependency package graph
                    PackageReferenceNode sourceNode = packageNodes[target.Id];

                    // traverse all dependencies of nuspec
                    foreach (PackageDependency dependency in target.Dependencies)
                    {
                        //dependency.Id
                        //dependency.VersionRange

                        // resolved dependency of sourceNode
                        PackageReferenceNode targetNode = packageNodes[dependency.Id];

                        //targetNode.PackageId
                        //targetNode.Type (package)
                        //targetNode.Version

                        // labeled edge
                        //new Edge(sourceNode, targetNode, x.VersionRange.ToString())

                        Console.WriteLine($"    {sourceNode}---{dependency.VersionRange}---->{targetNode}");
                    }

                    // TODO: directed edge with label of version range for each successor node (successor node carries resolved version)
                    //builder.WithEdges(target.Dependencies.Select(x =>
                    //    new Edge(sourceNode, packageNodes[x.Id], x.VersionRange.ToString())));
                }

                Console.WriteLine();
                Console.WriteLine($"root package: {rootPackage}");


                //return builder.Build();
            }
        }