Exemple #1
0
        public static int Main(string[] args)
        {
            // TODO: Take a switch saying whether to use TeamCity logger

            // TODO: Show extraneous packages, exclusions, etc.

            // TODO: Get this from the command line
            var ignoreAssistanceMode = IgnoreAssistanceMode.None;

            ignoreAssistanceMode = IgnoreAssistanceMode.ShowNew;

            if (args.Length < 1 || args.Length > 2)
            {
                Console.WriteLine(@"USAGE: NuGetSuperBVT.exe c:\path\to\packages [c:\path\to\packages-to-scan.json]");
                return ReturnBadArgs;
            }

            var logger = new PackageVerifierLogger();

            IDictionary<string, PackageSet> packageSets = null;

            if (args.Length >= 2)
            {
                string packagesToScanJsonFilePath = args[1];
                if (!File.Exists(packagesToScanJsonFilePath))
                {
                    logger.LogError("Couldn't find packages JSON file at {0}", packagesToScanJsonFilePath);
                    return ReturnBadArgs;
                }

                string packagesToScanJsonFileContent = File.ReadAllText(packagesToScanJsonFilePath);

                packageSets = JsonConvert.DeserializeObject<IDictionary<string, PackageSet>>(packagesToScanJsonFileContent, new JsonSerializerSettings()
                {
                    MissingMemberHandling = MissingMemberHandling.Error
                });

                logger.LogInfo("Read {0} package set(s) from {1}", packageSets.Count, packagesToScanJsonFilePath);
            }

            var totalTimeStopWatch = Stopwatch.StartNew();

            var nupkgsPath = args[0];


            // TODO: Look this up using reflection or something
            var allRules = new IPackageVerifierRule[] {
                new AssemblyHasDocumentFileRule(),
                new AssemblyHasVersionAttributesRule(),
                new AssemblyHasServicingAttributeRule(),
                new AssemblyStrongNameRule(),
                new AuthenticodeSigningRule(),
                new PowerShellScriptIsSignedRule(),
                new RequiredPackageMetadataRule(),
                new SatellitePackageRule(),
                new StrictSemanticVersionValidationRule(),
            }.ToDictionary(t => t.GetType().Name, t => t);


            var localPackageRepo = new LocalPackageRepository(nupkgsPath);

            var numPackagesInRepo = localPackageRepo.GetPackages().Count();
            logger.LogInfo("Found {0} packages in {1}", numPackagesInRepo, nupkgsPath);

            var processedPackages = new HashSet<IPackage>();

            var totalErrors = 0;
            var totalWarnings = 0;

            var ignoreAssistanceData = new Dictionary<string, IDictionary<string, IDictionary<string, string>>>(StringComparer.OrdinalIgnoreCase);

            foreach (var packageSet in packageSets)
            {
                logger.LogInfo("Processing package set '{0}' with {1} package(s)", packageSet.Key, packageSet.Value.Packages.Count);

                var packageSetRuleInfo = packageSet.Value.Rules;

                var packageSetRules = packageSetRuleInfo.Select(ruleId => allRules.Single(rule => string.Equals(rule.Key, ruleId, StringComparison.OrdinalIgnoreCase)).Value);

                var analyzer = new PackageAnalyzer();
                foreach (var ruleInstance in packageSetRules)
                {
                    analyzer.Rules.Add(ruleInstance);
                }

                IList<IssueIgnore> issuesToIgnore = GetIgnoresFromFile(packageSet.Value.Packages);

                var issueProcessor = new IssueProcessor(issuesToIgnore);


                foreach (var packageInfo in packageSet.Value.Packages)
                {
                    var packageId = packageInfo.Key;
                    var packageIgnoreInfo = packageInfo.Value;

                    var packagesWithId = localPackageRepo.FindPackagesById(packageId);
                    if (!packagesWithId.Any())
                    {
                        logger.LogError("Couldn't find package '{0}' in the repo", packageId);
                        totalErrors++;
                        continue;
                    }
                    if (packagesWithId.Count() > 1)
                    {
                        logger.LogError("Found more than one package with id '{0}' in the repo", packageId);
                        totalErrors++;
                        continue;
                    }
                    var package = packagesWithId.Single();

                    var packageTimeStopWatch = Stopwatch.StartNew();
                    logger.LogInfo("Analyzing {0} ({1})", package.Id, package.Version);


                    var issues = analyzer.AnalyzePackage(localPackageRepo, package, logger).ToList();

                    var packageErrorsAndWarnings = ProcessPackageIssues(
                        ignoreAssistanceMode, logger, issueProcessor,
                        ignoreAssistanceData, package, issues);

                    totalErrors += packageErrorsAndWarnings.Item1;
                    totalWarnings += packageErrorsAndWarnings.Item2;


                    packageTimeStopWatch.Stop();
                    logger.LogInfo("Took {0}ms", packageTimeStopWatch.ElapsedMilliseconds);
                    Console.WriteLine();

                    processedPackages.Add(package);
                }
            }

            var unprocessedPackages = localPackageRepo.GetPackages().Except(processedPackages);

            if (unprocessedPackages.Any())
            {
                logger.LogWarning("Found {0} unprocessed packages. Every package in the repo should be listed in exactly one package set. Running all rules on unlisted packages.", unprocessedPackages.Count());

                // For unprocessed packages we run all rules (because we have no idea what exactly to run)
                var analyzer = new PackageAnalyzer();
                foreach (var ruleInstance in allRules.Values)
                {
                    analyzer.Rules.Add(ruleInstance);
                }

                var issueProcessor = new IssueProcessor(issuesToIgnore: null);

                foreach (var unprocessedPackage in unprocessedPackages)
                {
                    logger.LogWarning("\tUnprocessed package: {0} ({1})", unprocessedPackage.Id, unprocessedPackage.Version);

                    var packageTimeStopWatch = Stopwatch.StartNew();
                    logger.LogInfo("Analyzing {0} ({1})", unprocessedPackage.Id, unprocessedPackage.Version);


                    var issues = analyzer.AnalyzePackage(localPackageRepo, unprocessedPackage, logger).ToList();

                    var packageErrorsAndWarnings = ProcessPackageIssues(
                        ignoreAssistanceMode, logger, issueProcessor,
                        ignoreAssistanceData, unprocessedPackage, issues);

                    totalErrors += packageErrorsAndWarnings.Item1;
                    totalWarnings += packageErrorsAndWarnings.Item2;


                    packageTimeStopWatch.Stop();
                    logger.LogInfo("Took {0}ms", packageTimeStopWatch.ElapsedMilliseconds);
                    Console.WriteLine();
                }
            }


            if (ignoreAssistanceMode != IgnoreAssistanceMode.None)
            {
                Console.WriteLine("Showing JSON for ignore content:");
                Console.WriteLine(JsonConvert.SerializeObject(ignoreAssistanceData, Formatting.Indented));
                Console.WriteLine();
            }

            LogLevel errorLevel = LogLevel.Info;
            if (totalWarnings > 0)
            {
                errorLevel = LogLevel.Warning;
            }
            if (totalErrors > 0)
            {
                errorLevel = LogLevel.Error;
            }
            logger.Log(
                errorLevel,
                "SUMMARY: {0} error(s) and {1} warning(s) found",
                totalErrors, totalWarnings);

            totalTimeStopWatch.Stop();
            logger.LogInfo("Total took {0}ms", totalTimeStopWatch.ElapsedMilliseconds);

            Console.ReadLine();

            return (totalErrors + totalWarnings > 0) ? ReturnErrorsOrWarnings : ReturnOk;
        }
