Beispiel #1
0
        /// <summary>
        /// Generate the serialized module metadata for a given module.
        /// </summary>
        public void SerializeModule()
        {
            var outputModuleManifestPath   = _fileHelper.OutputModuleManifestPath;
            var outputModuleDirectory      = _fileHelper.OutputModuleDirectory;
            var outputDirectories          = _fileHelper.OutputDirectories;
            var serializedCmdletsDirectory = _fileHelper.SerializedCmdletsDirectory;
            var moduleName = _fileHelper.ModuleName;
            IEnumerable <string> nestedModules = null;

            using (PowerShell powershell = PowerShell.Create())
            {
                powershell.AddScript("(Test-ModuleManifest -Path " + outputModuleManifestPath + ").NestedModules");
                var cmdletResult = powershell.Invoke();
                nestedModules = cmdletResult.Select(c => c.ToString() + ".dll");
            }

            if (nestedModules.Any())
            {
                List <string> requiredModules = null;
                using (PowerShell powershell = PowerShell.Create())
                {
                    powershell.AddScript("(Test-ModuleManifest -Path " + outputModuleManifestPath + ").RequiredModules");
                    var cmdletResult = powershell.Invoke();
                    requiredModules = cmdletResult.Select(c => c.ToString())
                                      .Join(outputDirectories,
                                            module => 1,
                                            directory => 1,
                                            (module, directory) => Path.Combine(directory, module))
                                      .Where(f => Directory.Exists(f))
                                      .ToList();
                }

                requiredModules.Add(outputModuleDirectory);
                foreach (var nestedModule in nestedModules)
                {
                    var assemblyPath         = Directory.GetFiles(outputModuleDirectory, nestedModule, SearchOption.AllDirectories).FirstOrDefault();
                    var proxy                = new CmdletLoader();
                    var newModuleMetadata    = proxy.GetModuleMetadata(assemblyPath, requiredModules);
                    var serializedCmdletName = nestedModule + ".json";
                    var serializedCmdletFile = Path.Combine(serializedCmdletsDirectory, serializedCmdletName);
                    SerializeCmdlets(serializedCmdletFile, newModuleMetadata);
                }
            }
            else
            {
                Console.WriteLine("No nested module(s) found for " + moduleName + " -- skipping serialization step.");
            }
        }
