Example #1
0
        public static Task <ExitCode> ExecuteAsync(
            string executePath,
            IEnumerable <string> arguments = null,
            ILogger logger = null,
            IEnumerable <KeyValuePair <string, string> > environmentVariables = null,
            CancellationToken cancellationToken = default,
            bool addProcessNameAsLogCategory    = false,
            bool addProcessRunnerCategory       = false,
            string parentPrefix = null)
        {
            ILogger usedLogger = logger ?? new NullLogger();

            string executingCategory = $"[{Path.GetFileNameWithoutExtension(Path.GetFileName(executePath))}]";

            return(ProcessRunner.ExecuteAsync(
                       executePath,
                       cancellationToken,
                       arguments,
                       (message, category) => usedLogger.Write(message, executingCategory),
                       usedLogger.WriteError,
                       verboseAction: usedLogger.WriteVerbose,
                       toolAction: (message, category) => usedLogger.Write(message, ToolName),
                       environmentVariables: environmentVariables,
                       debugAction: usedLogger.WriteDebug,
                       addProcessNameAsLogCategory: addProcessNameAsLogCategory,
                       addProcessRunnerCategory: addProcessRunnerCategory,
                       parentPrefix: parentPrefix));
        }
Example #2
0
        private async Task <bool> IsCurrentDirectoryClonableAsync()
        {
            if (!_directoryCloneEnabled)
            {
                _logger.WriteVerbose("Directory clone is disabled");
                return(false);
            }

            _logger.WriteVerbose("Directory clone is enabled");

            string sourceRoot = VcsPathHelper.TryFindVcsRootPath();

            if (string.IsNullOrWhiteSpace(sourceRoot))
            {
                _logger.WriteWarning("Could not find source root", _Prefix);
                return(false);
            }

            bool isClonable = false;

            string gitExePath = GitHelper.GetGitExePath(_logger);

            if (!string.IsNullOrWhiteSpace(gitExePath))
            {
                string gitDir = Path.Combine(sourceRoot, ".git");

                var statusAllArguments = new[]
                {
                    $"--git-dir={gitDir}",
                    $"--work-tree={sourceRoot}",
                    "status"
                };

                var argumentVariants = new List <string[]> {
                    new[] { "status" }, statusAllArguments
                };

                foreach (string[] argumentVariant in argumentVariants)
                {
                    ExitCode statusExitCode = await ProcessRunner.ExecuteAsync(
                        gitExePath,
                        arguments : argumentVariant,
                        standardOutLog : _logger.WriteVerbose,
                        standardErrorAction : _logger.WriteVerbose,
                        toolAction : _logger.Write,
                        verboseAction : _logger.WriteVerbose);

                    if (statusExitCode.IsSuccess)
                    {
                        isClonable = true;
                        break;
                    }
                }
            }

            _logger.WriteVerbose($"Is directory clonable: {isClonable}");

            return(isClonable);
        }
Example #3
0
        private async Task <ExitCode> RunVsTestAsync(
            ILogger logger,
            string vsTestReportDirectoryPath,
            string vsTestExePath,
            bool runTestsInReleaseConfiguration,
            string assemblyFilePrefix)
        {
            Type testClassAttribute  = typeof(TestClassAttribute);
            Type testMethodAttribute = typeof(TestMethodAttribute);

            var directory = new DirectoryInfo(_sourceRoot);

            var typesToFind = new List <Type> {
                testClassAttribute, testMethodAttribute
            };

            List <string> vsTestConsoleArguments =
                new UnitTestFinder(typesToFind).GetUnitTestFixtureDlls(directory, runTestsInReleaseConfiguration, assemblyFilePrefix, FrameworkConstants.NetFramework)
                .ToList();

            if (!vsTestConsoleArguments.Any())
            {
                logger.WriteWarning(
                    $"Could not find any VSTest tests in directory '{directory.FullName}' or any sub-directory");
                return(ExitCode.Success);
            }

            IEnumerable <string> options = GetVsTestConsoleOptions();

            vsTestConsoleArguments.AddRange(options);

            EnsureTestReportDirectoryExists(vsTestReportDirectoryPath);

            string oldCurrentDirectory = SaveCurrentDirectory();

            SetCurrentDirectory(vsTestReportDirectoryPath);

            LogExecution(logger, vsTestConsoleArguments, vsTestExePath);

            try
            {
                Task <ExitCode> execute = ProcessRunner.ExecuteAsync(
                    vsTestExePath,
                    arguments: vsTestConsoleArguments,
                    standardOutLog: logger.Write,
                    standardErrorAction: logger.WriteError,
                    toolAction: logger.Write);

                ExitCode result = await execute;

                return(result);
            }
            finally
            {
                RestoreCurrentDirectory(oldCurrentDirectory);
            }
        }
Example #4
0
        private async Task <ExitCode> RunBuildToolsAsync(string buildDir, string buildToolDirectoryName)
        {
            string buildToolDirectoryPath = Path.Combine(buildDir, buildToolDirectoryName);

            var buildToolDirectory = new DirectoryInfo(buildToolDirectoryPath);

            List <FileInfo> exeFiles =
                buildToolDirectory.GetFiles("*.exe", SearchOption.TopDirectoryOnly)
                .Where(file => !file.Name.Equals("nuget.exe", StringComparison.InvariantCultureIgnoreCase))
                .ToList();

            if (exeFiles.Count != 1)
            {
                PrintInvalidExeFileCount(exeFiles, buildToolDirectoryPath);
                return(ExitCode.Failure);
            }

            FileInfo buildToolExe = exeFiles.Single();

            string timeoutKey = WellKnownVariables.BuildToolTimeoutInSeconds;
            string timeoutInSecondsFromEnvironment = Environment.GetEnvironmentVariable(timeoutKey);

            ParseResult <int> parseResult =
                timeoutInSecondsFromEnvironment.TryParseInt32(MaxBuildTimeInSeconds);

            if (parseResult.Parsed)
            {
                _logger.WriteVerbose($"Using timeout from environment variable {timeoutKey}", _Prefix);
            }

            int usedTimeoutInSeconds = parseResult;

            _logger.Write($"Using build timeout {usedTimeoutInSeconds} seconds", _Prefix);

            var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(usedTimeoutInSeconds));

            const string buildApplicationPrefix = "[Arbor.X] ";

            IEnumerable <string> arguments = Enumerable.Empty <string>();
            ExitCode             result    = await ProcessRunner.ExecuteAsync(
                buildToolExe.FullName,
                cancellationTokenSource.Token,
                arguments,
                (message, prefix) => _logger.Write(message, buildApplicationPrefix),
                (message, prefix) => _logger.WriteError(message, buildApplicationPrefix),
                _logger.Write,
                _logger.WriteVerbose);

            return(result);
        }
