示例#1
0
        /// <summary>
        /// Update the ModuleVersion of the bumped module in any dependent module's RequiredModule field.
        /// </summary>
        private void UpdateDependentModules()
        {
            var moduleName         = _fileHelper.ModuleName;
            var projectDirectories = _fileHelper.ProjectDirectories;

            foreach (var projectDirectory in projectDirectories)
            {
                var moduleManifestPaths = Directory.GetFiles(projectDirectory, "*.psd1", SearchOption.AllDirectories)
                                          .Where(f => !f.Contains("Netcore") &&
                                                 !f.Contains("bin") &&
                                                 !f.Contains("dll-Help") &&
                                                 !ModuleFilter.IsAzureStackModule(f))
                                          .ToList();
                foreach (var moduleManifestPath in moduleManifestPaths)
                {
                    var file    = File.ReadAllLines(moduleManifestPath);
                    var pattern = @"ModuleName(\s*)=(\s*)(['\""])" + moduleName + @"(['\""])(\s*);(\s*)ModuleVersion(\s*)=(\s*)(['\""])" + "\\d+(\\.\\d+)+" + @"(['\""])";
                    if (file.Where(l => Regex.IsMatch(l, pattern)).Any())
                    {
                        var updatedFile = file.Select(l => Regex.Replace(l, pattern, "ModuleName = '" + moduleName + "'; ModuleVersion = '" + _newVersion + "'"));
                        File.WriteAllLines(moduleManifestPath, updatedFile);
                    }
                }
            }
        }
示例#2
0
        /// <summary>
        /// Update the ModuleVersion of the bumped module in any dependent module's RequiredModule field.
        /// </summary>
        private void UpdateDependentModules()
        {
            var moduleName         = _fileHelper.ModuleName;
            var projectDirectories = _fileHelper.ProjectDirectories;
            var outputDirectories  = _fileHelper.OutputDirectories;

            foreach (var projectDirectory in projectDirectories)
            {
                var moduleManifestPaths = Directory.GetFiles(projectDirectory, "*.psd1", SearchOption.AllDirectories)
                                          .Where(f => !f.Contains("Netcore") &&
                                                 !f.Contains("bin") &&
                                                 !f.Contains("dll-Help") &&
                                                 !ModuleFilter.IsAzureStackModule(f))
                                          .ToList();
                foreach (var moduleManifestPath in moduleManifestPaths)
                {
                    var file    = File.ReadAllLines(moduleManifestPath);
                    var pattern = @"ModuleName(\s*)=(\s*)(['\""])" + moduleName + @"(['\""])(\s*);(\s*)ModuleVersion(\s*)=(\s*)(['\""])" + _oldVersion + @"(['\""])";
                    if (file.Where(l => Regex.IsMatch(l, pattern)).Any())
                    {
                        var updatedFile = file.Select(l => Regex.Replace(l, pattern, "ModuleName = '" + moduleName + "'; ModuleVersion = '" + _newVersion + "'"));
                        File.WriteAllLines(moduleManifestPath, updatedFile);
                        var updatedModuleName = Path.GetFileNameWithoutExtension(moduleManifestPath);
                        foreach (var outputDirectory in outputDirectories)
                        {
                            var outputModuleDirectory = Directory.GetDirectories(outputDirectory, updatedModuleName).FirstOrDefault();
                            if (outputModuleDirectory == null)
                            {
                                continue;
                            }

                            var outputModuleManifestPath = Directory.GetFiles(outputModuleDirectory, updatedModuleName + ".psd1").FirstOrDefault();
                            if (outputModuleManifestPath == null)
                            {
                                continue;
                            }

                            File.WriteAllLines(outputModuleManifestPath, updatedFile);
                        }
                    }
                }
            }
        }