Beispiel #2
0
        /// <summary>
        /// Given a set of directory paths containing PowerShell module folders,
        /// analyze the breaking changes in the modules and report any issues
        ///
        /// Filters can be added to find breaking changes for specific modules
        /// </summary>
        /// <param name="cmdletProbingDirs">Set of directory paths containing PowerShell module folders to be checked for breaking changes.</param>
        /// <param name="directoryFilter">Function that filters the directory paths to be checked.</param>
        /// <param name="cmdletFilter">Function that filters the cmdlets to be checked.</param>
        public void Analyze(
            IEnumerable <string> cmdletProbingDirs,
            Func <IEnumerable <string>, IEnumerable <string> > directoryFilter,
            Func <string, bool> cmdletFilter,
            IEnumerable <string> modulesToAnalyze)
        {
            var processedHelpFiles = new List <string>();
            var issueLogger        = Logger.CreateLogger <BreakingChangeIssue>("BreakingChangeIssues.csv");

            if (directoryFilter != null)
            {
                cmdletProbingDirs = directoryFilter(cmdletProbingDirs);
            }

            foreach (var baseDirectory in cmdletProbingDirs.Where(s => !s.Contains("ServiceManagement") &&
                                                                  !ModuleFilter.IsAzureStackModule(s) && Directory.Exists(Path.GetFullPath(s))))
            {
                var probingDirectories = new List <string> {
                    baseDirectory
                };

                // Add current directory for probing
                probingDirectories.AddRange(Directory.EnumerateDirectories(Path.GetFullPath(baseDirectory)));

                foreach (var directory in probingDirectories)
                {
                    if (modulesToAnalyze != null &&
                        modulesToAnalyze.Any() &&
                        !modulesToAnalyze.Any(m => directory.EndsWith(m)))
                    {
                        continue;
                    }

                    var service = Path.GetFileName(directory);

                    var manifestFiles = Directory.EnumerateFiles(directory, "*.psd1").ToList();

                    if (manifestFiles.Count > 1)
                    {
                        manifestFiles = manifestFiles.Where(f => Path.GetFileName(f).IndexOf(service) >= 0).ToList();
                    }

                    if (manifestFiles.Count == 0)
                    {
                        continue;
                    }

                    var psd1            = manifestFiles.FirstOrDefault();
                    var parentDirectory = Directory.GetParent(psd1).FullName;
                    var psd1FileName    = Path.GetFileName(psd1);
                    var powershell      = PowerShell.Create();

                    var script = $"Import-LocalizedData -BaseDirectory {parentDirectory} -FileName {psd1FileName} -BindingVariable ModuleMetadata;";
                    powershell.AddScript($"{script} $ModuleMetadata.NestedModules;");
                    var cmdletResult  = powershell.Invoke();
                    var nestedModules = cmdletResult.Where(c => c != null).Select(c => c.ToString()).Select(c => (c.StartsWith(".") ? c.Substring(2) : c)).ToList();

                    powershell.AddScript($"{script} $ModuleMetadata.RequiredModules | % {{ $_[\"ModuleName\"] }};");
                    cmdletResult = powershell.Invoke();
                    var requiredModules = cmdletResult.Where(c => !c.ToString().StartsWith(".")).Select(c => c.ToString()).ToList();

                    if (!nestedModules.Any())
                    {
                        continue;
                    }

                    Directory.SetCurrentDirectory(directory);

                    requiredModules = requiredModules.Join(cmdletProbingDirs,
                                                           module => 1,
                                                           dir => 1,
                                                           (module, dir) => Path.Combine(dir, module))
                                      .Where(Directory.Exists)
                                      .ToList();

                    requiredModules.Add(directory);

                    foreach (var nestedModule in nestedModules)
                    {
                        var assemblyFile     = Directory.GetFiles(parentDirectory, nestedModule, SearchOption.AllDirectories).FirstOrDefault();
                        var assemblyFileName = Path.GetFileName(assemblyFile);
                        if (!File.Exists(assemblyFile))
                        {
                            continue;
                        }

                        issueLogger.Decorator.AddDecorator(a => a.AssemblyFileName = assemblyFileName, "AssemblyFileName");
                        processedHelpFiles.Add(assemblyFileName);
// TODO: Remove IfDef
#if NETSTANDARD
                        var proxy = new CmdletLoader();
#else
                        var proxy = EnvironmentHelpers.CreateProxy <CmdletLoader>(directory, out _appDomain);
#endif
                        var newModuleMetadata = proxy.GetModuleMetadata(assemblyFile, requiredModules);

                        var fileName      = assemblyFileName + ".json";
                        var executingPath =
                            Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().CodeBase).AbsolutePath);

                        var filePath = executingPath + "\\SerializedCmdlets\\" + fileName;

#if SERIALIZE
                        SerializeCmdlets(filePath, newModuleMetadata);
#endif

                        if (!File.Exists(filePath))
                        {
                            continue;
                        }

                        var oldModuleMetadata = DeserializeCmdlets(filePath);

                        if (cmdletFilter != null)
                        {
                            var output = string.Format("Before filter\nOld module cmdlet count: {0}\nNew module cmdlet count: {1}",
                                                       oldModuleMetadata.Cmdlets.Count, newModuleMetadata.Cmdlets.Count);

                            output += string.Format("\nCmdlet file: {0}", assemblyFileName);

                            oldModuleMetadata.FilterCmdlets(cmdletFilter);
                            newModuleMetadata.FilterCmdlets(cmdletFilter);

                            output += string.Format("After filter\nOld module cmdlet count: {0}\nNew module cmdlet count: {1}",
                                                    oldModuleMetadata.Cmdlets.Count, newModuleMetadata.Cmdlets.Count);

                            foreach (var cmdlet in oldModuleMetadata.Cmdlets)
                            {
                                output += string.Format("\n\tOld cmdlet - {0}", cmdlet.Name);
                            }

                            foreach (var cmdlet in newModuleMetadata.Cmdlets)
                            {
                                output += string.Format("\n\tNew cmdlet - {0}", cmdlet.Name);
                            }

                            issueLogger.WriteMessage(output + Environment.NewLine);
                        }

                        RunBreakingChangeChecks(oldModuleMetadata, newModuleMetadata, issueLogger);
// TODO: Remove IfDef code
#if !NETSTANDARD
                        AppDomain.Unload(_appDomain);
#endif
                    }
                }
            }
        }
        public void Analyze(IEnumerable <string> cmdletProbingDirs,
                            Func <IEnumerable <string>, IEnumerable <string> > directoryFilter,
                            Func <string, bool> cmdletFilter,
                            IEnumerable <string> modulesToAnalyze)
        {
            var savedDirectory     = Directory.GetCurrentDirectory();
            var processedHelpFiles = new List <string>();
            var issueLogger        = Logger.CreateLogger <SignatureIssue>(_signatureIssueReportLoggerName);

            var probingDirectories = new List <string>();

            if (directoryFilter != null)
            {
                cmdletProbingDirs = directoryFilter(cmdletProbingDirs);
            }

            foreach (var baseDirectory in cmdletProbingDirs.Where(s => !s.Contains("ServiceManagement") &&
                                                                  !s.Contains("Stack") && Directory.Exists(Path.GetFullPath(s))))
            {
                //Add current directory for probing
                probingDirectories.Add(baseDirectory);
                probingDirectories.AddRange(Directory.EnumerateDirectories(Path.GetFullPath(baseDirectory)));

                foreach (var directory in probingDirectories)
                {
                    if (modulesToAnalyze != null &&
                        modulesToAnalyze.Any() &&
                        !modulesToAnalyze.Any(m => directory.EndsWith(m)))
                    {
                        continue;
                    }

                    var service       = Path.GetFileName(directory);
                    var manifestFiles = Directory.EnumerateFiles(directory, "*.psd1").ToList();
                    if (manifestFiles.Count > 1)
                    {
                        manifestFiles = manifestFiles.Where(f => Path.GetFileName(f).IndexOf(service) >= 0).ToList();
                    }

                    if (!manifestFiles.Any())
                    {
                        continue;
                    }

                    var psd1            = manifestFiles.FirstOrDefault();
                    var parentDirectory = Directory.GetParent(psd1).FullName;
                    var psd1FileName    = Path.GetFileName(psd1);
                    IEnumerable <string> nestedModules   = null;
                    List <string>        requiredModules = null;
                    var powershell = PowerShell.Create(RunspaceMode.NewRunspace);
                    var script     = $"Import-LocalizedData -BaseDirectory {parentDirectory} -FileName {psd1FileName}" +
                                     " -BindingVariable ModuleMetadata; $ModuleMetadata.NestedModules; $ModuleMetadata.RequiredModules | % { $_[\"ModuleName\"] };";
                    powershell.AddScript(script);
                    var cmdletResult = powershell.Invoke();
                    nestedModules   = cmdletResult.Where(c => c != null && c.ToString().StartsWith(".")).Select(c => c.ToString().Substring(2));
                    requiredModules = cmdletResult.Where(c => c != null && !c.ToString().StartsWith(".")).Select(c => c.ToString()).ToList();

                    if (!nestedModules.Any())
                    {
                        continue;
                    }

                    Directory.SetCurrentDirectory(directory);

                    requiredModules = requiredModules.Join(cmdletProbingDirs,
                                                           module => 1,
                                                           dir => 1,
                                                           (module, dir) => Path.Combine(dir, module))
                                      .Where(Directory.Exists)
                                      .ToList();

                    requiredModules.Add(directory);

                    foreach (var nestedModule in nestedModules)
                    {
                        var assemblyFile = Directory.GetFiles(parentDirectory, nestedModule, SearchOption.AllDirectories).FirstOrDefault();
                        if (!File.Exists(assemblyFile))
                        {
                            continue;
                        }

                        issueLogger.Decorator.AddDecorator(a => a.AssemblyFileName = assemblyFile, "AssemblyFileName");
                        processedHelpFiles.Add(assemblyFile);
// TODO: Remove IfDef
#if NETSTANDARD
                        var proxy = new CmdletLoader();
#else
                        var proxy = EnvironmentHelpers.CreateProxy <CmdletLoader>(directory, out _appDomain);
#endif
                        var module  = proxy.GetModuleMetadata(assemblyFile, requiredModules);
                        var cmdlets = module.Cmdlets;

                        if (cmdletFilter != null)
                        {
                            cmdlets = cmdlets.Where(cmdlet => cmdletFilter(cmdlet.Name)).ToList();
                        }

                        foreach (var cmdlet in cmdlets)
                        {
                            Logger.WriteMessage("Processing cmdlet '{0}'", cmdlet.ClassName);
                            const string defaultRemediation = "Determine if the cmdlet should implement ShouldProcess and " +
                                                              "if so determine if it should implement Force / ShouldContinue";
                            if (!cmdlet.SupportsShouldProcess && cmdlet.HasForceSwitch)
                            {
                                issueLogger.LogSignatureIssue(
                                    cmdlet: cmdlet,
                                    severity: 0,
                                    problemId: SignatureProblemId.ForceWithoutShouldProcessAttribute,
                                    description: string.Format("{0} Has  -Force parameter but does not set the SupportsShouldProcess " +
                                                               "property to true in the Cmdlet attribute.", cmdlet.Name),
                                    remediation: defaultRemediation);
                            }
                            if (!cmdlet.SupportsShouldProcess && cmdlet.ConfirmImpact != ConfirmImpact.Medium)
                            {
                                issueLogger.LogSignatureIssue(
                                    cmdlet: cmdlet,
                                    severity: 2,
                                    problemId: SignatureProblemId.ConfirmLeveleWithNoShouldProcess,
                                    description:
                                    string.Format("{0} Changes the ConfirmImpact but does not set the " +
                                                  "SupportsShouldProcess property to true in the cmdlet attribute.",
                                                  cmdlet.Name),
                                    remediation: defaultRemediation);
                            }
                            if (!cmdlet.SupportsShouldProcess && cmdlet.IsShouldProcessVerb)
                            {
                                issueLogger.LogSignatureIssue(
                                    cmdlet: cmdlet,
                                    severity: 1,
                                    problemId: SignatureProblemId.ActionIndicatesShouldProcess,
                                    description:
                                    string.Format(
                                        "{0} Does not support ShouldProcess but the cmdlet verb {1} indicates that it should.",
                                        cmdlet.Name, cmdlet.VerbName),
                                    remediation: defaultRemediation);
                            }
                            if (cmdlet.ConfirmImpact != ConfirmImpact.Medium)
                            {
                                issueLogger.LogSignatureIssue(
                                    cmdlet: cmdlet,
                                    severity: 2,
                                    problemId: SignatureProblemId.ConfirmLevelChange,
                                    description:
                                    string.Format("{0} changes the confirm impact.  Please ensure that the " +
                                                  "change in ConfirmImpact is justified", cmdlet.Name),
                                    remediation:
                                    "Verify that ConfirmImpact is changed appropriately by the cmdlet. " +
                                    "It is very rare for a cmdlet to change the ConfirmImpact.");
                            }
                            if (!cmdlet.IsApprovedVerb)
                            {
                                issueLogger.LogSignatureIssue(
                                    cmdlet: cmdlet,
                                    severity: 1,
                                    problemId: SignatureProblemId.CmdletWithUnapprovedVerb,
                                    description:
                                    string.Format(
                                        "{0} uses the verb '{1}', which is not on the list of approved " +
                                        "verbs for PowerShell commands. Use the cmdlet 'Get-Verb' to see " +
                                        "the full list of approved verbs and consider renaming the cmdlet.",
                                        cmdlet.Name, cmdlet.VerbName),
                                    remediation: "Consider renaming the cmdlet to use an approved verb for PowerShell.");
                            }

                            if (!cmdlet.HasSingularNoun)
                            {
                                issueLogger.LogSignatureIssue(
                                    cmdlet: cmdlet,
                                    severity: 1,
                                    problemId: SignatureProblemId.CmdletWithPluralNoun,
                                    description:
                                    string.Format(
                                        "{0} uses the noun '{1}', which does not follow the enforced " +
                                        "naming convention of using a singular noun for a cmdlet name.",
                                        cmdlet.Name, cmdlet.NounName),
                                    remediation: "Consider using a singular noun for the cmdlet name.");
                            }

                            if (!cmdlet.OutputTypes.Any())
                            {
                                issueLogger.LogSignatureIssue(
                                    cmdlet: cmdlet,
                                    severity: 1,
                                    problemId: SignatureProblemId.CmdletWithNoOutputType,
                                    description:
                                    string.Format(
                                        "Cmdlet '{0}' has no defined output type.", cmdlet.Name),
                                    remediation: "Add an OutputType attribute that declares the type of the object(s) returned " +
                                    "by this cmdlet. If this cmdlet returns no output, please set the output " +
                                    "type to 'bool' and make sure to implement the 'PassThru' parameter.");
                            }

                            foreach (var parameter in cmdlet.GetParametersWithPluralNoun())
                            {
                                issueLogger.LogSignatureIssue(
                                    cmdlet: cmdlet,
                                    severity: 1,
                                    problemId: SignatureProblemId.ParameterWithPluralNoun,
                                    description:
                                    string.Format(
                                        "Parameter {0} of cmdlet {1} does not follow the enforced " +
                                        "naming convention of using a singular noun for a parameter name.",
                                        parameter.Name, cmdlet.Name),
                                    remediation: "Consider using a singular noun for the parameter name.");
                            }

                            foreach (var parameterSet in cmdlet.ParameterSets)
                            {
                                if (parameterSet.Name.Contains(" "))
                                {
                                    issueLogger.LogSignatureIssue(
                                        cmdlet: cmdlet,
                                        severity: 1,
                                        problemId: SignatureProblemId.ParameterSetWithSpace,
                                        description:
                                        string.Format(
                                            "Parameter set '{0}' of cmdlet '{1}' contains a space, which " +
                                            "is discouraged for PowerShell parameter sets.",
                                            parameterSet.Name, cmdlet.Name),
                                        remediation: "Remove the space(s) in the parameter set name.");
                                }

                                if (parameterSet.Parameters.Any(p => p.Position >= 4))
                                {
                                    issueLogger.LogSignatureIssue(
                                        cmdlet: cmdlet,
                                        severity: 1,
                                        problemId: SignatureProblemId.ParameterWithOutOfRangePosition,
                                        description:
                                        string.Format(
                                            "Parameter set '{0}' of cmdlet '{1}' contains at least one parameter " +
                                            "with a position larger than four, which is discouraged.",
                                            parameterSet.Name, cmdlet.Name),
                                        remediation: "Limit the number of positional parameters in a single parameter set to " +
                                        "four or fewer.");
                                }
                            }

                            if (cmdlet.ParameterSets.Count > 2 && cmdlet.DefaultParameterSetName == "__AllParameterSets")
                            {
                                issueLogger.LogSignatureIssue(
                                    cmdlet: cmdlet,
                                    severity: 1,
                                    problemId: SignatureProblemId.MultipleParameterSetsWithNoDefault,
                                    description:
                                    string.Format(
                                        "Cmdlet '{0}' has multiple parameter sets, but no defined default parameter set.",
                                        cmdlet.Name),
                                    remediation: "Define a default parameter set in the cmdlet attribute.");
                            }
                        }
// TODO: Remove IfDef code
#if !NETSTANDARD
                        AppDomain.Unload(_appDomain);
#endif
                        issueLogger.Decorator.Remove("AssemblyFileName");
                    }
                    Directory.SetCurrentDirectory(savedDirectory);
                }
            }
        }