Example #5
0
        private static async Task <ExitCode> UploadNugetPackageAsync(
            string nugetExePath,
            string symbolServerUrl,
            string apiKey,
            string nugetPackage,
            ILogger logger,
            int timeout)
        {
            var args = new List <string>
            {
                "push",
                nugetPackage,
                "-source",
                symbolServerUrl,
                apiKey,
                "-verbosity",
                "detailed"
            };

            if (timeout > 0)
            {
                args.Add("-timeout");
                args.Add(timeout.ToString(CultureInfo.InvariantCulture));
            }

            ExitCode exitCode =
                await
                ProcessRunner.ExecuteAsync(
                    nugetExePath,
                    arguments : args,
                    standardOutLog : logger.Write,
                    standardErrorAction : logger.WriteError,
                    toolAction : logger.Write);

            return(exitCode);
        }
Example #6
0
        private async Task <ExitCode> RunNUnitAsync(
            IVariable externalTools,
            ILogger logger,
            IVariable reportPath,
            bool runTestsInReleaseConfiguration,
            string assemblyFilePrefix)
        {
            Type fixtureAttribute    = typeof(TestFixtureAttribute);
            Type testMethodAttribute = typeof(TestAttribute);

            var directory = new DirectoryInfo(_sourceRoot);

            var typesToFind = new List <Type> {
                fixtureAttribute, testMethodAttribute
            };

            Stopwatch stopwatch = Stopwatch.StartNew();

            List <string> testDlls = new UnitTestFinder(typesToFind)
                                     .GetUnitTestFixtureDlls(directory, runTestsInReleaseConfiguration, assemblyFilePrefix, FrameworkConstants.NetFramework)
                                     .ToList();

            stopwatch.Stop();

            logger.Write($"NUnit test assembly lookup took {stopwatch.ElapsedMilliseconds:F2} milliseconds");

            if (!testDlls.Any())
            {
                logger.WriteWarning(
                    $"Could not find any NUnit tests in directory '{directory.FullName}' or any sub-directory");
                return(ExitCode.Success);
            }

            string nunitExePath = GetNunitExePath(externalTools);

            var results = new List <Tuple <string, ExitCode> >();

            foreach (string testDll in testDlls)
            {
                var nunitConsoleArguments = new List <string> {
                    testDll
                };

                string reportFilePath = GetNUnitXmlReportFilePath(reportPath, testDll);

                EnsureNUnitReportDirectoryExists(reportFilePath);

                IEnumerable <string> options = GetNUnitConsoleOptions(externalTools, reportFilePath);

                nunitConsoleArguments.AddRange(options);

                LogExecution(logger, nunitConsoleArguments, nunitExePath);

                Stopwatch executionStopwatch = Stopwatch.StartNew();

                ExitCode result = await ProcessRunner.ExecuteAsync(
                    nunitExePath,
                    arguments : nunitConsoleArguments,
                    standardOutLog : logger.Write,
                    standardErrorAction : logger.WriteError,
                    toolAction : logger.Write);

                executionStopwatch.Stop();

                logger.Write($"NUnit execution took {executionStopwatch.ElapsedMilliseconds:F2} milliseconds");

                results.Add(Tuple.Create(testDll, result));
            }

            if (results.All(result => result.Item2.IsSuccess))
            {
                return(ExitCode.Success);
            }

            var failedTestsBuilder = new StringBuilder();

            failedTestsBuilder.AppendLine("The following DLL files were not tested successfully:");
            foreach (Tuple <string, ExitCode> result in results.Where(r => !r.Item2.IsSuccess))
            {
                failedTestsBuilder.AppendLine(result.Item1);
            }

            logger.WriteError(failedTestsBuilder.ToString());

            return(ExitCode.Failure);
        }
Example #7
0
        public async Task <ExitCode> ExecuteAsync(
            ILogger logger,
            IReadOnlyCollection <IVariable> buildVariables,
            CancellationToken cancellationToken)
        {
            _logger = logger;

            ParseResult <bool> parseResult = buildVariables
                                             .GetVariableValueOrDefault(WellKnownVariables.ExternalTools_LibZ_Enabled, "false")
                                             .TryParseBool(false);

            if (!parseResult.Value)
            {
                _logger.Write(
                    $"LibZPacker is disabled, to enable it, set the flag {WellKnownVariables.ExternalTools_LibZ_Enabled} to true");
                return(ExitCode.Success);
            }

            _exePath = buildVariables.Require(WellKnownVariables.ExternalTools_LibZ_ExePath)
                       .ThrowIfEmptyValue()
                       .Value;

            string customExePath = buildVariables.GetVariableValueOrDefault(
                WellKnownVariables.ExternalTools_LibZ_Custom_ExePath,
                string.Empty);

            if (!string.IsNullOrWhiteSpace(customExePath) && File.Exists(customExePath))
            {
                logger.Write($"Using custom path for LibZ: '{customExePath}'");
                _exePath = customExePath;
            }

            _artifactsPath =
                buildVariables.Require(WellKnownVariables.Artifacts).ThrowIfEmptyValue().Value;

            string sourceRoot = buildVariables.Require(WellKnownVariables.SourceRoot).ThrowIfEmptyValue().Value;

            var sourceRootDirectory = new DirectoryInfo(sourceRoot);

            List <FileInfo> csharpProjectFiles =
                sourceRootDirectory.GetFilesRecursive(
                    new List <string> {
                ".csproj"
            },
                    DefaultPaths.DefaultPathLookupSpecification,
                    sourceRoot)
                .ToList();

            List <FileInfo> ilMergeProjects = csharpProjectFiles.Where(IsMergeEnabledInProjectFile).ToList();

            string merges = string.Join(Environment.NewLine, ilMergeProjects.Select(item => item.FullName));

            logger.Write($"Found {ilMergeProjects.Count} projects marked for merging:{Environment.NewLine}{merges}");

            ImmutableArray <ILRepackData> filesToMerge = (await Task.WhenAll(ilMergeProjects.Select(GetMergeFilesAsync)))
                                                         .SelectMany(item => item).ToImmutableArray();

            foreach (ILRepackData repackData in filesToMerge)
            {
                var fileInfo = new FileInfo(repackData.Exe);

                string mergedDirectoryPath = Path.Combine(
                    _artifactsPath,
                    "LibZ",
                    repackData.Platform,
                    repackData.Configuration);

                DirectoryInfo mergedDirectory = new DirectoryInfo(mergedDirectoryPath).EnsureExists();

                string mergedPath = Path.Combine(mergedDirectory.FullName, fileInfo.Name);

                fileInfo.CopyTo(mergedPath);

                string exeConfiguration =
                    Path.Combine(fileInfo.Directory.FullName, $"{fileInfo.Name}.config");

                if (File.Exists(exeConfiguration))
                {
                    string targetConfigFilePath =
                        Path.Combine(mergedDirectory.FullName, Path.GetFileName(exeConfiguration));

                    File.Copy(exeConfiguration, targetConfigFilePath);
                }

                var arguments = new List <string>
                {
                    "inject-dll",
                    "--assembly",
                    $"{mergedPath}"
                };

                foreach (FileInfo dll in repackData.Dlls)
                {
                    arguments.Add("--include");
                    arguments.Add(dll.FullName);
                }

                arguments.Add("--move");

                ExitCode result;

                using (CurrentDirectoryScope.Create(
                           new DirectoryInfo(Directory.GetCurrentDirectory()),
                           mergedDirectory))
                {
                    result = await ProcessRunner.ExecuteAsync(
                        _exePath,
                        arguments : arguments,
                        standardOutLog : logger.Write,
                        toolAction : logger.Write,
                        standardErrorAction : logger.WriteError,
                        cancellationToken : cancellationToken);
                }

                if (!result.IsSuccess)
                {
                    logger.WriteError($"Could not LibZ '{fileInfo.FullName}'");
                    return(result);
                }

                logger.Write($"LibZ result: {mergedPath}");
            }

            return(ExitCode.Success);
        }
