Example #1
0
        public async Task <UpdateImpactResults> Analyze(
            Stream appxBlockMap1,
            Stream appxBlockMap2,
            CancellationToken cancellationToken   = default,
            IProgress <ProgressData> progressData = default)
        {
            var progressReadFiles1           = new RangeProgress(progressData, 0, 30);
            var progressReadFiles2           = new RangeProgress(progressData, 30, 60);
            var progressCalculatingStatus    = new RangeProgress(progressData, 60, 75);
            var progressDuplicates           = new RangeProgress(progressData, 75, 90);
            var progressRemainingCalculation = new RangeProgress(progressData, 90, 100);

            var filesInOldPackage = new SortedDictionary <string, AppxFile>();
            var filesInNewPackage = new SortedDictionary <string, AppxFile>();
            var blocksInPackage1  = new Dictionary <string, AppxBlock>();
            var blocksInPackage2  = new Dictionary <string, AppxBlock>();

            progressReadFiles1.Report(new ProgressData(0, "Reading the first package..."));
            var files1 = await this.GetFiles(appxBlockMap1, cancellationToken, progressReadFiles1).ConfigureAwait(false);

            foreach (var file1 in files1)
            {
                foreach (var block in file1.Blocks)
                {
                    blocksInPackage1[block.Hash] = block;
                }

                filesInOldPackage.Add(file1.Name, file1);
            }

            progressReadFiles2.Report(new ProgressData(0, "Reading the second package..."));
            var files2 = await this.GetFiles(appxBlockMap2, cancellationToken, progressReadFiles2).ConfigureAwait(false);

            foreach (var file2 in files2)
            {
                foreach (var block in file2.Blocks)
                {
                    blocksInPackage2[block.Hash] = block;
                }

                filesInNewPackage.Add(file2.Name, file2);
            }

            progressCalculatingStatus.Report(new ProgressData(0, "Calculating file status (1/2)..."));
            foreach (var file in filesInOldPackage)
            {
                cancellationToken.ThrowIfCancellationRequested();
                if (!filesInNewPackage.TryGetValue(file.Key, out var fileFromPackage2))
                {
                    file.Value.Status         = ComparisonStatus.Old;
                    file.Value.UpdateImpact   = 0; // file removed = no update on impact
                    file.Value.SizeDifference = -file.Value.UncompressedSize;
                    continue;
                }

                if (file.Value.UncompressedSize == fileFromPackage2.UncompressedSize && file.Value.Blocks.Select(b => b.Hash).SequenceEqual(fileFromPackage2.Blocks.Select(b => b.Hash)))
                {
                    file.Value.Status         = ComparisonStatus.Unchanged;
                    file.Value.UpdateImpact   = 0; // file unchanged = no update on impact
                    file.Value.SizeDifference = 0;

                    fileFromPackage2.Status         = ComparisonStatus.Unchanged;
                    fileFromPackage2.UpdateImpact   = 0; // file unchanged = no update on impact
                    fileFromPackage2.SizeDifference = 0;
                }
                else
                {
                    file.Value.Status         = ComparisonStatus.Changed;
                    file.Value.UpdateImpact   = 0; // file changed = show no update impact. The impact should be shown in the other package.
                    file.Value.SizeDifference = file.Value.UncompressedSize - fileFromPackage2.UncompressedSize;

                    var blocksOnlyInPackage2 = fileFromPackage2.Blocks.Select(b => b.Hash).Except(file.Value.Blocks.Select(b => b.Hash));
                    fileFromPackage2.Status         = ComparisonStatus.Changed;
                    fileFromPackage2.UpdateImpact   = blocksOnlyInPackage2.Select(b => blocksInPackage2[b]).Sum(b => b.CompressedSize);
                    fileFromPackage2.SizeDifference = fileFromPackage2.UncompressedSize - file.Value.UncompressedSize;
                }
            }
            foreach (var block1KeyValuePair in blocksInPackage1)
            {
                cancellationToken.ThrowIfCancellationRequested();
                var block1 = block1KeyValuePair.Value;
                if (!blocksInPackage2.TryGetValue(block1KeyValuePair.Key, out var block2))
                {
                    block1.Status       = ComparisonStatus.Old;
                    block1.UpdateImpact = 0; // block removed, no update impact
                }
                else
                {
                    block1.Status       = ComparisonStatus.Unchanged;
                    block1.UpdateImpact = 0;
                    block2.Status       = ComparisonStatus.Unchanged;
                    block2.UpdateImpact = 0;
                }
            }

            progressCalculatingStatus.Report(new ProgressData(50, "Calculating file status (2/2)..."));
            foreach (var file in filesInNewPackage)
            {
                cancellationToken.ThrowIfCancellationRequested();
                if (!filesInOldPackage.TryGetValue(file.Key, out _))
                {
                    file.Value.Status         = ComparisonStatus.New;
                    file.Value.UpdateImpact   = file.Value.Blocks.Sum(b => b.CompressedSize); // sum of all blocks
                    file.Value.SizeDifference = file.Value.UncompressedSize;
                }
            }
            foreach (var block2KeyValuePair in blocksInPackage2)
            {
                cancellationToken.ThrowIfCancellationRequested();
                var block2 = block2KeyValuePair.Value;
                if (!blocksInPackage1.ContainsKey(block2KeyValuePair.Key))
                {
                    block2.Status       = ComparisonStatus.New;
                    block2.UpdateImpact = block2.CompressedSize;
                }
            }

            var duplicates1 = new Dictionary <string, IList <AppxFile> >();
            var duplicates2 = new Dictionary <string, IList <AppxFile> >();

            progressDuplicates.Report(new ProgressData(0, "Finding duplicates..."));
            using (var md5 = MD5.Create())
            {
                foreach (var file in filesInOldPackage.Values)
                {
                    cancellationToken.ThrowIfCancellationRequested();
                    var hash          = string.Join(Environment.NewLine, file.Blocks.Select(b => b.Hash));
                    var allBlocksHash = System.Text.Encoding.ASCII.GetString(md5.ComputeHash(System.Text.Encoding.ASCII.GetBytes(hash)));

                    if (!duplicates1.TryGetValue(allBlocksHash, out var list))
                    {
                        list = new List <AppxFile>();
                        duplicates1[allBlocksHash] = list;
                    }

                    list.Add(file);
                }

                foreach (var file in filesInNewPackage.Values)
                {
                    cancellationToken.ThrowIfCancellationRequested();
                    var hash          = string.Join(System.Environment.NewLine, file.Blocks.Select(b => b.Hash));
                    var allBlocksHash = System.Text.Encoding.ASCII.GetString(md5.ComputeHash(System.Text.Encoding.ASCII.GetBytes(hash)));

                    if (!duplicates2.TryGetValue(allBlocksHash, out var list))
                    {
                        list = new List <AppxFile>();
                        duplicates2[allBlocksHash] = list;
                    }

                    list.Add(file);
                }
            }

            var duplicatedFiles1 = new AppxDuplication
            {
                Duplicates = new List <ComparedDuplicate>(),
                FileCount  = duplicates1.Count(d => d.Value.Count > 1) * 2,
                FileSize   = duplicates1.Where(d => d.Value.Count > 1).Sum(d => d.Value[0].UncompressedSize)
            };

            var duplicatedFiles2 = new AppxDuplication
            {
                Duplicates = new List <ComparedDuplicate>(),
                FileCount  = duplicates2.Count(d => d.Value.Count > 1) * 2,
                FileSize   = duplicates2.Where(d => d.Value.Count > 1).Sum(d => d.Value[0].UncompressedSize)
            };

            progressDuplicates.Report(new ProgressData(55, "Analyzing duplication impact (1/3)..."));
            foreach (var file in duplicates1.Where(d => d.Value.Count > 1))
            {
                cancellationToken.ThrowIfCancellationRequested();
                var duplicate = new ComparedDuplicate
                {
                    Files = new List <ComparedDuplicateFile>()
                };

                foreach (var df in file.Value)
                {
                    var mdf = new ComparedDuplicateFile
                    {
                        Name = df.Name,
                        PossibleSizeReduction   = df.UncompressedSize,
                        PossibleImpactReduction = df.Blocks.Sum(d => blocksInPackage1[d.Hash].CompressedSize)
                    };

                    duplicate.Files.Add(mdf);
                }

                duplicate.PossibleSizeReduction   = duplicate.Files[0].PossibleSizeReduction * (duplicate.Files.Count - 1);
                duplicate.PossibleImpactReduction = duplicate.Files[0].PossibleImpactReduction * (duplicate.Files.Count - 1);

                duplicatedFiles1.Duplicates.Add(duplicate);
            }

            progressDuplicates.Report(new ProgressData(70, "Analyzing duplication impact (2/3)..."));
            foreach (var file in duplicates2.Where(d => d.Value.Count > 1))
            {
                cancellationToken.ThrowIfCancellationRequested();
                var duplicate = new ComparedDuplicate
                {
                    Files = new List <ComparedDuplicateFile>()
                };

                foreach (var df in file.Value)
                {
                    var mdf = new ComparedDuplicateFile
                    {
                        Name = df.Name,
                        PossibleSizeReduction   = df.UncompressedSize,
                        PossibleImpactReduction = df.Blocks.Sum(d => blocksInPackage2[d.Hash].CompressedSize)
                    };

                    duplicate.Files.Add(mdf);
                }

                duplicate.PossibleSizeReduction   = duplicate.Files[0].PossibleSizeReduction * (duplicate.Files.Count - 1);
                duplicate.PossibleImpactReduction = duplicate.Files[0].PossibleImpactReduction * (duplicate.Files.Count - 1);

                duplicatedFiles2.Duplicates.Add(duplicate);
            }

            progressDuplicates.Report(new ProgressData(85, "Analyzing duplication impact (3/3)..."));
            duplicatedFiles1.PossibleImpactReduction = duplicatedFiles1.Duplicates.Sum(d => d.PossibleImpactReduction);
            duplicatedFiles1.PossibleSizeReduction   = duplicatedFiles1.Duplicates.Sum(d => d.PossibleSizeReduction);
            duplicatedFiles2.PossibleImpactReduction = duplicatedFiles2.Duplicates.Sum(d => d.PossibleImpactReduction);
            duplicatedFiles2.PossibleSizeReduction   = duplicatedFiles2.Duplicates.Sum(d => d.PossibleSizeReduction);

            cancellationToken.ThrowIfCancellationRequested();
            progressRemainingCalculation.Report(new ProgressData(0, "Analyzing changed files..."));
            var changedFiles = new ChangedFiles
            {
                // Shared parameters
                UpdateImpact       = filesInNewPackage.Values.Where(b => b.Status == ComparisonStatus.Changed).SelectMany(b => b.Blocks).Sum(b => b.UpdateImpact),
                ActualUpdateImpact = blocksInPackage2.Values.Where(b => b.Status == ComparisonStatus.Changed).Sum(b => b.UpdateImpact),
                SizeDifference     = filesInNewPackage.Values.Where(b => b.Status == ComparisonStatus.Changed).Sum(b => b.SizeDifference),
                FileCount          = filesInOldPackage.Values.Count(b => b.Status == ComparisonStatus.Changed),

                // Old package
                OldPackageFiles      = filesInOldPackage.Values.Where(f => f.Status == ComparisonStatus.Changed).ToList(),
                OldPackageFileSize   = filesInOldPackage.Values.Where(b => b.Status == ComparisonStatus.Changed).Sum(b => b.UncompressedSize),
                OldPackageBlockCount = filesInOldPackage.Values.Where(b => b.Status == ComparisonStatus.Changed).Sum(b => b.Blocks.Count),
                OldPackageBlockSize  = filesInOldPackage.Values.Where(b => b.Status == ComparisonStatus.Changed).SelectMany(b => b.Blocks).Sum(b => b.CompressedSize),

                // New package
                NewPackageFiles      = filesInNewPackage.Values.Where(f => f.Status == ComparisonStatus.Changed).ToList(),
                NewPackageFileSize   = filesInNewPackage.Values.Where(b => b.Status == ComparisonStatus.Changed).Sum(b => b.UncompressedSize),
                NewPackageBlockCount = filesInNewPackage.Values.Where(b => b.Status == ComparisonStatus.Changed).Sum(b => b.Blocks.Count),
                NewPackageBlockSize  = filesInNewPackage.Values.Where(b => b.Status == ComparisonStatus.Changed).SelectMany(b => b.Blocks).Sum(b => b.CompressedSize)
            };

            cancellationToken.ThrowIfCancellationRequested();
            progressRemainingCalculation.Report(new ProgressData(25, "Analyzing added files..."));
            var addedFiles = new AddedFiles
            {
                UpdateImpact       = filesInNewPackage.Values.Where(b => b.Status == ComparisonStatus.New).SelectMany(b => b.Blocks).Sum(b => b.UpdateImpact),
                ActualUpdateImpact = blocksInPackage2.Values.Where(b => b.Status == ComparisonStatus.New).Sum(b => b.UpdateImpact),
                BlockCount         = blocksInPackage2.Values.Count(b => b.Status == ComparisonStatus.New),
                BlockSize          = blocksInPackage2.Values.Where(b => b.Status == ComparisonStatus.New).Sum(b => b.CompressedSize),
                FileCount          = filesInNewPackage.Values.Count(s => s.Status == ComparisonStatus.New),
                SizeDifference     = filesInNewPackage.Values.Where(s => s.Status == ComparisonStatus.New).Sum(f => f.SizeDifference),
                FileSize           = filesInNewPackage.Values.Where(s => s.Status == ComparisonStatus.New).Sum(f => f.UncompressedSize),
                Files = filesInNewPackage.Values.Where(f => f.Status == ComparisonStatus.New).ToList()
            };

            cancellationToken.ThrowIfCancellationRequested();
            progressRemainingCalculation.Report(new ProgressData(50, "Analyzing deleted files..."));
            var deletedFiles = new DeletedFiles
            {
                FileSize           = filesInOldPackage.Values.Where(s => s.Status == ComparisonStatus.Old).Sum(f => f.UncompressedSize),
                SizeDifference     = filesInOldPackage.Values.Where(s => s.Status == ComparisonStatus.Old).Sum(f => f.SizeDifference),
                FileCount          = filesInOldPackage.Values.Count(s => s.Status == ComparisonStatus.Old),
                BlockSize          = blocksInPackage1.Values.Where(b => b.Status == ComparisonStatus.Old).Sum(b => b.CompressedSize),
                BlockCount         = blocksInPackage1.Values.Count(b => b.Status == ComparisonStatus.Old),
                ActualUpdateImpact = blocksInPackage1.Values.Where(s => s.Status == ComparisonStatus.Old).Sum(f => f.UpdateImpact),
                Files = filesInOldPackage.Values.Where(f => f.Status == ComparisonStatus.Old).ToList()
            };

            deletedFiles.UpdateImpact = deletedFiles.ActualUpdateImpact;

            cancellationToken.ThrowIfCancellationRequested();
            progressRemainingCalculation.Report(new ProgressData(75, "Analyzing unchanged files..."));
            var unchangedFiles = new UnchangedFiles
            {
                FileCount  = filesInNewPackage.Values.Count(b => b.Status == ComparisonStatus.Unchanged),
                BlockCount = blocksInPackage2.Values.Count(b => b.Status == ComparisonStatus.Unchanged),
                BlockSize  = blocksInPackage2.Values.Where(b => b.Status == ComparisonStatus.Unchanged).Sum(b => b.CompressedSize),
                FileSize   = filesInNewPackage.Values.Where(b => b.Status == ComparisonStatus.Unchanged).Sum(b => b.UncompressedSize),
                Files      = filesInOldPackage.Values.Where(f => f.Status == ComparisonStatus.Unchanged).ToList()
            };

            cancellationToken.ThrowIfCancellationRequested();
            progressRemainingCalculation.Report(new ProgressData(90, "Please wait..."));
            var comparisonResult = new UpdateImpactResults
            {
                OldPackageLayout = new AppxLayout
                {
                    FileSize   = filesInOldPackage.Sum(f => f.Value.UncompressedSize),
                    FileCount  = filesInOldPackage.Count,
                    BlockSize  = blocksInPackage1.Sum(f => f.Value.CompressedSize),
                    BlockCount = blocksInPackage1.Count,
                    Layout     = new PackageLayout
                    {
                        Blocks = this.GetChartForBlocks(filesInOldPackage),
                        Files  = this.GetChartForFiles(filesInOldPackage)
                    }
                },
                NewPackageLayout = new AppxLayout
                {
                    FileSize   = filesInNewPackage.Sum(f => f.Value.UncompressedSize),
                    FileCount  = filesInNewPackage.Count,
                    BlockSize  = blocksInPackage2.Sum(f => f.Value.CompressedSize),
                    BlockCount = blocksInPackage2.Count,
                    Layout     = new PackageLayout
                    {
                        Blocks = this.GetChartForBlocks(filesInNewPackage),
                        Files  = this.GetChartForFiles(filesInNewPackage)
                    }
                },
                OldPackageDuplication = duplicatedFiles1,
                NewPackageDuplication = duplicatedFiles2,
                UpdateImpact          =
                    blocksInPackage2.Where(b => b.Value.Status == ComparisonStatus.New).Sum(b => b.Value.UpdateImpact) +
                    blocksInPackage1.Sum(b => b.Value.UpdateImpact),
                ChangedFiles   = changedFiles,
                DeletedFiles   = deletedFiles,
                AddedFiles     = addedFiles,
                UnchangedFiles = unchangedFiles
            };

            comparisonResult.OldPackageLayout.Size = comparisonResult.OldPackageLayout.BlockSize + filesInOldPackage.Values.Sum(f => f.HeaderSize);
            comparisonResult.NewPackageLayout.Size = comparisonResult.NewPackageLayout.BlockSize + filesInNewPackage.Values.Sum(f => f.HeaderSize);

            comparisonResult.SizeDifference     = comparisonResult.AddedFiles.SizeDifference + comparisonResult.ChangedFiles.SizeDifference + comparisonResult.DeletedFiles.SizeDifference;
            comparisonResult.ActualUpdateImpact = comparisonResult.UpdateImpact;

            return(comparisonResult);
        }
        public async Task CreateVolumes(
            IReadOnlyCollection <string> packagePaths,
            string volumeDirectory,
            AppAttachVolumeType type                  = AppAttachVolumeType.Vhd,
            bool extractCertificate                   = false,
            bool generateScripts                      = true,
            CancellationToken cancellationToken       = default,
            IProgress <ProgressData> progressReporter = null)
        {
            if (volumeDirectory == null)
            {
                throw new ArgumentNullException(nameof(volumeDirectory));
            }

            var diskExtension = type.ToString("G").ToLowerInvariant();

            if (!Directory.Exists(volumeDirectory))
            {
                Logger.Info("Creating directory {0}...", volumeDirectory);
                Directory.CreateDirectory(volumeDirectory);
            }

            var allPackagesCount = packagePaths.Count;
            var currentPackage   = 0;

            var volumeCreationStrategy = await this.GetVolumeCreationStrategy(type).ConfigureAwait(false);

            IAppAttachVolumeCreationStrategyInitialization initialization = null;

            try
            {
                initialization = await volumeCreationStrategy.Initialize(cancellationToken).ConfigureAwait(false);

                foreach (var packagePath in packagePaths)
                {
                    var currentVolumeDirectory = type == AppAttachVolumeType.Cim ? Path.Combine(volumeDirectory, Path.GetFileNameWithoutExtension(packagePath)) : volumeDirectory;

                    var packageProgressReporter = new RangeProgress(progressReporter, (int)(currentPackage * 80.0 / allPackagesCount), (int)((currentPackage + 1) * 80.0 / allPackagesCount));
                    currentPackage++;

                    var packageFileInfo = new FileInfo(packagePath);
                    if (!packageFileInfo.Exists)
                    {
                        throw new FileNotFoundException($"File {packagePath} does not exist.", packagePath);
                    }

                    var volumeFileInfo = new FileInfo(Path.Combine(currentVolumeDirectory, Path.GetFileNameWithoutExtension(packagePath) + "." + diskExtension));
                    if (volumeFileInfo.Exists)
                    {
                        volumeFileInfo.Delete();
                    }
                    else if (volumeFileInfo.Directory?.Exists == false)
                    {
                        volumeFileInfo.Directory.Create();
                    }

                    packageProgressReporter.Report(new ProgressData(0, $"Analyzing {Path.GetFileName(packagePath)}..."));

                    await volumeCreationStrategy.CreateVolume(packagePath, volumeFileInfo.FullName, null, cancellationToken, packageProgressReporter).ConfigureAwait(false);

                    if (extractCertificate)
                    {
                        packageProgressReporter.Report(new ProgressData(100, $"Extracting certificate from {Path.GetFileName(packagePath)}..."));
                        var actualSigningManager = this.signingManager ?? await this.managerFactory.GetProxyFor(SelfElevationLevel.AsInvoker, cancellationToken).ConfigureAwait(false);

                        cancellationToken.ThrowIfCancellationRequested();

                        // ReSharper disable once AssignNullToNotNullAttribute
                        await actualSigningManager.ExtractCertificateFromMsix(packagePath, Path.Combine(volumeFileInfo.DirectoryName, Path.GetFileNameWithoutExtension(volumeFileInfo.FullName)) + ".cer", cancellationToken).ConfigureAwait(false);
                    }
                }

                if (type == AppAttachVolumeType.Cim)
                {
                    // Currently JSON and PS1 are only supported for VHD(X)...
                }
                else
                {
                    var jsonData = new List <JsonData>();

                    if (generateScripts)
                    {
                        progressReporter?.Report(new ProgressData(90, "Creating scripts..."));
                        await CreateScripts(volumeDirectory, cancellationToken).ConfigureAwait(false);
                    }

                    progressReporter?.Report(new ProgressData(95, "Creating JSON file..."));
                    foreach (var volumePath in packagePaths.Select(p => Path.Combine(volumeDirectory, Path.GetFileNameWithoutExtension(p) + "." + diskExtension)))
                    {
                        var volumeData = await this.GetExpandedPackageData(volumePath, cancellationToken).ConfigureAwait(false);

                        var volumeGuid           = volumeData.Item1;
                        var volumeMsixFolderName = volumeData.Item2;

                        jsonData.Add(new JsonData(Path.GetFileName(volumePath), Path.GetFileNameWithoutExtension(volumePath), volumeGuid, volumeMsixFolderName));
                    }

                    var jsonPath = Path.Combine(volumeDirectory, "app-attach.json");

                    await CreateJson(jsonPath, jsonData, cancellationToken).ConfigureAwait(false);
                }
            }
            finally
            {
                await volumeCreationStrategy.Finish(initialization, cancellationToken).ConfigureAwait(false);
            }
        }