Beispiel #4
0
        /// <summary>
        /// Determine which version bump should be applied to a module version.
        /// This will compare the cmdlet assemblies in the output (built) module manifest with
        /// the cmdlet assemblies in the saved gallery folder.
        /// </summary>
        /// <returns>Version enum representing the version bump to be applied.</returns>
        public Version GetVersionBumpUsingGallery()
        {
            Console.WriteLine("Comparing the cmdlet assumblies with seemblies in the saved gallery folder...");
            var outputModuleManifestPath      = _fileHelper.OutputModuleManifestPath;
            var outputModuleDirectory         = _fileHelper.OutputModuleDirectory;
            var outputDirectories             = _fileHelper.OutputDirectories;
            var serializedCmdletsDirectory    = _fileHelper.SerializedCmdletsDirectory;
            var galleryModuleVersionDirectory = _fileHelper.GalleryModuleVersionDirectory;
            var moduleName = _fileHelper.ModuleName;
            IEnumerable <string> nestedModules = null;

            using (PowerShell powershell = PowerShell.Create())
            {
                powershell.AddScript("(Test-ModuleManifest -Path " + outputModuleManifestPath + ").NestedModules");
                var cmdletResult = powershell.Invoke();
                nestedModules = cmdletResult.Select(c => c.ToString() + ".dll");
            }

            Version versionBump = Version.PATCH;

            if (nestedModules.Any())
            {
                var           tempVersionBump        = Version.PATCH;
                var           issueLogger            = _logger.CreateLogger <BreakingChangeIssue>("BreakingChangeIssues.csv");
                List <string> requiredModules        = null;
                List <string> galleryRequiredModules = null;
                using (PowerShell powershell = PowerShell.Create())
                {
                    powershell.AddScript("(Test-ModuleManifest -Path " + outputModuleManifestPath + ").RequiredModules");
                    var cmdletResult = powershell.Invoke();
                    requiredModules = cmdletResult.Select(c => c.ToString())
                                      .Join(outputDirectories,
                                            module => 1,
                                            directory => 1,
                                            (module, directory) => Path.Combine(directory, module))
                                      .Where(f => Directory.Exists(f))
                                      .ToList();
                    galleryRequiredModules = cmdletResult.Select(c => c.ToString())
                                             .Select(f => Directory.GetDirectories(outputModuleDirectory, f).FirstOrDefault())
                                             .Select(f => Directory.GetDirectories(f).FirstOrDefault())
                                             .ToList();
                }

                requiredModules.Add(outputModuleDirectory);
                galleryRequiredModules.Add(galleryModuleVersionDirectory);
                foreach (var nestedModule in nestedModules)
                {
                    var assemblyPath      = Directory.GetFiles(galleryModuleVersionDirectory, nestedModule).FirstOrDefault();
                    var proxy             = new CmdletLoader();
                    var oldModuleMetadata = proxy.GetModuleMetadata(assemblyPath, galleryRequiredModules);
                    assemblyPath = Directory.GetFiles(outputModuleDirectory, nestedModule).FirstOrDefault();
                    proxy        = new CmdletLoader();
                    var newModuleMetadata = proxy.GetModuleMetadata(assemblyPath, requiredModules);
                    CmdletLoader.ModuleMetadata = oldModuleMetadata;
                    issueLogger.Decorator.AddDecorator(a => a.AssemblyFileName = assemblyPath, "AssemblyFileName");
                    CheckBreakingChangesInModules(oldModuleMetadata, newModuleMetadata, issueLogger);
                    if (issueLogger.Records.Any())
                    {
                        tempVersionBump = Version.MAJOR;
                    }
                    else if (!oldModuleMetadata.Equals(newModuleMetadata))
                    {
                        tempVersionBump = Version.MINOR;
                    }

                    if (tempVersionBump == Version.MAJOR)
                    {
                        versionBump = Version.MAJOR;
                    }
                    else if (tempVersionBump == Version.MINOR && versionBump == Version.PATCH)
                    {
                        versionBump = Version.MINOR;
                    }
                }
            }
            else
            {
                throw new NullReferenceException("No nested modules found for " + moduleName);
            }

            return(versionBump);
        }