Example #8
0
        public async Task <ExitCode> ExecuteAsync(
            ILogger logger,
            IReadOnlyCollection <IVariable> buildVariables,
            CancellationToken cancellationToken)
        {
            _logger = logger;

            ParseResult <bool> parseResult = buildVariables
                                             .GetVariableValueOrDefault(WellKnownVariables.ExternalTools_ILRepack_Enabled, "false")
                                             .TryParseBool(false);

            if (!parseResult.Value)
            {
                _logger.Write(
                    $"ILRepack is disabled, to enable it, set the flag {WellKnownVariables.ExternalTools_ILRepack_Enabled} to true");
                return(ExitCode.Success);
            }

            _ilRepackExePath =
                buildVariables.Require(WellKnownVariables.ExternalTools_ILRepack_ExePath).ThrowIfEmptyValue().Value;

            string customILRepackPath =
                buildVariables.GetVariableValueOrDefault(
                    WellKnownVariables.ExternalTools_ILRepack_Custom_ExePath,
                    string.Empty);

            if (!string.IsNullOrWhiteSpace(customILRepackPath) && File.Exists(customILRepackPath))
            {
                logger.Write($"Using custom path for ILRepack: '{customILRepackPath}'");
                _ilRepackExePath = customILRepackPath;
            }

            _artifactsPath =
                buildVariables.Require(WellKnownVariables.Artifacts).ThrowIfEmptyValue().Value;

            string sourceRoot = buildVariables.Require(WellKnownVariables.SourceRoot).ThrowIfEmptyValue().Value;

            var sourceRootDirectory = new DirectoryInfo(sourceRoot);

            List <FileInfo> csharpProjectFiles =
                sourceRootDirectory.GetFilesRecursive(
                    new List <string> {
                ".csproj"
            },
                    DefaultPaths.DefaultPathLookupSpecification,
                    sourceRoot)
                .ToList();

            List <FileInfo> ilMergeProjects = csharpProjectFiles.Where(IsILMergeEnabledInProjectFile).ToList();

            string merges = string.Join(Environment.NewLine, ilMergeProjects.Select(item => item.FullName));

            logger.Write($"Found {ilMergeProjects.Count} projects marked for ILMerge:{Environment.NewLine}{merges}");

            IReadOnlyCollection <ILRepackData> mergeDatas = ilMergeProjects.SelectMany(GetIlMergeFiles)
                                                            .ToReadOnlyCollection();

            foreach (ILRepackData repackData in mergeDatas)
            {
                var fileInfo = new FileInfo(repackData.Exe);

                string ilMergedDirectoryPath = Path.Combine(
                    _artifactsPath,
                    "ILMerged",
                    repackData.Platform,
                    repackData.Configuration);

                DirectoryInfo ilMergedDirectory = new DirectoryInfo(ilMergedDirectoryPath).EnsureExists();

                string ilMergedPath = Path.Combine(ilMergedDirectory.FullName, fileInfo.Name);
                var    arguments    = new List <string>
                {
                    "/target:exe",
                    $"/out:{ilMergedPath}",
                    $"/Lib:{fileInfo.Directory.FullName}",
                    "/verbose",
                    fileInfo.FullName
                };

                arguments.AddRange(repackData.Dlls.Select(dll => dll.FullName));

                string programFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86);

                string dotNetVersion = repackData.TargetFramework;

                string referenceAssemblyDirectory =
                    $"{$@"{programFiles}\Reference Assemblies\Microsoft\Framework\.NETFramework\v"}{dotNetVersion}";

                if (!Directory.Exists(referenceAssemblyDirectory))
                {
                    logger.WriteError(
                        $"Could not ILMerge, the reference assembly directory {referenceAssemblyDirectory} does not exist, currently only .NET v{dotNetVersion} is supported");

                    return(ExitCode.Failure);
                }

                arguments.Add(
                    $@"/targetplatform:v4,{referenceAssemblyDirectory}");

                ExitCode result =
                    await
                    ProcessRunner.ExecuteAsync(
                        _ilRepackExePath,
                        arguments : arguments,
                        standardOutLog : logger.Write,
                        toolAction : logger.Write,
                        standardErrorAction : logger.WriteError,
                        cancellationToken : cancellationToken);

                if (!result.IsSuccess)
                {
                    logger.WriteError($"Could not ILRepack '{fileInfo.FullName}'");
                    return(result);
                }

                logger.Write($"ILMerged result: {ilMergedPath}");
            }

            return(ExitCode.Success);
        }
Example #9
0
        private async Task <bool?> CheckPackageExistsAsync(
            FileInfo nugetPackage,
            string nugetExePath,
            ILogger logger,
            string sourceName)
        {
            if (!File.Exists(nugetPackage.FullName))
            {
                logger.WriteError(
                    $"The NuGet package '{nugetPackage}' does not exist");
                return(null);
            }

            logger.WriteDebug($"Searching for existing NuGet package '{nugetPackage}'");

            string packageVersion;
            string packageId;

            using (var fs = new FileStream(nugetPackage.FullName, FileMode.Open, FileAccess.Read))
            {
                using (var archive = new ZipArchive(fs))
                {
                    ZipArchiveEntry nuspecEntry =
                        archive.Entries.SingleOrDefault(
                            entry =>
                            Path.GetExtension(entry.Name)
                            .Equals(".nuspec", StringComparison.InvariantCultureIgnoreCase));

                    if (nuspecEntry == null)
                    {
                        throw new InvalidOperationException("The nuget package does not contain any nuspec");
                    }

                    var          nuspecReader = new NuspecReader(nuspecEntry.Open());
                    NuGetVersion nuGetVersion = nuspecReader.GetVersion();

                    packageVersion = nuGetVersion.ToNormalizedString();
                    packageId      = nuspecReader.GetIdentity().Id;
                }
            }

            SemanticVersion expectedVersion = SemanticVersion.Parse(packageVersion);

            var packageInfo = new { Id = packageId, Version = expectedVersion };

            var args = new List <string>
            {
                "list",
                packageId
            };

            if (!string.IsNullOrWhiteSpace(sourceName))
            {
                logger.WriteVerbose($"Using specific source name '{sourceName}'");
                args.Add("-source");
                args.Add(sourceName);
            }

            args.Add("-verbosity");
            args.Add("normal");

            if (packageInfo.Version.IsPrerelease)
            {
                logger.WriteVerbose($"Package '{nugetPackage.Name}' is pre-release");
                args.Add("-prerelease");
            }

            var errorBuilder    = new StringBuilder();
            var standardBuilder = new List <string>();

            string expectedNameAndVersion = $"{packageInfo.Id} {expectedVersion.ToNormalizedString()}";

            logger.Write($"Looking for '{expectedNameAndVersion}' package");

            ExitCode exitCode =
                await
                ProcessRunner.ExecuteAsync(
                    nugetExePath,
                    arguments : args,
                    standardOutLog :
                    (message, prefix) =>
            {
                standardBuilder.Add(message);
                logger.Write(message, prefix);
            },
                    standardErrorAction : (message, prefix) =>
            {
                errorBuilder.AppendLine(message);
                logger.WriteError(message, prefix);
            },
                    toolAction : logger.Write,
                    addProcessNameAsLogCategory : true,
                    addProcessRunnerCategory : true);

            if (!exitCode.IsSuccess)
            {
                logger.WriteError($"Could not execute process to check if package '{expectedNameAndVersion}' exists");
                return(null);
            }

            bool foundSpecificPackage = standardBuilder.Any(
                line => line.Equals(expectedNameAndVersion, StringComparison.InvariantCultureIgnoreCase));

            if (foundSpecificPackage)
            {
                logger.Write($"Found existing package id '{expectedNameAndVersion}'");
            }
            else
            {
                logger.Write($"Could not find existing package id '{expectedNameAndVersion}'");
            }

            return(foundSpecificPackage);
        }
