// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; using Amazon.Runtime; using AWS.Deploy.Common; using AWS.Deploy.Orchestration; using AWS.Deploy.Orchestration.Utilities; namespace AWS.Deploy.CLI.Utilities { public class CommandLineWrapper : ICommandLineWrapper { private readonly IOrchestratorInteractiveService _interactiveService; private readonly bool _useSeparateWindow; private Action? _processStartInfoAction; public CommandLineWrapper( IOrchestratorInteractiveService interactiveService) { _interactiveService = interactiveService; } public CommandLineWrapper( IOrchestratorInteractiveService interactiveService, bool useSeparateWindow) : this(interactiveService) { _useSeparateWindow = useSeparateWindow; } /// public async Task Run( string command, string workingDirectory = "", bool streamOutputToInteractiveService = true, Action? onComplete = null, bool redirectIO = true, string? stdin = null, IDictionary? 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 || stdin != null, 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.LogInfoMessage(e.Data); } strOutput.AppendLine(e.Data); }; process.ErrorDataReceived += (sender, e) => { if(streamOutputToInteractiveService) { _interactiveService.LogInfoMessage(e.Data); } strError.AppendLine(e.Data); }; process.BeginOutputReadLine(); process.BeginErrorReadLine(); } if(stdin != null) { process.StandardInput.Write(stdin); process.StandardInput.Close(); } await process.WaitForExitAsync(); if (onComplete != null) { var result = new TryRunResult { ExitCode = process.ExitCode }; if (redirectIO) { result.StandardError = strError.ToString(); result.StandardOut = strOutput.ToString(); } onComplete(result); } } private static void UpdateEnvironmentVariables(ProcessStartInfo processStartInfo, IDictionary? environmentVariables) { if (environmentVariables == null) { return; } foreach (var (key, value) in environmentVariables) { if (key == EnvironmentVariableKeys.AWS_EXECUTION_ENV) { var awsExecutionEnvValue = BuildAWSExecutionEnvValue(processStartInfo, value); processStartInfo.EnvironmentVariables[key] = awsExecutionEnvValue; } else { processStartInfo.EnvironmentVariables[key] = value; } } } private static string BuildAWSExecutionEnvValue(ProcessStartInfo processStartInfo, string awsExecutionEnv) { var awsExecutionEnvBuilder = new StringBuilder(); if (processStartInfo.EnvironmentVariables.ContainsKey(EnvironmentVariableKeys.AWS_EXECUTION_ENV) && !string.IsNullOrEmpty(processStartInfo.EnvironmentVariables[EnvironmentVariableKeys.AWS_EXECUTION_ENV])) { awsExecutionEnvBuilder.Append(processStartInfo.EnvironmentVariables[EnvironmentVariableKeys.AWS_EXECUTION_ENV]); } if (!string.IsNullOrEmpty(awsExecutionEnv)) { if (awsExecutionEnvBuilder.Length != 0) { awsExecutionEnvBuilder.Append("_"); } awsExecutionEnvBuilder.Append(awsExecutionEnv); } return awsExecutionEnvBuilder.ToString(); } public void ConfigureProcess(Action? processStartInfoAction) { _processStartInfoAction = processStartInfoAction; } private string GetSystemShell() { if (TryGetEnvironmentVariable("COMSPEC", out var comspec)) return comspec!; if (TryGetEnvironmentVariable("SHELL", out var shell)) return shell!; // fall back to defaults return RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "cmd.exe" : "/bin/sh"; } private bool TryGetEnvironmentVariable(string variable, out string? value) { value = Environment.GetEnvironmentVariable(variable); return !string.IsNullOrEmpty(value); } } }