/// <inheritdoc />
        public async Task Run(
            string command,
            string workingDirectory = "",
            bool streamOutputToInteractiveService = true,
            Action <TryRunResult>?onComplete      = null,
            bool redirectIO = true,
            IDictionary <string, string>?environmentVariables = null,
            CancellationToken cancelToken = default,
            bool needAwsCredentials       = false)
        {
            StringBuilder strOutput = new StringBuilder();
            StringBuilder strError  = new StringBuilder();

            var processStartInfo = new ProcessStartInfo
            {
                FileName = GetSystemShell(),

                Arguments =
                    RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
                        ? $"/c {command}"
                        : $"-c \"{command}\"",

                RedirectStandardInput  = redirectIO,
                RedirectStandardOutput = redirectIO,
                RedirectStandardError  = redirectIO,
                UseShellExecute        = false,
                CreateNoWindow         = redirectIO,
                WorkingDirectory       = workingDirectory
            };

            // If the command output is not being redirected check to see if
            // the output should go to a separate console window. This is important when run from
            // an IDE which won't have a console window by default.
            if (!streamOutputToInteractiveService && !redirectIO)
            {
                processStartInfo.UseShellExecute = _useSeparateWindow;
            }

            UpdateEnvironmentVariables(processStartInfo, environmentVariables);

            if (needAwsCredentials)
            {
                _processStartInfoAction?.Invoke(processStartInfo);
            }

            var process = Process.Start(processStartInfo);

            if (null == process)
            {
                throw new Exception("Process.Start failed to return a non-null process");
            }

            if (redirectIO)
            {
                process.OutputDataReceived += (sender, e) =>
                {
                    if (streamOutputToInteractiveService)
                    {
                        _interactiveService.LogMessageLine(e.Data);
                    }

                    strOutput.Append(e.Data);
                };

                process.ErrorDataReceived += (sender, e) =>
                {
                    if (streamOutputToInteractiveService)
                    {
                        _interactiveService.LogMessageLine(e.Data);
                    }

                    strError.Append(e.Data);
                };
                process.BeginOutputReadLine();
                process.BeginErrorReadLine();
            }

            // poll for process to prevent blocking the main thread
            // as opposed to using process.WaitForExit()
            // in .net5 we can use process.WaitForExitAsync()
            while (true)
            {
                if (process.HasExited)
                {
                    break;
                }

                await Task.Delay(TimeSpan.FromMilliseconds(50), cancelToken);
            }

            if (onComplete != null)
            {
                var result = new TryRunResult
                {
                    ExitCode = process.ExitCode
                };

                if (redirectIO)
                {
                    result.StandardError = strError.ToString();
                    result.StandardOut   = strOutput.ToString();
                }

                onComplete(result);
            }
        }
Example #2
0
        /// <inheritdoc />
        public async Task Run(
            string command,
            string workingDirectory = "",
            bool streamOutputToInteractiveService = true,
            Action <TryRunResult> onComplete      = null,
            bool redirectIO = true,
            CancellationToken cancelToken = default)
        {
            StringBuilder strOutput   = new StringBuilder();
            StringBuilder strError    = new StringBuilder();
            var           credentials = await _awsCredentials.GetCredentialsAsync();

            var processStartInfo = new ProcessStartInfo
            {
                FileName = GetSystemShell(),

                Arguments =
                    RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
                        ? $"/c {command}"
                        : $"-c \"{command}\"",

                RedirectStandardInput  = redirectIO,
                RedirectStandardOutput = redirectIO,
                RedirectStandardError  = redirectIO,
                UseShellExecute        = false,
                CreateNoWindow         = redirectIO,
                WorkingDirectory       = workingDirectory
            };

            // environment variables could already be set at the machine level,
            // use this syntax to make sure we don't create duplicate entries
            processStartInfo.EnvironmentVariables["AWS_ACCESS_KEY_ID"]     = credentials.AccessKey;
            processStartInfo.EnvironmentVariables["AWS_SECRET_ACCESS_KEY"] = credentials.SecretKey;
            processStartInfo.EnvironmentVariables["AWS_REGION"]            = _awsRegion;

            if (credentials.UseToken)
            {
                processStartInfo.EnvironmentVariables["AWS_SESSION_TOKEN"] = credentials.Token;
            }

            var process = Process.Start(processStartInfo);

            if (null == process)
            {
                throw new Exception("Process.Start failed to return a non-null process");
            }

            if (redirectIO && streamOutputToInteractiveService)
            {
                process.OutputDataReceived += (sender, e) => {
                    _interactiveService.LogMessageLine(e.Data);
                    strOutput.Append(e.Data);
                };
                process.ErrorDataReceived += (sender, e) => {
                    _interactiveService.LogMessageLine(e.Data);
                    strError.Append(e.Data);
                };
                process.BeginOutputReadLine();
                process.BeginErrorReadLine();
            }

            // poll for process to prevent blocking the main thread
            // as opposed to using process.WaitForExit()
            // in .net5 we can use process.WaitForExitAsync()
            while (true)
            {
                if (process.HasExited)
                {
                    break;
                }

                await Task.Delay(TimeSpan.FromMilliseconds(50), cancelToken);
            }

            if (onComplete != null)
            {
                var result = new TryRunResult
                {
                    ExitCode = process.ExitCode
                };

                if (redirectIO)
                {
                    result.StandardError = streamOutputToInteractiveService ? strError.ToString() : await process.StandardError.ReadToEndAsync();

                    result.StandardOut = streamOutputToInteractiveService ? strOutput.ToString() : await process.StandardOutput.ReadToEndAsync();
                }

                onComplete(result);
            }
        }