Example #10
0
        private static async Task <ExitCode> UploadNugetPackageAsync(
            string nugetExePath,
            string serverUri,
            string apiKey,
            string nugetPackage,
            ILogger logger,
            int timeoutInseconds,
            bool checkNuGetPackagesExists,
            bool timeoutIncreaseEnabled)
        {
            if (!File.Exists(nugetPackage))
            {
                logger.WriteError(
                    $"The NuGet package '{nugetPackage}' does not exist, when trying to push to nuget source");
                return(ExitCode.Failure);
            }

            logger.WriteDebug($"Pushing NuGet package '{nugetPackage}'");

            var args = new List <string>
            {
                "push",
                nugetPackage
            };

            if (!string.IsNullOrWhiteSpace(serverUri))
            {
                args.Add("-source");
                args.Add(serverUri);
            }

            if (!string.IsNullOrWhiteSpace(apiKey))
            {
                args.Add(apiKey);
            }

            args.Add("-verbosity");
            args.Add("detailed");

            const int MaxAttempts = 5;

            ExitCode exitCode = ExitCode.Failure;

            int attemptCount = 1;

            while (!exitCode.IsSuccess && attemptCount <= MaxAttempts)
            {
                var errorBuilder = new StringBuilder();

                List <string> runSpecificArgs = args.ToList();

                if (timeoutInseconds > 0)
                {
                    runSpecificArgs.Add("-timeout");

                    int timeout;

                    if (timeoutIncreaseEnabled)
                    {
                        timeout = timeoutInseconds * attemptCount;
                    }
                    else
                    {
                        timeout = timeoutInseconds;
                    }

                    runSpecificArgs.Add(timeout.ToString(CultureInfo.InvariantCulture));
                }

                exitCode =
                    await
                    ProcessRunner.ExecuteAsync(
                        nugetExePath,
                        arguments : runSpecificArgs,
                        standardOutLog : logger.Write,
                        standardErrorAction : (message, prefix) =>
                {
                    errorBuilder.AppendLine(message);
                    logger.WriteError(message, prefix);
                },
                        toolAction : logger.Write,
                        addProcessNameAsLogCategory : true,
                        addProcessRunnerCategory : true);

                if (!exitCode.IsSuccess &&
                    errorBuilder.ToString().IndexOf("conflict", StringComparison.InvariantCultureIgnoreCase) >= 0)
                {
                    if (checkNuGetPackagesExists)
                    {
                        logger.WriteWarning(
                            $"The NuGet package could not be pushed, however, the pre-check if the package exists succeeded, so this error might be temporal");

                        return(ExitCode.Success);
                    }

                    logger.WriteError(
                        $"Failed to upload NuGet package '{nugetPackage}', skipping retry for NuGet package, conflict detected");

                    return(exitCode);
                }

                if (!exitCode.IsSuccess && attemptCount < MaxAttempts)
                {
                    logger.WriteWarning(
                        $"Failed to upload nuget package '{nugetPackage}', attempt {attemptCount} of {MaxAttempts}, retrying...");
                }

                attemptCount++;

                if (!exitCode.IsSuccess && attemptCount == MaxAttempts)
                {
                    logger.WriteError(
                        $"Failed to upload nuget package '{nugetPackage}' on last attempt {attemptCount} of {MaxAttempts}");
                }
            }

            return(exitCode);
        }
