From 646427082c3a7f366d2a13e763eec17edacd6868 Mon Sep 17 00:00:00 2001 From: Gabriel Vanca Date: Sat, 14 Sep 2024 20:15:32 +0100 Subject: [PATCH 1/2] fixed process executor --- .../RepositoryActionCommandV1Mapper.cs | 6 +- src/RepoM.Api/IO/ProcessHelper.cs | 71 ++++++++++++++++--- .../Executors/StartProcessCommandExecutor.cs | 11 +++ .../Resources/RepositoryActionsV2.yaml | 18 ++--- 4 files changed, 85 insertions(+), 21 deletions(-) diff --git a/src/RepoM.ActionMenu.Core/ActionMenu/Model/ActionMenus/Command/RepositoryActionCommandV1Mapper.cs b/src/RepoM.ActionMenu.Core/ActionMenu/Model/ActionMenus/Command/RepositoryActionCommandV1Mapper.cs index f46b3cd7..8c6cc977 100644 --- a/src/RepoM.ActionMenu.Core/ActionMenu/Model/ActionMenus/Command/RepositoryActionCommandV1Mapper.cs +++ b/src/RepoM.ActionMenu.Core/ActionMenu/Model/ActionMenus/Command/RepositoryActionCommandV1Mapper.cs @@ -18,8 +18,8 @@ protected override async IAsyncEnumerable Map var arguments = await action.Arguments.RenderAsync(context).ConfigureAwait(false); yield return new UserInterfaceRepositoryAction(name, repository) - { - RepositoryCommand = new StartProcessRepositoryCommand(command, arguments), - }; + { + RepositoryCommand = new StartProcessRepositoryCommand(command, arguments), + }; } } \ No newline at end of file diff --git a/src/RepoM.Api/IO/ProcessHelper.cs b/src/RepoM.Api/IO/ProcessHelper.cs index 3380809e..d44e94f2 100644 --- a/src/RepoM.Api/IO/ProcessHelper.cs +++ b/src/RepoM.Api/IO/ProcessHelper.cs @@ -1,34 +1,85 @@ namespace RepoM.Api.IO; using System; +using System.Collections.Generic; using System.Diagnostics; +using System.Linq; using Microsoft.Extensions.Logging; +using static RepoM.Api.IO.ProcessHelper; public static class ProcessHelper { public static void StartProcess(string process, string arguments, ILogger logger) { + List exceptionList = []; + + var psi = new ProcessStartInfo() + { + FileName = process, // The file to run. + Arguments = arguments, // Arguments to pass to the process. + UseShellExecute = false, // Execute the process directly rather than using the shell. Similar to double-clicking the file in Explorer or the RunAs command. + }; + try { - Process.Start(process, arguments); + var processState = Process.Start(psi); + + // This is probably never null as RunAs always throws an exception if there is a problem, but we check just in case. + if (null == processState) { throw new Exception("Process failed to start."); } + + logger.LogInformation("Successfully started process {Process} with arguments {Arguments} via primary method.", process, arguments); return; } - catch (Exception) + catch (Exception ex) { - // swallow, retry below. + /* + * This method wil fail if the process is: + * a. not found in the PATH system variable + * b. not found in %LOCALAPPDATA%\Microsoft\WindowsApps + * c. %LOCALAPPDATA%\Microsoft\WindowsApps is not in the PATH. + */ + logger.LogInformation(ex, "Failed to start process {Process} with arguments {Arguments} via primary method. Attempt via secondary method.", process, arguments); + exceptionList.Add(ex); } try { - var psi = new ProcessStartInfo(process, arguments) - { - UseShellExecute = true, - }; - Process.Start(psi); + psi.UseShellExecute = true; // Uses shell execute to allow for file associations to work. The default is true on .NET Framework apps and false on .NET Core apps. + psi.WindowStyle = ProcessWindowStyle.Hidden; // Hides the shell/terminal window. + var processState = Process.Start(psi); + + // Sometimes the shell execute method fails to start the process but does not throw an exception so we need to check if the process is null. + if (null == processState) { throw new Exception("Process failed to start."); } + + logger.LogInformation("Successfully started process {Process} with arguments {Arguments} via secondary method.", process, arguments); + return; } catch (Exception ex) { - logger.LogError(ex, "Failed to start process {Process} with arguments {Arguments}", process, arguments); + logger.LogInformation(ex, "Failed to start process {Process} with arguments {Arguments} via secondary method.", process, arguments); + exceptionList.Add(ex); } + + /* + * We only log the exceptions as errors if we failed to start the process through all the methods. + * Otherwise, we log them as information. + */ + foreach ((Exception currentException, var index) in exceptionList.WithIndex()) + { + logger.LogError(currentException, "Failed to start process {Process} with arguments {Arguments}. [{Index}/{Count}]", process, arguments, index, exceptionList.Count); + } + } -} \ No newline at end of file + +} + + +// [CODE REFACTORING] Move this class somewhere else + +// ReSharper disable once InconsistentNaming +public static class IEnumerableExtensions +{ + public static IEnumerable<(T item, int index)> WithIndex(this IEnumerable self) + => self?.Select((item, index) => (item, index)) ?? new List<(T, int)>(); +} + diff --git a/src/RepoM.Api/RepositoryActions/Executors/StartProcessCommandExecutor.cs b/src/RepoM.Api/RepositoryActions/Executors/StartProcessCommandExecutor.cs index 63c28ad7..2ebd241d 100644 --- a/src/RepoM.Api/RepositoryActions/Executors/StartProcessCommandExecutor.cs +++ b/src/RepoM.Api/RepositoryActions/Executors/StartProcessCommandExecutor.cs @@ -17,6 +17,17 @@ public StartProcessCommandExecutor(ILogger logger) _logger = logger; } + /* + * The purpose of this function is to execute a process specified by the + * Executable property of the repositoryCommand object, along with + * any additional arguments passed in the Arguments property. + * The ProcessHelper.StartProcess method is responsible for starting + * the process with the specified executable and arguments. + * Overall, this function takes care of executing a process based on + * the provided repository command, handling different scenarios + * for the number of arguments, + * and logging any relevant information using the _logger object. + */ public void Execute(IRepository repository, StartProcessRepositoryCommand repositoryCommand) { var args = string.Empty; diff --git a/src/RepoM.Api/Resources/RepositoryActionsV2.yaml b/src/RepoM.Api/Resources/RepositoryActionsV2.yaml index f3890467..5298316e 100644 --- a/src/RepoM.Api/Resources/RepositoryActionsV2.yaml +++ b/src/RepoM.Api/Resources/RepositoryActionsV2.yaml @@ -1,4 +1,4 @@ -context: +๏ปฟcontext: - type: evaluate-script@1 content: |- func is_null(input) @@ -90,9 +90,10 @@ context: action-menu: -- type: command@1 - name: Open in Windows File Explorer - command: '"{{ repository.path }}"' +- type : command@1 + name : ๐Ÿ“‚ Open in Windows File Explorer + command : explorer + arguments : '{{ repository.path }}' - type: command@1 name: Open in Windows Terminal @@ -123,10 +124,11 @@ action-menu: name: '{{ get_filename(sln) }}' command: '{{ sln }}' -- type: executable@1 - name: Open in Visual Studio Code - executable: '{{ exe_vs_code }}' - arguments: '"{{ repository.linux_path }}"' +- type : command@1 + name : ๐Ÿ“ Open in Visual Studio Code + command : '{{ exe_vs_code }}' + arguments : '"{{ repository.path }}"' + active : file.file_exists(exe_vs_code) - type: executable@1 name: Open in Sourcetree From 4655bf3fa1acc711695dca0649c7124158586e11 Mon Sep 17 00:00:00 2001 From: Gabriel Vanca Date: Sat, 14 Sep 2024 20:40:03 +0100 Subject: [PATCH 2/2] icon problem --- src/RepoM.Api/Resources/RepositoryActionsV2.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/RepoM.Api/Resources/RepositoryActionsV2.yaml b/src/RepoM.Api/Resources/RepositoryActionsV2.yaml index 5298316e..5e97f22f 100644 --- a/src/RepoM.Api/Resources/RepositoryActionsV2.yaml +++ b/src/RepoM.Api/Resources/RepositoryActionsV2.yaml @@ -91,7 +91,7 @@ action-menu: - type : command@1 - name : ๐Ÿ“‚ Open in Windows File Explorer + name : Open in Windows File Explorer command : explorer arguments : '{{ repository.path }}' @@ -125,7 +125,7 @@ action-menu: command: '{{ sln }}' - type : command@1 - name : ๐Ÿ“ Open in Visual Studio Code + name : Open in Visual Studio Code command : '{{ exe_vs_code }}' arguments : '"{{ repository.path }}"' active : file.file_exists(exe_vs_code)