Beispiel #5
0
        /// <summary>
        /// Determine what version bump should be applied to a module version.
        /// This will compare the cmdlet assemblies in the output (built) module manifest with
        /// the corresponding deserialized module metadata from the JSON file.
        /// </summary>
        /// <param name="serialize">Whether or not the module metadata should be serialized.</param>
        /// <returns>Version enum representing the version bump to be applied.</returns>
        public Version GetVersionBumpUsingSerialized(bool serialize = true)
        {
            Console.WriteLine("Comparing the cmdlet assumblies with metadata from JSON file...");
            var outputModuleManifestPath   = _fileHelper.OutputModuleManifestPath;
            var outputModuleDirectory      = _fileHelper.OutputModuleDirectory;
            var outputDirectories          = _fileHelper.OutputDirectories;
            var serializedCmdletsDirectory = _fileHelper.SerializedCmdletsDirectory;
            var moduleName = _fileHelper.ModuleName;
            IEnumerable <string> nestedModules = null;

            using (PowerShell powershell = PowerShell.Create())
            {
                powershell.AddScript("(Test-ModuleManifest -Path " + outputModuleManifestPath + ").NestedModules");
                var cmdletResult = powershell.Invoke();
                nestedModules = cmdletResult.Select(c => c.ToString());
            }

            Version versionBump = Version.PATCH;

            if (nestedModules.Any())
            {
                var           tempVersionBump = Version.PATCH;
                var           issueLogger     = _logger.CreateLogger <BreakingChangeIssue>("BreakingChangeIssues.csv");
                List <string> requiredModules = null;
                using (PowerShell powershell = PowerShell.Create())
                {
                    powershell.AddScript("(Test-ModuleManifest -Path " + outputModuleManifestPath + ").RequiredModules");
                    var cmdletResult = powershell.Invoke();
                    requiredModules = cmdletResult.Select(c => c.ToString())
                                      .Join(outputDirectories,
                                            module => 1,
                                            directory => 1,
                                            (module, directory) => Path.Combine(directory, module))
                                      .Where(f => Directory.Exists(f))
                                      .ToList();
                }

                requiredModules.Add(outputModuleDirectory);
                foreach (var nestedModuleName in nestedModules)
                {
                    // Handcrafted modules assume its nested module always is DLL file.
                    var nestedModule = nestedModuleName + ".dll";
                    var assemblyPath = Directory.GetFiles(outputModuleDirectory, nestedModule, SearchOption.AllDirectories).FirstOrDefault();

                    // However we support PSM1, PSD1 other nested module type. Skip this check and we need to use a different design soon.
                    if (assemblyPath == null)
                    {
                        continue;
                    }
                    var proxy                = new CmdletLoader();
                    var newModuleMetadata    = proxy.GetModuleMetadata(assemblyPath, requiredModules);
                    var serializedCmdletName = nestedModule + ".json";
                    var serializedCmdletFile = Directory.GetFiles(serializedCmdletsDirectory, serializedCmdletName).FirstOrDefault();
                    if (serializedCmdletFile == null)
                    {
                        var currentColor = Console.ForegroundColor;
                        Console.ForegroundColor = ConsoleColor.Yellow;
                        Console.WriteLine($"Warning: {nestedModule} does not have a previously serialized cmdlet for comparison.");
                        Console.ForegroundColor = currentColor;
                        var newCmdletFile = Path.Join(serializedCmdletsDirectory, serializedCmdletName);
                        SerializeCmdlets(newCmdletFile, newModuleMetadata);
                        continue;
                    }
                    var oldModuleMetadata = DeserializeCmdlets(serializedCmdletFile);
                    CmdletLoader.ModuleMetadata = oldModuleMetadata;
                    issueLogger.Decorator.AddDecorator(a => a.AssemblyFileName = assemblyPath, "AssemblyFileName");
                    CheckBreakingChangesInModules(oldModuleMetadata, newModuleMetadata, issueLogger);
                    if (issueLogger.Records.Any())
                    {
                        var currentColor = Console.ForegroundColor;
                        Console.ForegroundColor = ConsoleColor.Yellow;
                        Console.WriteLine($"Detected below {issueLogger.Records.Count} breack change(s):");
                        foreach (IReportRecord record in issueLogger.Records)
                        {
                            Console.WriteLine(((BreakingChangeIssue)record).Target + " " + record.ProblemId + " " + record.Description);
                        }
                        Console.ForegroundColor = currentColor;
                        tempVersionBump         = Version.MAJOR;
                    }
                    else if (!oldModuleMetadata.Equals(newModuleMetadata))
                    {
                        tempVersionBump = Version.MINOR;
                    }

                    if (tempVersionBump != Version.PATCH && serialize)
                    {
                        SerializeCmdlets(serializedCmdletFile, newModuleMetadata);
                    }

                    if (tempVersionBump == Version.MAJOR)
                    {
                        versionBump = Version.MAJOR;
                    }
                    else if (tempVersionBump == Version.MINOR && versionBump == Version.PATCH)
                    {
                        versionBump = Version.MINOR;
                    }
                }
            }
            else
            {
                return(Version.PATCH);
            }

            return(versionBump);
        }