Example #11
0
        public async Task <IEnumerable <IVariable> > GetEnvironmentVariablesAsync(
            ILogger logger,
            IReadOnlyCollection <IVariable> buildVariables,
            CancellationToken cancellationToken)
        {
            int       currentProcessBits = Environment.Is64BitProcess ? 64 : 32;
            const int registryLookupBits = 32;

            logger.WriteVerbose(
                $"Running current process [id {Process.GetCurrentProcess().Id}] as a {currentProcessBits}-bit process");

            List <SemanticVersion> possibleVersions = new List <string> {
                "15.0.0", "14.0.0", "12.0.0", "4.0.0"
            }
            .Select(SemanticVersion.Parse)
            .ToList();

            string max = buildVariables.GetVariableValueOrDefault(
                WellKnownVariables.ExternalTools_MSBuild_MaxVersion,
                "15.0.0");

            SemanticVersion[] toRemove = possibleVersions.Where(version => version > SemanticVersion.Parse(max))
                                         .ToArray();

            foreach (SemanticVersion semVersion in toRemove)
            {
                possibleVersions.Remove(semVersion);
            }

            string vsWherePath = Path.Combine(
                Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86),
                "Microsoft Visual Studio",
                "Installer",
                "vswhere.exe");

            if (File.Exists(vsWherePath))
            {
                logger.WriteDebug($"vswhere.exe exists at '{vsWherePath}'");

                ExitCode versionExitCode = await ProcessHelper.ExecuteAsync(
                    vsWherePath,
                    new List <string> {
                    "-prerelease"
                },
                    cancellationToken : cancellationToken);

                var vsWhereArgs = new List <string> {
                    "-requires", "Microsoft.Component.MSBuild", "-format", "json"
                };

                bool allowPreRelease =
                    buildVariables.GetBooleanByKey(WellKnownVariables.ExternalTools_MSBuild_AllowPrereleaseEnabled);

                if (allowPreRelease)
                {
                    // NOTE only newer releases of vswhere.exe supports -prerelease flag
                    if (versionExitCode.IsSuccess)
                    {
                        vsWhereArgs.Add("-prerelease");
                    }
                }

                var resultBuilder = new StringBuilder();

                await ProcessRunner.ExecuteAsync(
                    vsWherePath,
                    arguments : vsWhereArgs,
                    standardOutLog : (message, category) => resultBuilder.Append(message),
                    cancellationToken : cancellationToken,
                    toolAction : logger.WriteDebug,
                    standardErrorAction : logger.WriteError);

                string json = resultBuilder.ToString();

                try
                {
                    var instanceTypePattern = new[]
                    {
                        new
                        {
                            installationName    = string.Empty,
                            installationPath    = string.Empty,
                            installationVersion = string.Empty,
                            channelId           = string.Empty
                        }
                    };

                    var typedInstallations = JsonConvert.DeserializeAnonymousType(
                        json,
                        instanceTypePattern);

                    var candidates = typedInstallations.ToArray();

                    if (!allowPreRelease)
                    {
                        candidates = candidates
                                     .Where(candidate =>
                                            candidate.channelId.IndexOf("preview", StringComparison.OrdinalIgnoreCase) < 0)
                                     .ToArray();
                    }

                    var array = candidates
                                .Select(candidate => new { candidate, versoin = Version.Parse(candidate.installationVersion) })
                                .ToArray();

                    var firstOrDefault = array.OrderByDescending(candidateItem => candidateItem.versoin)
                                         .FirstOrDefault();

                    if (firstOrDefault != null)
                    {
                        string msbuildPath = Path.Combine(
                            firstOrDefault.candidate.installationPath,
                            "MSBuild",
                            "15.0",
                            "bin",
                            "MSBuild.exe");

                        if (File.Exists(msbuildPath))
                        {
                            logger.Write($"Found MSBuild with vswhere.exe at '{msbuildPath}'");

                            var variables = new[]
                            {
                                new EnvironmentVariable(
                                    WellKnownVariables.ExternalTools_MSBuild_ExePath,
                                    msbuildPath)
                            };

                            return(variables);
                        }
                    }

                    logger.Write($"Could not find any version of MSBuild.exe with vswhere.exe");
                }
                catch (Exception ex)
                {
                    throw new InvalidOperationException(
                              $"Could not deserialize installed Visual Studio versions from json '{json}'",
                              ex);
                }
            }

            var possiblePaths = new[]
            {
                Path.Combine(
                    Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86),
                    "Microsoft Visual Studio",
                    "2017",
                    "Enterprise",
                    "MSBuild",
                    "15.0",
                    "bin",
                    "MSBuild.exe"),

                Path.Combine(
                    Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86),
                    "Microsoft Visual Studio",
                    "2017",
                    "Profesional",
                    "MSBuild",
                    "15.0",
                    "bin",
                    "MSBuild.exe"),

                Path.Combine(
                    Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86),
                    "Microsoft Visual Studio",
                    "2017",
                    "Community",
                    "MSBuild",
                    "15.0",
                    "bin",
                    "MSBuild.exe"),

                Path.Combine(
                    Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86),
                    "Microsoft Visual Studio",
                    "2017",
                    "BuildTools",
                    "MSBuild",
                    "15.0",
                    "bin",
                    "MSBuild.exe")
            };

            string fileBasedLookupResultPath = possiblePaths.FirstOrDefault(File.Exists);

            if (fileBasedLookupResultPath != null)
            {
                logger.Write($"Found MSBuild at '{fileBasedLookupResultPath}'");

                var variables = new[]
                {
                    new EnvironmentVariable(
                        WellKnownVariables.ExternalTools_MSBuild_ExePath,
                        fileBasedLookupResultPath)
                };

                return(variables);
            }

            string foundPath = null;

            foreach (SemanticVersion possibleVersion in possibleVersions)
            {
                string registryKeyName = @"SOFTWARE\Microsoft\MSBuild\" + possibleVersion.Major + "." +
                                         possibleVersion.Minor;
                object       msBuildPathRegistryKeyValue = null;
                const string valueKey = "MSBuildOverrideTasksPath";

                logger.WriteVerbose(
                    $"Looking for MSBuild exe path in {registryLookupBits}-bit registry key '{registryKeyName}\\{valueKey}");

                using (RegistryKey view32 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32))
                {
                    using (RegistryKey key = view32.OpenSubKey(registryKeyName))
                    {
                        if (key != null)
                        {
                            msBuildPathRegistryKeyValue = key.GetValue(valueKey, null);
                        }
                    }
                }

                string msBuildPath = msBuildPathRegistryKeyValue != null
                    ? $"{msBuildPathRegistryKeyValue}MSBuild.exe"
                    : null;

                if (!string.IsNullOrWhiteSpace(msBuildPath))
                {
                    foundPath = msBuildPath;
                    logger.WriteVerbose(
                        $"Using MSBuild exe path '{foundPath}' defined in {registryLookupBits}-bit registry key {registryKeyName}\\{valueKey}");
                    break;
                }
            }

            if (string.IsNullOrWhiteSpace(foundPath))
            {
                const string msbuildPath             = "MSBUILD_PATH";
                string       fromEnvironmentVariable = Environment.GetEnvironmentVariable(msbuildPath);

                if (!string.IsNullOrWhiteSpace(fromEnvironmentVariable))
                {
                    logger.Write($"Using MSBuild exe path '{foundPath}' from environment variable {msbuildPath}");
                    foundPath = fromEnvironmentVariable;
                }
                else
                {
                    logger.WriteError(
                        $"The MSBuild path could not be found in the {registryLookupBits}-bit registry keys.");
                    return(null);
                }
            }

            logger.Write($"Using MSBuild exe path '{foundPath}'");

            var environmentVariables = new[]
            {
                new EnvironmentVariable(
                    WellKnownVariables.ExternalTools_MSBuild_ExePath,
                    foundPath)
            };

            return(environmentVariables);
        }