Example #3
0
        private async Task <IList <AppxPackage> > GetConsideredPackages(AppxPackage startPackage, CancellationToken cancellationToken = default, IProgress <ProgressData> progress = default)
        {
            var progressForGettingPackages = new RangeProgress(progress, 0, 70);
            var progressForGettingAddOns   = new RangeProgress(progress, 70, 90);
            var progressForCalculation     = new RangeProgress(progress, 90, 100);

            var manager = await this.packageManager.GetProxyFor(SelfElevationLevel.HighestAvailable, cancellationToken).ConfigureAwait(false);

            var allPackages = await manager.GetInstalledPackages(PackageFindMode.Auto, cancellationToken, progressForGettingPackages).ConfigureAwait(false);

            var consideredPackages = new List <AppxPackage> {
                startPackage
            };
            var addOnPackages = new List <AppxPackage>();

            var manifestReader = new AppxManifestReader();

            progressForGettingAddOns.Report(new ProgressData(0, "Reading optional packages..."));
            foreach (var addOnPackage in allPackages.Where(installedPackage => installedPackage.IsOptional))
            {
                using var fileReader = FileReaderFactory.CreateFileReader(addOnPackage.ManifestLocation);
                addOnPackages.Add(await manifestReader.Read(fileReader, false, cancellationToken).ConfigureAwait(false));
            }

            progressForCalculation.Report(new ProgressData(0, "Reading relations..."));
            for (var i = 0; i < consideredPackages.Count; i++)
            {
                var currentPkg = consideredPackages[i];

                var matchingAddOns = addOnPackages.Where(addOnPackage => addOnPackage.MainPackages.Any(dep => dep.Name == currentPkg.Name));
                foreach (var matchingAddOn in matchingAddOns)
                {
                    if (consideredPackages.Any(existing => existing.Publisher == matchingAddOn.Publisher && existing.Name == matchingAddOn.Name))
                    {
                        // we have already processes this package
                        continue;
                    }

                    consideredPackages.Add(matchingAddOn);
                }

                foreach (var dependency in currentPkg.PackageDependencies)
                {
                    if (consideredPackages.Any(existing => existing.Publisher == dependency.Publisher && existing.Name == dependency.Name))
                    {
                        // we have already processes this package
                        continue;
                    }

                    var candidate = allPackages.FirstOrDefault(installedPackage => installedPackage.Name == dependency.Name && installedPackage.Publisher == dependency.Publisher && installedPackage.Version >= Version.Parse(dependency.Version));

                    if (candidate != null)
                    {
                        using var fileReader = FileReaderFactory.CreateFileReader(candidate.ManifestLocation);
                        consideredPackages.Add(await manifestReader.Read(fileReader, false, cancellationToken).ConfigureAwait(false));
                    }
                }

                foreach (var dependency in currentPkg.MainPackages)
                {
                    if (consideredPackages.Any(existing => existing.Name == dependency.Name))
                    {
                        // we have already processes this package
                        continue;
                    }

                    var candidate = allPackages.FirstOrDefault(installedPackage => installedPackage.Name == dependency.Name);

                    if (candidate != null)
                    {
                        using var fileReader = FileReaderFactory.CreateFileReader(candidate.ManifestLocation);
                        consideredPackages.Add(await manifestReader.Read(fileReader, false, cancellationToken).ConfigureAwait(false));
                    }
                }
            }

            return(consideredPackages);
        }