static int LintCodeownersFile()

in tools/codeowners-utils/Azure.Sdk.Tools.CodeownersLinter/Program.cs [159:308]


        static int LintCodeownersFile(string teamUserBlobStorageUri, 
                                      string userOrgVisibilityBlobStorageUri, 
                                      string repoLabelBlobStorageUri, 
                                      string repoRoot, 
                                      string repoName,
                                      bool   filterBaselineErrors,
                                      bool   generateBaseline,
                                      string baseBranchBaselineFile)
        {
            // Don't allow someone to create and use a baseline in the same run
            if (filterBaselineErrors && generateBaseline)
            {
                throw new ArgumentException("The --filterBaselineErrors (-fbl) and --generateBaseline (-gbl) options cannot both be set. Either a baseline is being generated or being used to filter but not both.");
            }

            // Verify that the repoRoot exists
            if (!Directory.Exists(repoRoot))
            {
                throw new ArgumentException($"The repository root '{repoRoot}' is not a valid directory. Please ensure the --repoRoot is set to the root of the repository.");
            }
            // Verify that the CODEOWNERS file exists in the .github subdirectory of the repository root
            string codeownersFileFullPath = Path.Combine(repoRoot, ".github", "CODEOWNERS");
            if (!File.Exists(codeownersFileFullPath))
            {
                throw new ArgumentException($"CODEOWNERS file {codeownersFileFullPath} does not exist. Please ensure the --repoRoot is set to the root of the repository and the CODEOWNERS file exists in the .github subdirectory.");
            }
            // Verify that label data exists for the repository
            RepoLabelDataUtils repoLabelData = new RepoLabelDataUtils(repoLabelBlobStorageUri, repoName);
            if (!repoLabelData.RepoLabelDataExists())
            {
                throw new ArgumentException($"The repository label data for {repoName} does not exist. Should this be running in this repository?");
            }

            bool useBaseBranchBaselineFile = false;
            if (!string.IsNullOrEmpty(baseBranchBaselineFile))
            {
                if ((filterBaselineErrors && File.Exists(baseBranchBaselineFile)) || generateBaseline)
                {
                    useBaseBranchBaselineFile = true;
                }
                else
                {
                    throw new ArgumentException($"The base branch baseline file {baseBranchBaselineFile} does not exist.");
                }
            }

            string codeownersBaselineFile = Path.Combine(repoRoot, ".github", BaselineConstants.BaselineErrorFile);
            bool codeownersBaselineFileExists = false;
            // If the baseline is to be used, verify that it exists.
            if (filterBaselineErrors)
            {
                if (File.Exists(codeownersBaselineFile))
                {
                    codeownersBaselineFileExists = true;
                }
                else
                {
                    Console.WriteLine($"The CODEOWNERS baseline error file, {codeownersBaselineFile}, file for {repoName} does not exist. No filtering will be done for errors.");
                }
            }

            DirectoryUtils directoryUtils = new DirectoryUtils(repoRoot);
            OwnerDataUtils ownerData = new OwnerDataUtils(teamUserBlobStorageUri, userOrgVisibilityBlobStorageUri);

            var errors = CodeownersUtils.Verification.CodeownersLinter.LintCodeownersFile(directoryUtils, 
                                                                           ownerData, 
                                                                           repoLabelData,
                                                                           codeownersFileFullPath);

            // Regenerate the baseline file if that option was selected
            if (generateBaseline)
            {
                BaselineUtils baselineUtils = null;
                if (useBaseBranchBaselineFile)
                {
                    baselineUtils = new BaselineUtils(baseBranchBaselineFile);
                }
                else
                {
                    baselineUtils = new BaselineUtils(codeownersBaselineFile);
                }
                baselineUtils.GenerateBaseline(errors);
            }

            // If the baseline is being used to filter out known errors, set the list
            // of errors to the filtered list.
            if (filterBaselineErrors)
            {
                // Can only filter if the filter file exists, if it doesn't then there's nothing to filter
                // and all encountered errors will be output. Also, if the file doesn't exist that's reported
                // above and doesn't need to be reported here.
                if (codeownersBaselineFileExists)
                {
                    if (errors.Count == 0)
                    {
                        Console.WriteLine($"##vso[task.LogIssue type=warning;]There were no CODEOWNERS parsing errors but there is a baseline file {codeownersBaselineFile} for filtering. If the file is empty, or all errors have been fixed, then it should be deleted.");
                    }
                    else
                    {
                        BaselineUtils baselineUtils = new BaselineUtils(codeownersBaselineFile);
                        errors = baselineUtils.FilterErrorsUsingBaseline(errors);
                    }
                }

                // After the file has been filered with the standard CODEOWNERS baseline file, if there are
                // still remaining errors and there is a base branch baseline file, further filter with that
                // file.
                if (useBaseBranchBaselineFile && errors.Count > 0)
                {
                    BaselineUtils baselineUtils = new BaselineUtils(baseBranchBaselineFile);
                    errors = baselineUtils.FilterErrorsUsingBaseline(errors);
                }
            }
            bool loggingInDevOps = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("SYSTEM_TEAMPROJECTID"));
            int returnCode = 0;
            // If there are errors, and this isn't a baseline generation, ensure the returnCode is non-zero and output the errors.
            if ((errors.Count > 0) && !generateBaseline)
            {
                returnCode = 1;

                // DevOps only adds the first 4 errors to the github checks list so lets always add the generic one first and then as many of the individual ones as can be found afterwards
                if (loggingInDevOps)
                {
                    Console.WriteLine($"##vso[task.logissue type=error;]There are linter errors. Please visit {linterErrorsHelpLink} for guidance on how to handle them.");
                }
                else
                {
                    Console.WriteLine($"There are linter errors. Please visit {linterErrorsHelpLink} for guidance on how to handle them.");
                }

                // Output the errors sorted ascending by line number and by type. If there's a block
                // error with the same starting line number as a single line error, the block error
                // should be output first.
                var errorsByLineAndType = errors.OrderBy(e => e.LineNumber).ThenBy(e => e.GetType().Name);

                foreach (var error in errorsByLineAndType)
                {
                    if (loggingInDevOps)
                    {
                        // Environment.NewLine needs to be replaced by an encoded NewLine "%0D%0A" in order to display on GitHub and DevOps checks
                        Console.WriteLine($"##vso[task.logissue type=error;sourcepath={codeownersFileFullPath};linenumber={error.LineNumber};columnnumber=1;]{error.ToString().Replace(Environment.NewLine,"%0D%0A")}");
                    }
                    else 
                    {
                        Console.WriteLine(error + Environment.NewLine);
                    }
                }
            }
            return returnCode;
        }