示例#3
0
        /// <summary>
        /// Validate version bump of changed modules or a specified module.
        /// </summary>
        private static void ValidateVersionBump()
        {
            var changedModules = new List <string>();

            foreach (var directory in _projectDirectories)
            {
                var changeLogs = Directory.GetFiles(directory, "ChangeLog.md", SearchOption.AllDirectories)
                                 .Where(f => !ModuleFilter.IsAzureStackModule(f))
                                 .Select(f => GetModuleManifestPath(Directory.GetParent(f).FullName))
                                 .Where(m => !string.IsNullOrEmpty(m) && m.Contains(_moduleNameFilter))
                                 .ToList();
                changedModules.AddRange(changeLogs);
            }

            foreach (var projectModuleManifestPath in changedModules)
            {
                var moduleFileName = Path.GetFileName(projectModuleManifestPath);
                var moduleName     = moduleFileName.Replace(".psd1", "");

                var outputModuleManifest = _outputDirectories
                                           .SelectMany(d => Directory.GetDirectories(d, moduleName))
                                           .SelectMany(d => Directory.GetFiles(d, moduleFileName))
                                           .ToList();
                if (outputModuleManifest.Count == 0)
                {
                    throw new FileNotFoundException("No module manifest file found in output directories");
                }
                else if (outputModuleManifest.Count > 1)
                {
                    throw new IndexOutOfRangeException("Multiple module manifest files found in output directories");
                }

                var outputModuleManifestFile = outputModuleManifest.FirstOrDefault();

                var validatorFileHelper = new VersionFileHelper(_rootDirectory, outputModuleManifestFile, projectModuleManifestPath);

                _versionValidator = new VersionValidator(validatorFileHelper);

                _versionValidator.ValidateAllVersionBumps();
            }
        }
示例#4
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
                    }
                }
            }
        }
示例#5
0
        /// <summary>
        /// Bump the version of changed modules or a specified module.
        /// </summary>
        private static void BumpVersions()
        {
            var changedModules = new List <string>();

            foreach (var directory in _projectDirectories)
            {
                var changeLogs = Directory.GetFiles(directory, "ChangeLog.md", SearchOption.AllDirectories)
                                 .Where(f => !ModuleFilter.IsAzureStackModule(f) && IsChangeLogUpdated(f))
                                 .Select(f => GetModuleManifestPath(Directory.GetParent(f).FullName))
                                 .Where(m => m.Contains(_moduleNameFilter))
                                 .ToList();
                changedModules.AddRange(changeLogs);
            }

            var executingAssemblyPath      = Assembly.GetExecutingAssembly().Location;
            var versionControllerDirectory = Directory.GetParent(executingAssemblyPath).FullName;
            var miniVersionFile            = Path.Combine(versionControllerDirectory, "MinimalVersion.csv");

            if (File.Exists(miniVersionFile))
            {
                var lines = File.ReadAllLines(miniVersionFile).Skip(1).Where(c => !string.IsNullOrEmpty(c));
                foreach (var line in lines)
                {
                    var cols = line.Split(",").Select(c => c.StartsWith("\"") ? c.Substring(1) : c)
                               .Select(c => c.EndsWith("\"") ? c.Substring(0, c.Length - 1) : c)
                               .Select(c => c.Trim()).ToArray();
                    if (cols.Length >= 2)
                    {
                        _minimalVersion.Add(cols[0], new AzurePSVersion(cols[1]));
                    }
                }
            }

            foreach (var projectModuleManifestPath in changedModules)
            {
                var moduleFileName = Path.GetFileName(projectModuleManifestPath);
                var moduleName     = moduleFileName.Replace(".psd1", "");

                var outputModuleManifest = _outputDirectories
                                           .SelectMany(d => Directory.GetDirectories(d, moduleName))
                                           .SelectMany(d => Directory.GetFiles(d, moduleFileName))
                                           .ToList();
                if (outputModuleManifest.Count == 0)
                {
                    throw new FileNotFoundException("No module manifest file found in output directories");
                }
                else if (outputModuleManifest.Count > 1)
                {
                    throw new IndexOutOfRangeException("Multiple module manifest files found in output directories");
                }

                var outputModuleManifestFile = outputModuleManifest.FirstOrDefault();

                _versionBumper = new VersionBumper(new VersionFileHelper(_rootDirectory, outputModuleManifestFile, projectModuleManifestPath));

                if (_minimalVersion.ContainsKey(moduleName))
                {
                    _versionBumper.MinimalVersion = _minimalVersion[moduleName];
                }

                _versionBumper.BumpAllVersions();
            }
        }