Example #12
0
        private async Task <string> DownloadNuGetPackageAsync(string buildDir, string nugetExePath)
        {
            const string buildToolPackageName = "Arbor.X";

            string outputDirectoryPath = Path.Combine(buildDir, buildToolPackageName);

            var outputDirectory = new DirectoryInfo(outputDirectoryPath);

            bool reinstall = !outputDirectory.Exists ||
                             Environment.GetEnvironmentVariable(WellKnownVariables.NuGetReinstallArborPackageEnabled)
                             .TryParseBool(true);

            if (!reinstall)
            {
                return(outputDirectoryPath);
            }

            outputDirectory.DeleteIfExists();
            outputDirectory.EnsureExists();

            string version = Environment.GetEnvironmentVariable(WellKnownVariables.ArborXNuGetPackageVersion);

            var nugetArguments = new List <string>
            {
                "install",
                buildToolPackageName,
                "-ExcludeVersion",
                "-OutputDirectory",
                buildDir.TrimEnd('\\')
            };

            if (LogLevel.Verbose.Level <= _logger.LogLevel.Level)
            {
                nugetArguments.Add("-Verbosity");
                nugetArguments.Add("detailed");
            }

            string nuGetSource = Environment.GetEnvironmentVariable(WellKnownVariables.ArborXNuGetPackageSource);

            if (!string.IsNullOrWhiteSpace(nuGetSource))
            {
                nugetArguments.Add("-Source");
                nugetArguments.Add(nuGetSource);
            }

            string noCache = Environment.GetEnvironmentVariable(WellKnownVariables.ArborXNuGetPackageNoCacheEnabled);

            if (noCache.TryParseBool(false))
            {
                nugetArguments.Add("-NoCache");
            }

            if (!string.IsNullOrWhiteSpace(version))
            {
                nugetArguments.Add("-Version");
                nugetArguments.Add(version);

                _logger.WriteVerbose(
                    $"'{WellKnownVariables.ArborXNuGetPackageVersion}' flag is set, using specific version of Arbor.X: {version}",
                    _Prefix);
            }
            else
            {
                _logger.WriteVerbose(
                    $"'{WellKnownVariables.ArborXNuGetPackageVersion}' flag is not set, using latest version of Arbor.X",
                    _Prefix);

                bool allowPrerelease;
                if (_startOptions.PrereleaseEnabled.HasValue)
                {
                    allowPrerelease = _startOptions.PrereleaseEnabled.Value;

                    if (allowPrerelease)
                    {
                        _logger.WriteVerbose(
                            "Prerelease option is set via start options, using latest version of Arbor.X allowing prerelease versions",
                            _Prefix);
                    }
                }
                else
                {
                    allowPrerelease =
                        Environment.GetEnvironmentVariable(WellKnownVariables.AllowPrerelease)
                        .TryParseBool(false);

                    if (allowPrerelease)
                    {
                        _logger.WriteVerbose(
                            $"'{WellKnownVariables.AllowPrerelease}' flag is set, using latest version of Arbor.X allowing prerelease versions",
                            _Prefix);
                    }
                    else
                    {
                        _logger.WriteVerbose(
                            $"'{WellKnownVariables.AllowPrerelease}' flag is not set, using latest stable version of Arbor.X",
                            _Prefix);
                    }
                }

                if (allowPrerelease)
                {
                    nugetArguments.Add("-Prerelease");
                }
            }

            var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(MaxBuildTimeInSeconds));

            ExitCode exitCode = await ProcessRunner.ExecuteAsync(
                nugetExePath,
                arguments : nugetArguments,
                cancellationToken : cancellationTokenSource.Token,
                standardOutLog : _logger.Write,
                standardErrorAction : _logger.WriteError,
                toolAction : _logger.Write,
                verboseAction : _logger.WriteVerbose,
                addProcessRunnerCategory : true,
                addProcessNameAsLogCategory : true,
                parentPrefix : _Prefix);

            if (!exitCode.IsSuccess)
            {
                outputDirectoryPath = string.Empty;
            }

            return(outputDirectoryPath);
        }
        public async Task <ExitCode> ExecuteAsync([NotNull] ILogger logger, [NotNull] IReadOnlyCollection <IVariable> buildVariables, CancellationToken cancellationToken)
        {
            if (logger == null)
            {
                throw new ArgumentNullException(nameof(logger));
            }

            if (buildVariables == null)
            {
                throw new ArgumentNullException(nameof(buildVariables));
            }

            bool enabled = buildVariables.GetBooleanByKey(WellKnownVariables.XUnitNetCoreAppEnabled, false);

            if (!enabled)
            {
                logger.WriteDebug("Xunit .NET Core App test runner is not enabled");
                return(ExitCode.Success);
            }

            _sourceRoot = buildVariables.Require(WellKnownVariables.SourceRoot).ThrowIfEmptyValue().Value;
            IVariable reportPath   = buildVariables.Require(WellKnownVariables.ReportPath).ThrowIfEmptyValue();
            string    xunitDllPath = buildVariables.GetVariableValueOrDefault(WellKnownVariables.XUnitNetCoreAppDllPath, null) ?? Path.Combine(buildVariables.Require(WellKnownVariables.ExternalTools).Value, "xunit", "netcoreapp2.0", "xunit.console.dll");

            logger.WriteDebug($"Using XUnit dll path '{xunitDllPath}'");

            Type theoryType    = typeof(TheoryAttribute);
            Type factAttribute = typeof(FactAttribute);

            var directory = new DirectoryInfo(_sourceRoot);

            var typesToFind = new List <Type> {
                theoryType, factAttribute
            };

            bool runTestsInReleaseConfiguration =
                buildVariables.GetBooleanByKey(
                    WellKnownVariables.RunTestsInReleaseConfigurationEnabled,
                    true);

            string configuration = runTestsInReleaseConfiguration ? "release" : "debug";

            string assemblyFilePrefix = buildVariables.GetVariableValueOrDefault(WellKnownVariables.TestsAssemblyStartsWith, string.Empty);

            logger.Write($"Finding Xunit test DLL files built with {configuration} in directory '{_sourceRoot}'");
            logger.Write($"Looking for types {string.Join(", ", typesToFind.Select(t => t.FullName))} in directory '{_sourceRoot}'");

            List <string> testDlls = new UnitTestFinder(typesToFind, logger: logger, debugLogEnabled: true)
                                     .GetUnitTestFixtureDlls(directory, runTestsInReleaseConfiguration, assemblyFilePrefix: assemblyFilePrefix, targetFrameworkPrefix: FrameworkConstants.NetCoreApp)
                                     .ToList();

            if (!testDlls.Any())
            {
                logger.Write("Found no .NETCoreApp Assemblies with Xunit tests");
                return(ExitCode.Success);
            }

            string dotNetExePath =
                buildVariables.GetVariableValueOrDefault(WellKnownVariables.DotNetExePath, string.Empty);

            if (string.IsNullOrWhiteSpace(dotNetExePath))
            {
                logger.Write(
                    $"Path to 'dotnet.exe' has not been specified, set variable '{WellKnownVariables.DotNetExePath}' or ensure the dotnet.exe is installed in its standard location");
                return(ExitCode.Failure);
            }

            logger.WriteDebug($"Using dotnet.exe in path '{dotNetExePath}'");

            string xmlReportName = $"{Guid.NewGuid()}.xml";

            var arguments = new List <string>();

            string reportFile = Path.Combine(reportPath.Value, "xunit", xmlReportName);

            var reportFileInfo = new FileInfo(reportFile);

            reportFileInfo.Directory.EnsureExists();

            arguments.Add(xunitDllPath);
            arguments.AddRange(testDlls);
            arguments.Add("-nunit");
            arguments.Add(reportFileInfo.FullName);

            ExitCode result = await ProcessRunner.ExecuteAsync(
                dotNetExePath,
                arguments : arguments,
                standardOutLog : logger.Write,
                standardErrorAction : logger.WriteError,
                toolAction : logger.Write,
                cancellationToken : cancellationToken);

            return(result);
        }