Exemple #2
0
        public static int Main(string[] args)
        {
            // TODO: Take a switch saying whether to use TeamCity logger

            // TODO: Show extraneous packages, exclusions, etc.

            // TODO: Get this from the command line
            var ignoreAssistanceMode = IgnoreAssistanceMode.None;

            ignoreAssistanceMode = IgnoreAssistanceMode.ShowNew;

            if (args.Length < 1 || args.Length > 2)
            {
                Console.WriteLine(@"USAGE: NuGetSuperBVT.exe c:\path\to\packages [c:\path\to\packages-to-scan.json]");
                return(ReturnBadArgs);
            }

            var logger = new PackageVerifierLogger();

            IDictionary <string, PackageSet> packageSets = null;

            if (args.Length >= 2)
            {
                string packagesToScanJsonFilePath = args[1];
                if (!File.Exists(packagesToScanJsonFilePath))
                {
                    logger.LogError("Couldn't find packages JSON file at {0}", packagesToScanJsonFilePath);
                    return(ReturnBadArgs);
                }

                string packagesToScanJsonFileContent = File.ReadAllText(packagesToScanJsonFilePath);

                packageSets = JsonConvert.DeserializeObject <IDictionary <string, PackageSet> >(packagesToScanJsonFileContent, new JsonSerializerSettings()
                {
                    MissingMemberHandling = MissingMemberHandling.Error
                });

                logger.LogInfo("Read {0} package set(s) from {1}", packageSets.Count, packagesToScanJsonFilePath);
            }

            var totalTimeStopWatch = Stopwatch.StartNew();

            var nupkgsPath = args[0];


            // TODO: Look this up using reflection or something
            var allRules = new IPackageVerifierRule[] {
                new AssemblyHasDocumentFileRule(),
                new AssemblyHasVersionAttributesRule(),
                new AssemblyHasServicingAttributeRule(),
                new AssemblyStrongNameRule(),
                new AuthenticodeSigningRule(),
                new PowerShellScriptIsSignedRule(),
                new RequiredPackageMetadataRule(),
                new SatellitePackageRule(),
                new StrictSemanticVersionValidationRule(),
            }.ToDictionary(t => t.GetType().Name, t => t);


            var localPackageRepo = new LocalPackageRepository(nupkgsPath);

            var numPackagesInRepo = localPackageRepo.GetPackages().Count();

            logger.LogInfo("Found {0} packages in {1}", numPackagesInRepo, nupkgsPath);

            var processedPackages = new HashSet <IPackage>();

            var totalErrors   = 0;
            var totalWarnings = 0;

            var ignoreAssistanceData = new Dictionary <string, IDictionary <string, IDictionary <string, string> > >(StringComparer.OrdinalIgnoreCase);

            foreach (var packageSet in packageSets)
            {
                logger.LogInfo("Processing package set '{0}' with {1} package(s)", packageSet.Key, packageSet.Value.Packages.Count);

                var packageSetRuleInfo = packageSet.Value.Rules;

                var packageSetRules = packageSetRuleInfo.Select(ruleId => allRules.Single(rule => string.Equals(rule.Key, ruleId, StringComparison.OrdinalIgnoreCase)).Value);

                var analyzer = new PackageAnalyzer();
                foreach (var ruleInstance in packageSetRules)
                {
                    analyzer.Rules.Add(ruleInstance);
                }

                IList <IssueIgnore> issuesToIgnore = GetIgnoresFromFile(packageSet.Value.Packages);

                var issueProcessor = new IssueProcessor(issuesToIgnore);


                foreach (var packageInfo in packageSet.Value.Packages)
                {
                    var packageId         = packageInfo.Key;
                    var packageIgnoreInfo = packageInfo.Value;

                    var packagesWithId = localPackageRepo.FindPackagesById(packageId);
                    if (!packagesWithId.Any())
                    {
                        logger.LogError("Couldn't find package '{0}' in the repo", packageId);
                        totalErrors++;
                        continue;
                    }
                    if (packagesWithId.Count() > 1)
                    {
                        logger.LogError("Found more than one package with id '{0}' in the repo", packageId);
                        totalErrors++;
                        continue;
                    }
                    var package = packagesWithId.Single();

                    var packageTimeStopWatch = Stopwatch.StartNew();
                    logger.LogInfo("Analyzing {0} ({1})", package.Id, package.Version);


                    var issues = analyzer.AnalyzePackage(localPackageRepo, package, logger).ToList();

                    var packageErrorsAndWarnings = ProcessPackageIssues(
                        ignoreAssistanceMode, logger, issueProcessor,
                        ignoreAssistanceData, package, issues);

                    totalErrors   += packageErrorsAndWarnings.Item1;
                    totalWarnings += packageErrorsAndWarnings.Item2;


                    packageTimeStopWatch.Stop();
                    logger.LogInfo("Took {0}ms", packageTimeStopWatch.ElapsedMilliseconds);
                    Console.WriteLine();

                    processedPackages.Add(package);
                }
            }

            var unprocessedPackages = localPackageRepo.GetPackages().Except(processedPackages);

            if (unprocessedPackages.Any())
            {
                logger.LogWarning("Found {0} unprocessed packages. Every package in the repo should be listed in exactly one package set. Running all rules on unlisted packages.", unprocessedPackages.Count());

                // For unprocessed packages we run all rules (because we have no idea what exactly to run)
                var analyzer = new PackageAnalyzer();
                foreach (var ruleInstance in allRules.Values)
                {
                    analyzer.Rules.Add(ruleInstance);
                }

                var issueProcessor = new IssueProcessor(issuesToIgnore: null);

                foreach (var unprocessedPackage in unprocessedPackages)
                {
                    logger.LogWarning("\tUnprocessed package: {0} ({1})", unprocessedPackage.Id, unprocessedPackage.Version);

                    var packageTimeStopWatch = Stopwatch.StartNew();
                    logger.LogInfo("Analyzing {0} ({1})", unprocessedPackage.Id, unprocessedPackage.Version);


                    var issues = analyzer.AnalyzePackage(localPackageRepo, unprocessedPackage, logger).ToList();

                    var packageErrorsAndWarnings = ProcessPackageIssues(
                        ignoreAssistanceMode, logger, issueProcessor,
                        ignoreAssistanceData, unprocessedPackage, issues);

                    totalErrors   += packageErrorsAndWarnings.Item1;
                    totalWarnings += packageErrorsAndWarnings.Item2;


                    packageTimeStopWatch.Stop();
                    logger.LogInfo("Took {0}ms", packageTimeStopWatch.ElapsedMilliseconds);
                    Console.WriteLine();
                }
            }


            if (ignoreAssistanceMode != IgnoreAssistanceMode.None)
            {
                Console.WriteLine("Showing JSON for ignore content:");
                Console.WriteLine(JsonConvert.SerializeObject(ignoreAssistanceData, Formatting.Indented));
                Console.WriteLine();
            }

            LogLevel errorLevel = LogLevel.Info;

            if (totalWarnings > 0)
            {
                errorLevel = LogLevel.Warning;
            }
            if (totalErrors > 0)
            {
                errorLevel = LogLevel.Error;
            }
            logger.Log(
                errorLevel,
                "SUMMARY: {0} error(s) and {1} warning(s) found",
                totalErrors, totalWarnings);

            totalTimeStopWatch.Stop();
            logger.LogInfo("Total took {0}ms", totalTimeStopWatch.ElapsedMilliseconds);

            Console.ReadLine();

            return((totalErrors + totalWarnings > 0) ? ReturnErrorsOrWarnings : ReturnOk);
        }
        private static int Execute(
            IDictionary <string, PackageSet> packageSets,
            IEnumerable <FileInfo> nupkgs,
            List <string> excludedRuleNames,
            IPackageVerifierLogger logger,
            IgnoreAssistanceMode ignoreAssistanceMode)
        {
            var allRules = typeof(Program).Assembly.GetTypes()
                           .Where(t =>
                                  typeof(IPackageVerifierRule).IsAssignableFrom(t) && !t.IsAbstract)
                           .ToDictionary(
                t => t.Name,
                t =>
            {
                var rule = (IPackageVerifierRule)Activator.CreateInstance(t);
                if (rule is CompositeRule compositeRule)
                {
                    return(compositeRule.GetRules());
                }
                else
                {
                    return(new[] { rule });
                }
            });

            var packages = nupkgs.ToDictionary(file =>
            {
                var reader = new PackageArchiveReader(file.FullName);
                return((IPackageMetadata) new PackageBuilder(reader.GetNuspec(), basePath: null));
            });

            var processedPackages = new HashSet <IPackageMetadata>();

            var totalErrors   = 0;
            var totalWarnings = 0;

            var ignoreAssistanceData = new Dictionary <string, PackageVerifierOptions>(
                StringComparer.OrdinalIgnoreCase);

            PackageSet defaultPackageSet = null;
            IEnumerable <IPackageVerifierRule> defaultRuleSet        = null;
            IEnumerable <IssueIgnore>          defaultIssuesToIgnore = null;

            foreach (var packageSet in packageSets)
            {
                logger.LogInfo(
                    "Processing package set '{0}' with {1} package(s)",
                    packageSet.Key,
                    packageSet.Value.Packages?.Count ?? 0);

                var packageSetRuleInfo = packageSet.Value.Rules;

                var packageSetRules = packageSetRuleInfo.SelectMany(
                    ruleId => allRules.SingleOrDefault(
                        rule => string.Equals(rule.Key, ruleId, StringComparison.OrdinalIgnoreCase)).Value)
                                      .Where(rule => !excludedRuleNames.Contains(rule.GetType().Name));

                if (string.Equals(packageSet.Key, "Default", StringComparison.OrdinalIgnoreCase))
                {
                    defaultPackageSet     = packageSet.Value;
                    defaultRuleSet        = packageSetRules;
                    defaultIssuesToIgnore = GetIgnoresFromFile(packageSet.Value.Packages);
                    continue;
                }

                var analyzer = new PackageAnalyzer();
                foreach (var ruleInstance in packageSetRules)
                {
                    analyzer.Rules.Add(ruleInstance);
                }

                var issuesToIgnore = GetIgnoresFromFile(packageSet.Value.Packages);

                var issueProcessor = new IssueProcessor(issuesToIgnore);

                if (packageSet.Value.Packages != null)
                {
                    foreach (var packageInfo in packageSet.Value.Packages)
                    {
                        var packageId = packageInfo.Key;

                        var packagesWithId = packages.Where(p => p.Key.Id.Equals(packageId));
                        if (!packagesWithId.Any())
                        {
                            logger.LogError("Couldn't find package '{0}' in the repo", packageId);
                            totalErrors++;
                            continue;
                        }

                        foreach (var packagePair in packagesWithId)
                        {
                            var package = packagePair.Key;
                            logger.LogInfo("Analyzing {0} ({1})", package.Id, package.Version);

                            List <PackageVerifierIssue> issues;
                            using (var context = new PackageAnalysisContext
                            {
                                PackageFileInfo = packagePair.Value,
                                Metadata = package,
                                Logger = logger,
                                Options = packageInfo.Value,
                            })
                            {
                                issues = analyzer.AnalyzePackage(context).ToList();
                            }

                            var packageErrorsAndWarnings = ProcessPackageIssues(
                                ignoreAssistanceMode,
                                logger,
                                issueProcessor,
                                ignoreAssistanceData,
                                package,
                                issues);

                            totalErrors   += packageErrorsAndWarnings.Item1;
                            totalWarnings += packageErrorsAndWarnings.Item2;

                            processedPackages.Add(package);
                        }
                    }
                }

                foreach (var issue in issueProcessor.RemainingIssuesToIgnore)
                {
                    // TODO: Don't show this for rules that we don't run.
                    logger.LogWarning(
                        "Unnecessary exclusion in {0}{3}Issue: {1}{3}Instance: {2}{3}",
                        issue.PackageId,
                        issue.IssueId,
                        issue.Instance,
                        Environment.NewLine);
                }
            }

            var unlistedPackages = packages.Keys.Except(processedPackages);

            if (unlistedPackages.Any())
            {
                // For unlisted packages we run the rules from 'Default' package set if present
                // or we run all rules (because we have no idea what exactly to run)
                var analyzer             = new PackageAnalyzer();
                var unlistedPackageRules = defaultRuleSet ??
                                           allRules.Values.SelectMany(f => f).Where(r => !excludedRuleNames.Contains(r.GetType().Name));

                foreach (var ruleInstance in unlistedPackageRules)
                {
                    analyzer.Rules.Add(ruleInstance);
                }

                var issueProcessor = new IssueProcessor(issuesToIgnore: defaultIssuesToIgnore);

                foreach (var unlistedPackage in unlistedPackages)
                {
                    logger.LogInfo("Analyzing {0} ({1})", unlistedPackage.Id, unlistedPackage.Version);

                    List <PackageVerifierIssue> issues;
                    PackageVerifierOptions      packageOptions = null;
                    defaultPackageSet?.Packages?.TryGetValue(unlistedPackage.Id, out packageOptions);

                    using (var context = new PackageAnalysisContext
                    {
                        PackageFileInfo = packages[unlistedPackage],
                        Metadata = unlistedPackage,
                        Logger = logger,
                        Options = packageOptions,
                    })
                    {
                        issues = analyzer.AnalyzePackage(context).ToList();
                    }

                    var packageErrorsAndWarnings = ProcessPackageIssues(
                        ignoreAssistanceMode,
                        logger,
                        issueProcessor,
                        ignoreAssistanceData,
                        unlistedPackage,
                        issues);

                    totalErrors   += packageErrorsAndWarnings.Item1;
                    totalWarnings += packageErrorsAndWarnings.Item2;
                }

                foreach (var issue in issueProcessor.RemainingIssuesToIgnore)
                {
                    // TODO: Don't show this for rules that we don't run.
                    logger.LogWarning(
                        "Unnecessary exclusion in {0}{3}Issue: {1}{3}Instance: {2}{3}",
                        issue.PackageId,
                        issue.IssueId,
                        issue.Instance,
                        Environment.NewLine);
                }
            }

            if (ignoreAssistanceMode != IgnoreAssistanceMode.None && ignoreAssistanceData.Any())
            {
                Console.WriteLine("Showing JSON for ignore content:");
                Console.WriteLine(JsonConvert.SerializeObject(ignoreAssistanceData,
                                                              Formatting.Indented,
                                                              new JsonSerializerSettings
                {
                    NullValueHandling = NullValueHandling.Ignore
                }));
                Console.WriteLine();
            }

            var errorLevel = LogLevel.Normal;

            if (totalWarnings > 0)
            {
                errorLevel = LogLevel.Warning;
            }
            if (totalErrors > 0)
            {
                errorLevel = LogLevel.Error;
            }
            logger.Log(
                errorLevel,
                "SUMMARY: {0} error(s) and {1} warning(s) found",
                totalErrors, totalWarnings);



            return((totalErrors + totalWarnings > 0) ? ReturnErrorsOrWarnings : ReturnOk);
        }