示例#6
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))))
            {
                SharedAssemblyLoader.Load(baseDirectory);
                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);

                    string moduleName = psd1FileName.Replace(".psd1", "");

                    Console.WriteLine(directory);
                    Directory.SetCurrentDirectory(directory);

                    issueLogger.Decorator.AddDecorator(a => a.AssemblyFileName = moduleName, "AssemblyFileName");
                    processedHelpFiles.Add(moduleName);

                    var newModuleMetadata = MetadataLoader.GetModuleMetadata(moduleName);
                    var fileName          = $"{moduleName}.json";
                    var executingPath     = Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().CodeBase).AbsolutePath);

                    var filePath = Path.Combine(executingPath, "SerializedCmdlets", fileName);

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

                    var oldModuleMetadata = ModuleMetadata.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}", moduleName);

                        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);
                }
            }
        }
示例#7
0
        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") &&
                                                                  !ModuleFilter.IsAzureStackModule(s) && Directory.Exists(Path.GetFullPath(s))))
            {
                SharedAssemblyLoader.Load(baseDirectory);

                //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);
                    string moduleName      = psd1FileName.Replace(".psd1", "");

                    Directory.SetCurrentDirectory(directory);

                    issueLogger.Decorator.AddDecorator(a => a.AssemblyFileName = moduleName, "AssemblyFileName");
                    processedHelpFiles.Add(moduleName);

                    var module = MetadataLoader.GetModuleMetadata(moduleName);
                    CmdletLoader.ModuleMetadata = module;
                    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.");
                        }

                        ValidateParameterSetWithMandatoryEqual(cmdlet, issueLogger);
                        ValidateParameterSetWithLenientMandatoryEqual(cmdlet, issueLogger);
                    }
                    issueLogger.Decorator.Remove("AssemblyFileName");
                    Directory.SetCurrentDirectory(savedDirectory);
                }
            }
        }
示例#8
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>
        /// <param name="modulesToAnalyze">The set of modules to analyze</param>
        public void Analyze(IEnumerable <string> cmdletProbingDirs, Func <IEnumerable <string>, IEnumerable <string> > directoryFilter, Func <string, bool> cmdletFilter, IEnumerable <string> modulesToAnalyze)
        {
            if (directoryFilter != null)
            {
                cmdletProbingDirs = directoryFilter(cmdletProbingDirs);
            }

            var logFileName = Path.Combine(OutputFilePath, BreakingChangeAttributeReportLoggerName);
            //Init the log file
            var logger = TextFileLogger.GetTextFileLogger(logFileName, CleanBreakingChangesFileBeforeWriting);

            try
            {
                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 cmdlets = GetCmdletsFilesInFolder(directory);
                        if (!cmdlets.Any())
                        {
                            continue;
                        }

                        foreach (var cmdletFileName in cmdlets)
                        {
                            var cmdletFileFullPath = Path.Combine(directory, Path.GetFileName(cmdletFileName));

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

// TODO: Remove IfDef
#if NETSTANDARD
                            var proxy = new CmdletBreakingChangeAttributeLoader();
#else
                            var proxy = EnvironmentHelpers.CreateProxy <CmdletBreakingChangeAttributeLoader>(directory, out _appDomain);
#endif
                            var cmdletDataForModule = proxy.GetModuleBreakingChangeAttributes(cmdletFileFullPath);

                            //If there is nothing in this module just continue
                            if (cmdletDataForModule == null)
                            {
                                Console.WriteLine("No breaking change attributes found in module " + cmdletFileName);
                                continue;
                            }

                            if (cmdletFilter != null)
                            {
                                var output = string.Format("Before filter\nmodule cmdlet count: {0}\n",
                                                           cmdletDataForModule.CmdletList.Count);

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

                                cmdletDataForModule.FilterCmdlets(cmdletFilter);

                                output += string.Format("After filter\nmodule cmdlet count: {0}\n",
                                                        cmdletDataForModule.CmdletList.Count);

                                foreach (var cmdlet in cmdletDataForModule.CmdletList)
                                {
                                    output += string.Format("\n\tcmdlet - {0}", cmdlet.CmdletName);
                                }

                                Console.WriteLine(output);
                            }

                            LogBreakingChangesInModule(cmdletDataForModule, logger);
// TODO: Remove IfDef code
#if !NETSTANDARD
                            AppDomain.Unload(_appDomain);
#endif
                        }
                    }
                }
            }
            finally
            {
                if (logger != null)
                {
                    TextFileLogger.CloseLogger(logFileName);
                }
            }
        }
示例#9
0
        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") &&
                                                                  !ModuleFilter.IsAzureStackModule(s) && 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);
                    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();
                        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);
                }
            }
        }