Beispiel #6
0
        /// <summary>
        /// Determine what version bump should be applied to a module version.
        /// This will compare the cmdlet assemblies in the output (built) module manifest with
        /// the corresponding deserialized module metadata from the JSON file.
        /// </summary>
        /// <param name="serialize">Whether or not the module metadata should be serialized.</param>
        /// <returns>Version enum representing the version bump to be applied.</returns>
        public Version GetVersionBumpUsingSerialized(bool serialize = true)
        {
            var outputModuleManifestPath   = _fileHelper.OutputModuleManifestPath;
            var outputModuleDirectory      = _fileHelper.OutputModuleDirectory;
            var outputDirectories          = _fileHelper.OutputDirectories;
            var serializedCmdletsDirectory = _fileHelper.SerializedCmdletsDirectory;
            var moduleName = _fileHelper.ModuleName;
            IEnumerable <string> nestedModules = null;

            using (PowerShell powershell = PowerShell.Create())
            {
                powershell.AddScript("(Test-ModuleManifest -Path " + outputModuleManifestPath + ").NestedModules");
                var cmdletResult = powershell.Invoke();
                nestedModules = cmdletResult.Select(c => c.ToString() + ".dll");
            }

            Version versionBump = Version.PATCH;

            if (nestedModules.Any())
            {
                var           tempVersionBump = Version.PATCH;
                var           issueLogger     = _logger.CreateLogger <BreakingChangeIssue>("BreakingChangeIssues.csv");
                List <string> requiredModules = null;
                using (PowerShell powershell = PowerShell.Create())
                {
                    powershell.AddScript("(Test-ModuleManifest -Path " + outputModuleManifestPath + ").RequiredModules");
                    var cmdletResult = powershell.Invoke();
                    requiredModules = cmdletResult.Select(c => c.ToString())
                                      .Join(outputDirectories,
                                            module => 1,
                                            directory => 1,
                                            (module, directory) => Path.Combine(directory, module))
                                      .Where(f => Directory.Exists(f))
                                      .ToList();
                }

                requiredModules.Add(outputModuleDirectory);
                foreach (var nestedModule in nestedModules)
                {
                    var assemblyPath         = Directory.GetFiles(outputModuleDirectory, nestedModule, SearchOption.AllDirectories).FirstOrDefault();
                    var proxy                = new CmdletLoader();
                    var newModuleMetadata    = proxy.GetModuleMetadata(assemblyPath, requiredModules);
                    var serializedCmdletName = nestedModule + ".json";
                    var serializedCmdletFile = Directory.GetFiles(serializedCmdletsDirectory, serializedCmdletName).FirstOrDefault();
                    if (serializedCmdletFile == null)
                    {
                        var currentColor = Console.ForegroundColor;
                        Console.ForegroundColor = ConsoleColor.Yellow;
                        Console.WriteLine($"Warning: {nestedModule} does not have a previously serialized cmdlet for comparison.");
                        Console.ForegroundColor = currentColor;
                        var newCmdletFile = Path.Join(serializedCmdletsDirectory, serializedCmdletName);
                        SerializeCmdlets(newCmdletFile, newModuleMetadata);
                        continue;
                    }
                    var oldModuleMetadata = DeserializeCmdlets(serializedCmdletFile);
                    CmdletLoader.ModuleMetadata = oldModuleMetadata;
                    issueLogger.Decorator.AddDecorator(a => a.AssemblyFileName = assemblyPath, "AssemblyFileName");
                    CheckBreakingChangesInModules(oldModuleMetadata, newModuleMetadata, issueLogger);
                    if (issueLogger.Records.Any())
                    {
                        tempVersionBump = Version.MAJOR;
                    }
                    else if (!oldModuleMetadata.Equals(newModuleMetadata))
                    {
                        tempVersionBump = Version.MINOR;
                    }

                    if (tempVersionBump != Version.PATCH && serialize)
                    {
                        SerializeCmdlets(serializedCmdletFile, newModuleMetadata);
                    }

                    if (tempVersionBump == Version.MAJOR)
                    {
                        versionBump = Version.MAJOR;
                    }
                    else if (tempVersionBump == Version.MINOR && versionBump == Version.PATCH)
                    {
                        versionBump = Version.MINOR;
                    }
                }
            }
            else
            {
                return(Version.PATCH);
            }

            return(versionBump);
        }