Example #14
0
        public async Task <ExitCode> ExecuteAsync(
            ILogger logger,
            IReadOnlyCollection <IVariable> buildVariables,
            CancellationToken cancellationToken)
        {
            bool enabled = buildVariables.GetBooleanByKey(WellKnownVariables.MSpecEnabled, true);

            if (!enabled)
            {
                logger.WriteWarning($"{MachineSpecificationsConstants.MachineSpecificationsName} not enabled");
                return(ExitCode.Success);
            }

            string externalToolsPath =
                buildVariables.Require(WellKnownVariables.ExternalTools).ThrowIfEmptyValue().Value;

            string sourceRoot =
                buildVariables.Require(WellKnownVariables.SourceRoot).ThrowIfEmptyValue().Value;

            string testReportDirectoryPath =
                buildVariables.Require(WellKnownVariables.ExternalTools_MSpec_ReportPath).ThrowIfEmptyValue().Value;

            string sourceRootOverride =
                buildVariables.GetVariableValueOrDefault(WellKnownVariables.SourceRootOverride, string.Empty);

            string sourceDirectoryPath;

            if (string.IsNullOrWhiteSpace(sourceRootOverride) || !Directory.Exists(sourceRootOverride))
            {
                if (sourceRoot == null)
                {
                    throw new InvalidOperationException("Source root cannot be null");
                }

                sourceDirectoryPath = sourceRoot;
            }
            else
            {
                sourceDirectoryPath = sourceRootOverride;
            }

            var    directory    = new DirectoryInfo(sourceDirectoryPath);
            string mspecExePath = Path.Combine(
                externalToolsPath,
                MachineSpecificationsConstants.MachineSpecificationsName,
                "mspec-clr4.exe");

            bool runTestsInReleaseConfiguration =
                buildVariables.GetBooleanByKey(
                    WellKnownVariables.RunTestsInReleaseConfigurationEnabled,
                    true);

            IEnumerable <Type> typesToFind = new List <Type>
            {
                typeof(It),
                typeof(BehaviorsAttribute),
                typeof(SubjectAttribute),
                typeof(Behaves_like <>)
            };

            logger.WriteVerbose(
                $"Scanning directory '{directory.FullName}' for assemblies containing Machine.Specifications tests");

            string assemblyFilePrefix = buildVariables.GetVariableValueOrDefault(WellKnownVariables.TestsAssemblyStartsWith, string.Empty);

            List <string> testDlls =
                new UnitTestFinder(typesToFind, logger: logger)
                .GetUnitTestFixtureDlls(directory, runTestsInReleaseConfiguration, assemblyFilePrefix, FrameworkConstants.NetFramework)
                .ToList();

            if (!testDlls.Any())
            {
                logger.WriteWarning(
                    $"No DLL files with {MachineSpecificationsConstants.MachineSpecificationsName} specifications was found");
                return(ExitCode.Success);
            }

            var arguments = new List <string>();

            arguments.AddRange(testDlls);

            arguments.Add("--xml");
            string timestamp     = DateTime.UtcNow.ToString("O").Replace(":", ".");
            string fileName      = "MSpec_" + timestamp + ".xml";
            string xmlReportPath = Path.Combine(testReportDirectoryPath, "Xml", fileName);

            new FileInfo(xmlReportPath).Directory.EnsureExists();

            arguments.Add(xmlReportPath);
            string htmlPath = Path.Combine(testReportDirectoryPath, "Html", "MSpec_" + timestamp);

            new DirectoryInfo(htmlPath).EnsureExists();

            IReadOnlyCollection <string> excludedTags = buildVariables
                                                        .GetVariableValueOrDefault(
                WellKnownVariables.IgnoredTestCategories,
                string.Empty)
                                                        .Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries)
                                                        .Select(item => item.Trim())
                                                        .Where(item => !string.IsNullOrWhiteSpace(item))
                                                        .ToReadOnlyCollection();

            arguments.Add("--html");
            arguments.Add(htmlPath);

            bool hasArborTestDll =
                testDlls.Any(dll => dll.IndexOf("arbor", StringComparison.InvariantCultureIgnoreCase) >= 0);

            if (hasArborTestDll || excludedTags.Any())
            {
                var allExcludedTags = new List <string>();

                arguments.Add("--exclude");

                if (hasArborTestDll)
                {
                    allExcludedTags.Add(MSpecInternalConstants.RecursiveArborXTest);
                }

                if (excludedTags.Any())
                {
                    allExcludedTags.AddRange(excludedTags);
                }

                string excludedTagsParameter = string.Join(",", allExcludedTags);

                logger.Write($"Running MSpec with excluded tags: {excludedTagsParameter}");

                arguments.Add(excludedTagsParameter);
            }

            // ReSharper disable once CollectionNeverUpdated.Local
            var environmentVariables = new Dictionary <string, string>();

            ExitCode exitCode = await
                                ProcessRunner.ExecuteAsync(
                mspecExePath,
                arguments : arguments,
                cancellationToken : cancellationToken,
                standardOutLog : logger.Write,
                standardErrorAction : logger.WriteError,
                toolAction : logger.Write,
                verboseAction : logger.WriteVerbose,
                environmentVariables : environmentVariables,
                debugAction : logger.WriteDebug);

            if (buildVariables.GetBooleanByKey(
                    WellKnownVariables.MSpecJUnitXslTransformationEnabled,
                    false))
            {
                logger.WriteVerbose(
                    $"Transforming {MachineSpecificationsConstants.MachineSpecificationsName} test reports to JUnit format");

                const string junitSuffix = "_junit.xml";

                DirectoryInfo xmlReportDirectory = new FileInfo(xmlReportPath).Directory;

// ReSharper disable once PossibleNullReferenceException
                IReadOnlyCollection <FileInfo> xmlReports = xmlReportDirectory
                                                            .GetFiles("*.xml")
                                                            .Where(report => !report.Name.EndsWith(junitSuffix, StringComparison.Ordinal))
                                                            .ToReadOnlyCollection();

                if (xmlReports.Any())
                {
                    Encoding encoding = Encoding.UTF8;
                    using (Stream stream = new MemoryStream(encoding.GetBytes(MSpecJUnitXsl.Xml)))
                    {
                        using (XmlReader xmlReader = new XmlTextReader(stream))
                        {
                            var myXslTransform = new XslCompiledTransform();
                            myXslTransform.Load(xmlReader);

                            foreach (FileInfo xmlReport in xmlReports)
                            {
                                logger.WriteDebug($"Transforming '{xmlReport.FullName}' to JUnit XML format");
                                try
                                {
                                    TransformReport(xmlReport, junitSuffix, encoding, myXslTransform, logger);
                                }
                                catch (Exception ex)
                                {
                                    logger.WriteError($"Could not transform '{xmlReport.FullName}', {ex}");
                                    return(ExitCode.Failure);
                                }

                                logger.WriteDebug(
                                    $"Successfully transformed '{xmlReport.FullName}' to JUnit XML format");
                            }
                        }
                    }
                }
            }

            return(exitCode);
        }