示例#10
0
        /// <summary>
        /// Bump the version of changed modules or a specified module.
        /// </summary>
        private static void BumpVersions()
        {
            string targetRepositories = null;

            using (PowerShell powershell = PowerShell.Create())
            {
                powershell.AddScript("Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope Process;");
                powershell.AddScript("Register-PackageSource -Name PSGallery -Location https://www.powershellgallery.com/api/v2 -ProviderName PowerShellGet");
                powershell.AddScript("Register-PackageSource -Name TestGallery -Location https://www.poshtestgallery.com/api/v2 -ProviderName PowerShellGet");
                powershell.AddScript("Get-PSRepository");
                var    repositories = powershell.Invoke();
                string psgallery    = null;
                string testgallery  = null;
                foreach (var repo in repositories)
                {
                    if ("https://www.powershellgallery.com/api/v2".Equals(repo.Properties["SourceLocation"]?.Value))
                    {
                        psgallery = repo.Properties["Name"]?.Value?.ToString();
                    }
                    if ("https://www.poshtestgallery.com/api/v2".Equals(repo.Properties["SourceLocation"]?.Value))
                    {
                        testgallery = repo.Properties["Name"]?.Value?.ToString();
                    }
                }
                if (psgallery == null)
                {
                    throw new Exception("Cannot calculate module version because PSGallery is not available.");
                }
                targetRepositories = psgallery;
                if (testgallery == null)
                {
                    Console.WriteLine("Warning: Cannot calculate module version precisely because TestGallery is not available.");
                }
                else
                {
                    targetRepositories += $",{testgallery}";
                }
            }

            var changedModules = new List <string>();

            foreach (var directory in _projectDirectories)
            {
                var changeLogs = Directory.GetFiles(directory, "ChangeLog.md", SearchOption.AllDirectories)
                                 .Where(f => !ModuleFilter.IsAzureStackModule(f) && IsChangeLogUpdated(f))
                                 .Select(f => GetModuleManifestPath(Directory.GetParent(f).FullName))
                                 .Where(m => m.Contains(_moduleNameFilter))
                                 .ToList();
                changedModules.AddRange(changeLogs);
            }

            var executingAssemblyPath      = Assembly.GetExecutingAssembly().Location;
            var versionControllerDirectory = Directory.GetParent(executingAssemblyPath).FullName;
            var miniVersionFile            = Path.Combine(versionControllerDirectory, "MinimalVersion.csv");

            if (File.Exists(miniVersionFile))
            {
                var lines = File.ReadAllLines(miniVersionFile).Skip(1).Where(c => !string.IsNullOrEmpty(c));
                foreach (var line in lines)
                {
                    var cols = line.Split(",").Select(c => c.StartsWith("\"") ? c.Substring(1) : c)
                               .Select(c => c.EndsWith("\"") ? c.Substring(0, c.Length - 1) : c)
                               .Select(c => c.Trim()).ToArray();
                    if (cols.Length >= 2)
                    {
                        _minimalVersion.Add(cols[0], new AzurePSVersion(cols[1]));
                    }
                }
            }
            //Make Az.Accounts as the first module to calcuate
            changedModules = changedModules.OrderBy(c => c == "Az.Accounts" ? "" : c).ToList();
            foreach (var projectModuleManifestPath in changedModules)
            {
                var moduleFileName = Path.GetFileName(projectModuleManifestPath);
                var moduleName     = moduleFileName.Replace(".psd1", "");

                var outputModuleManifest = _outputDirectories
                                           .SelectMany(d => Directory.GetDirectories(d, moduleName))
                                           .SelectMany(d => Directory.GetFiles(d, moduleFileName))
                                           .ToList();
                if (outputModuleManifest.Count == 0)
                {
                    throw new FileNotFoundException("No module manifest file found in output directories");
                }
                else if (outputModuleManifest.Count > 1)
                {
                    throw new IndexOutOfRangeException("Multiple module manifest files found in output directories");
                }

                var outputModuleManifestFile = outputModuleManifest.FirstOrDefault();

                _versionBumper = new VersionBumper(new VersionFileHelper(_rootDirectory, outputModuleManifestFile, projectModuleManifestPath));
                _versionBumper.PSRepositories = targetRepositories;
                if (_minimalVersion.ContainsKey(moduleName))
                {
                    _versionBumper.MinimalVersion = _minimalVersion[moduleName];
                }

                _versionBumper.BumpAllVersions();
            }
        }