Example #15
0
        private static async Task <ExitCode> ExecuteNuGetPackAsync(
            string nuGetExePath,
            string packagesDirectoryPath,
            ILogger logger,
            string nuSpecFileCopyPath,
            string properties,
            NuSpec nuSpecCopy,
            List <string> removedTags,
            bool keepBinaryAndSourcePackagesTogetherEnabled = false,
            bool nugetSymbolPackageEnabled      = false,
            bool ignoreWarnings                 = false,
            CancellationToken cancellationToken = default)
        {
            bool hasRemovedNoSourceTag =
                removedTags.Any(
                    tag => tag.Equals(WellKnownNuGetTags.NoSource, StringComparison.InvariantCultureIgnoreCase));

            ExitCode result;

            try
            {
                var arguments = new List <string>
                {
                    "pack",
                    nuSpecFileCopyPath,
                    "-Properties",
                    properties,
                    "-OutputDirectory",
                    packagesDirectoryPath,
                    "-Version",
                    nuSpecCopy.Version
                };

                if (!hasRemovedNoSourceTag && nugetSymbolPackageEnabled)
                {
                    arguments.Add("-Symbols");
                }

                if (LogLevel.Verbose.Level <= logger.LogLevel.Level)
                {
                    arguments.Add("-Verbosity");
                    arguments.Add("Detailed");
                }

                if (ignoreWarnings)
                {
                    arguments.Add("-NoPackageAnalysis");
                }

                ExitCode processResult =
                    await
                    ProcessRunner.ExecuteAsync(
                        nuGetExePath,
                        arguments : arguments,
                        standardOutLog : logger.Write,
                        standardErrorAction : logger.WriteError,
                        toolAction : logger.Write,
                        cancellationToken : cancellationToken,
                        verboseAction : logger.WriteVerbose,
                        debugAction : logger.WriteDebug,
                        addProcessNameAsLogCategory : true,
                        addProcessRunnerCategory : true);

                var packagesDirectory = new DirectoryInfo(packagesDirectoryPath);

                if (!keepBinaryAndSourcePackagesTogetherEnabled)
                {
                    logger.Write(
                        $"The flag {WellKnownVariables.NuGetKeepBinaryAndSymbolPackagesTogetherEnabled} is set to false, separating binary packages from symbol packages");
                    List <string> nugetPackages = packagesDirectory.GetFiles("*.nupkg", SearchOption.TopDirectoryOnly)
                                                  .Select(file => file.FullName)
                                                  .ToList();
                    List <string> nugetSymbolPackages = packagesDirectory
                                                        .GetFiles("*.symbols.nupkg", SearchOption.TopDirectoryOnly)
                                                        .Select(file => file.FullName)
                                                        .ToList();

                    List <string> binaryPackages = nugetPackages.Except(nugetSymbolPackages).ToList();

                    DirectoryInfo binaryPackagesDirectory =
                        new DirectoryInfo(Path.Combine(packagesDirectory.FullName, "binary")).EnsureExists();

                    DirectoryInfo symbolPackagesDirectory =
                        new DirectoryInfo(Path.Combine(packagesDirectory.FullName, "symbol")).EnsureExists();

                    foreach (string binaryPackage in binaryPackages)
                    {
                        var sourceFile       = new FileInfo(binaryPackage);
                        var targetBinaryFile =
                            new FileInfo(Path.Combine(binaryPackagesDirectory.FullName, sourceFile.Name));

                        if (targetBinaryFile.Exists)
                        {
                            targetBinaryFile.Delete();
                        }

                        logger.WriteDebug($"Copying NuGet binary package '{binaryPackage}' to '{targetBinaryFile}'");
                        sourceFile.MoveTo(targetBinaryFile.FullName);
                    }

                    foreach (string sourcePackage in nugetSymbolPackages)
                    {
                        var sourceFile       = new FileInfo(sourcePackage);
                        var targetSymbolFile =
                            new FileInfo(Path.Combine(symbolPackagesDirectory.FullName, sourceFile.Name));

                        if (targetSymbolFile.Exists)
                        {
                            targetSymbolFile.Delete();
                        }

                        logger.WriteDebug($"Copying NuGet symbol package '{sourcePackage}' to '{targetSymbolFile}'");
                        sourceFile.MoveTo(targetSymbolFile.FullName);
                    }
                }

                result = processResult;
            }
            finally
            {
                if (File.Exists(nuSpecFileCopyPath))
                {
                    File.Delete(nuSpecFileCopyPath);
                }
            }

            return(result);
        }
        public async Task <ExitCode> ExecuteAsync([NotNull] ILogger logger, [NotNull] IReadOnlyCollection <IVariable> buildVariables, CancellationToken cancellationToken)
        {
            if (logger == null)
            {
                throw new ArgumentNullException(nameof(logger));
            }

            if (buildVariables == null)
            {
                throw new ArgumentNullException(nameof(buildVariables));
            }

            bool enabled = buildVariables.GetBooleanByKey(WellKnownVariables.XUnitNetFrameworkEnabled, false);

            if (!enabled)
            {
                logger.WriteDebug("Xunit .NET Framework test runner is not enabled");
                return(ExitCode.Success);
            }

            _sourceRoot = buildVariables.Require(WellKnownVariables.SourceRoot).ThrowIfEmptyValue().Value;
            IVariable reportPath   = buildVariables.Require(WellKnownVariables.ReportPath).ThrowIfEmptyValue();
            string    xunitExePath = buildVariables.GetVariableValueOrDefault(WellKnownVariables.XUnitNetFrameworkExePath, Path.Combine(buildVariables.Require(WellKnownVariables.ExternalTools).Value, "xunit", "net452", "xunit.console.exe"));

            Type theoryType    = typeof(TheoryAttribute);
            Type factAttribute = typeof(FactAttribute);

            var directory = new DirectoryInfo(_sourceRoot);

            var typesToFind = new List <Type> {
                theoryType, factAttribute
            };

            bool runTestsInReleaseConfiguration =
                buildVariables.GetBooleanByKey(
                    WellKnownVariables.RunTestsInReleaseConfigurationEnabled,
                    true);

            string assemblyFilePrefix = buildVariables.GetVariableValueOrDefault(WellKnownVariables.TestsAssemblyStartsWith, string.Empty);

            List <string> testDlls = new UnitTestFinder(typesToFind)
                                     .GetUnitTestFixtureDlls(directory, runTestsInReleaseConfiguration, assemblyFilePrefix, FrameworkConstants.NetFramework)
                                     .ToList();

            if (!testDlls.Any())
            {
                logger.Write("Could not find any DLL files with Xunit test and target framework .NETFramework, skipping Xunit Net Framework tests");
                return(ExitCode.Success);
            }

            string xmlReportName = $"{Guid.NewGuid()}.xml";

            var arguments = new List <string>();

            string reportFile = Path.Combine(reportPath.Value, "xunit", xmlReportName);

            var fileInfo = new FileInfo(reportFile);

            fileInfo.Directory.EnsureExists();

            arguments.AddRange(testDlls);
            arguments.Add("-nunit");
            arguments.Add(fileInfo.FullName);

            ExitCode result = await ProcessRunner.ExecuteAsync(
                xunitExePath,
                arguments : arguments,
                standardOutLog : logger.Write,
                standardErrorAction : logger.WriteError,
                toolAction : logger.Write,
                cancellationToken : cancellationToken);

            return(result);
        }