diff --git a/src/RepoM.ActionMenu.CodeGen/RepoM.ActionMenu.CodeGen.csproj b/src/RepoM.ActionMenu.CodeGen/RepoM.ActionMenu.CodeGen.csproj index 918aae54..5b9471e7 100644 --- a/src/RepoM.ActionMenu.CodeGen/RepoM.ActionMenu.CodeGen.csproj +++ b/src/RepoM.ActionMenu.CodeGen/RepoM.ActionMenu.CodeGen.csproj @@ -8,7 +8,7 @@ - + diff --git a/src/RepoM.ActionMenu.Core/ActionMenu/Model/ActionMenus/BrowseRepository/RepositoryActionBrowseRepositoryV1Mapper.cs b/src/RepoM.ActionMenu.Core/ActionMenu/Model/ActionMenus/BrowseRepository/RepositoryActionBrowseRepositoryV1Mapper.cs index 3f4e8db1..e3239765 100644 --- a/src/RepoM.ActionMenu.Core/ActionMenu/Model/ActionMenus/BrowseRepository/RepositoryActionBrowseRepositoryV1Mapper.cs +++ b/src/RepoM.ActionMenu.Core/ActionMenu/Model/ActionMenus/BrowseRepository/RepositoryActionBrowseRepositoryV1Mapper.cs @@ -4,7 +4,6 @@ namespace RepoM.ActionMenu.Core.ActionMenu.Model.ActionMenus.BrowseRepository; using System.Linq; using System.Threading.Tasks; using JetBrains.Annotations; -using RepoM.ActionMenu.Core.Model; using RepoM.ActionMenu.Interface.ActionMenuFactory; using RepoM.ActionMenu.Interface.UserInterface; using RepoM.ActionMenu.Interface.YamlModel; @@ -21,25 +20,28 @@ protected override async IAsyncEnumerable Map yield break; } - var name = await context.RenderStringAsync(action.Name).ConfigureAwait(false); - if (string.IsNullOrWhiteSpace(name)) - { - name = "Browse remote"; - } + string menuTitle; + //var menuTitle = await context.RenderStringAsync(action.Name).ConfigureAwait(false); + //if (string.IsNullOrWhiteSpace(menuTitle)) + //{ + // menuTitle = "Github: "; + //} var forceSingle = await action.FirstOnly.EvaluateAsync(context).ConfigureAwait(false); if (repository.Remotes.Count == 1 || forceSingle) { - yield return new UserInterfaceRepositoryAction(name, repository) + menuTitle = TryGet_RemoteRepoLongName(repository.Remotes[0].Url); + yield return new UserInterfaceRepositoryAction(menuTitle, repository) { RepositoryCommand = new BrowseRepositoryCommand(repository.Remotes[0].Url), }; } else { + menuTitle = "Remote repos"; yield return new DeferredSubActionsUserInterfaceRepositoryAction( - name, + menuTitle, repository, context, captureScope: false, @@ -50,14 +52,27 @@ protected override async IAsyncEnumerable Map } } + private static string TryGet_RemoteRepoLongName(string remoteUrl) + { + var splitString = remoteUrl.Trim().Split('/'); + if (splitString.Length < 2) + { + return remoteUrl; + } + + var name = splitString.Last().Replace(".git", ""); + var author = splitString[^2]; + return $"{author}/{name}"; + } + private static Task EnumerateRemotes(IRepository repository) { return Task.FromResult(repository.Remotes .Take(50) - .Select(remote => new UserInterfaceRepositoryAction(remote.Name, repository) - { - RepositoryCommand = new BrowseRepositoryCommand(remote.Url), - }) + .Select(remote => new UserInterfaceRepositoryAction(TryGet_RemoteRepoLongName(remote.Url), repository) + { + RepositoryCommand = new BrowseRepositoryCommand(remote.Url), + }) .Cast() .ToArray()); } diff --git a/src/RepoM.ActionMenu.Core/ActionMenu/Model/ActionMenus/Pin/RepositoryActionPinV1.cs b/src/RepoM.ActionMenu.Core/ActionMenu/Model/ActionMenus/Pin/RepositoryActionPinV1.cs index 61b2637a..db12acb6 100644 --- a/src/RepoM.ActionMenu.Core/ActionMenu/Model/ActionMenus/Pin/RepositoryActionPinV1.cs +++ b/src/RepoM.ActionMenu.Core/ActionMenu/Model/ActionMenus/Pin/RepositoryActionPinV1.cs @@ -6,7 +6,7 @@ namespace RepoM.ActionMenu.Core.ActionMenu.Model.ActionMenus.Pin; using RepoM.ActionMenu.Interface.YamlModel.Templating; /// -/// Action to pin (or unpin) the current repository. Pinning is not persistant and all pinned repositories will be cleared when RepoM exits. +/// Action to pin (or unpin) the current repository. Pinning is not persistent and all pinned repositories will be cleared when RepoM exits. /// Pinning a repository allowed custom filtering, ordering and searching. /// [RepositoryAction(TYPE_VALUE)] @@ -21,7 +21,7 @@ public string Type } /// - [Text("(Un)Pin repository")] + [Text("Pin / Unpin Repo")] public Text Name { get; set; } = null!; /// @@ -34,7 +34,7 @@ public string Type /// /// The pin mode `[Toggle, Pin, UnPin]`. /// - public PinMode? Mode { get; set; } // GitHub issue: https://github.com/coenm/RepoM/issues/87 + public PinMode? Mode { get; set; } = PinMode.Toggle; // GitHub issue: https://github.com/coenm/RepoM/issues/87 public override string ToString() { diff --git a/src/RepoM.Api/Bootstrapper.cs b/src/RepoM.Api/Bootstrapper.cs index 237588eb..3cf745c9 100644 --- a/src/RepoM.Api/Bootstrapper.cs +++ b/src/RepoM.Api/Bootstrapper.cs @@ -1,38 +1,38 @@ namespace RepoM.Api; -using Microsoft.Extensions.Logging; -using RepoM.Api.Common; -using RepoM.Api.IO; -using RepoM.Api.Plugins; -using SimpleInjector; +using System; using System.Collections.Generic; using System.IO.Abstractions; using System.Linq; using System.Reflection; using System.Threading.Tasks; -using System; +using Microsoft.Extensions.Logging; +using RepoM.Api.Common; +using RepoM.Api.IO; +using RepoM.Api.Plugins; using RepoM.Api.Plugins.SimpleInjector; using RepoM.Core.Plugin; using RepoM.Core.Plugin.Common; using RepoM.Core.Plugin.RepositoryOrdering.Configuration; +using SimpleInjector; public class CoreBootstrapper { - private readonly IPluginFinder _pluginFinder; - private readonly IFileSystem _fileSystem; + private readonly IPluginFinder _pluginFinder; + private readonly IFileSystem _fileSystem; private readonly IAppDataPathProvider _appDataProvider; - private readonly ILoggerFactory _loggerFactory; + private readonly ILoggerFactory _loggerFactory; private static readonly Assembly _thisAssembly = typeof(CoreBootstrapper).Assembly; public CoreBootstrapper(IPluginFinder pluginFinder, IFileSystem fileSystem, IAppDataPathProvider appDataProvider, ILoggerFactory loggerFactory) { - _pluginFinder = pluginFinder ?? throw new ArgumentNullException(nameof(pluginFinder)); - _fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem)); + _pluginFinder = pluginFinder ?? throw new ArgumentNullException(nameof(pluginFinder)); + _fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem)); _appDataProvider = appDataProvider ?? throw new ArgumentNullException(nameof(appDataProvider)); - _loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory)); + _loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory)); } - + public static void RegisterRepositoryScorerConfigurationsTypes(Container container) { foreach (Type type in GetNonAbstractNonGenericInheritedExportedTypesFrom(_thisAssembly)) diff --git a/src/RepoM.Api/EnsureStartup.cs b/src/RepoM.Api/EnsureStartup.cs index 16307919..5f7c8586 100644 --- a/src/RepoM.Api/EnsureStartup.cs +++ b/src/RepoM.Api/EnsureStartup.cs @@ -9,12 +9,12 @@ namespace RepoM.Api; public class EnsureStartup { - private readonly IFileSystem _fileSystem; + private readonly IFileSystem _fileSystem; private readonly IAppDataPathProvider _appDataProvider; public EnsureStartup(IFileSystem fileSystem, IAppDataPathProvider appDataProvider) { - _fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem)); + _fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem)); _appDataProvider = appDataProvider ?? throw new ArgumentNullException(nameof(appDataProvider)); } diff --git a/src/RepoM.Api/Git/DefaultRepositoryMonitor.cs b/src/RepoM.Api/Git/DefaultRepositoryMonitor.cs index 8dc4f26d..1e9a07e5 100644 --- a/src/RepoM.Api/Git/DefaultRepositoryMonitor.cs +++ b/src/RepoM.Api/Git/DefaultRepositoryMonitor.cs @@ -60,7 +60,7 @@ public DefaultRepositoryMonitor( _fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _autoFetchHandler = autoFetchHandler ?? throw new ArgumentNullException(nameof(autoFetchHandler)); - _repositoryObservers = new Dictionary(); + _repositoryObservers = []; _storeFlushTimer = new Timer(RepositoryStoreFlushTimerCallback, null, Timeout.Infinite, Timeout.Infinite); } @@ -153,7 +153,7 @@ private void ObserveRepositoryChanges() { _logger.LogTrace("ObserveRepositoryChanges start"); - _detectors = new List(); + _detectors = []; foreach (var path in _pathProvider.GetPaths()) { diff --git a/src/RepoM.Api/Resources/RepoM.Filtering.yaml b/src/RepoM.Api/Resources/RepoM.Filtering.yaml index 52ff09c4..39415ff4 100644 --- a/src/RepoM.Api/Resources/RepoM.Filtering.yaml +++ b/src/RepoM.Api/Resources/RepoM.Filtering.yaml @@ -1,3 +1,12 @@ +Private: + name: Private + description: Private repositories + always-visible: + kind: query@1 + query: RepoM OR is:pinned + filter: + kind: query@1 + query: tag:private Work: name: Work description: Work filtering @@ -7,12 +16,3 @@ Work: filter: kind: query@1 query: tag:work -Private: - name: Private - description: Private repositories - always-visible: - kind: query@1 - query: RepoM OR is:pinned - filter: - kind: query@1 - query: (-tag:work OR tag:private) \ No newline at end of file diff --git a/src/RepoM.Api/Resources/RepoM.Sorting.yaml b/src/RepoM.Api/Resources/RepoM.Sorting.yaml index 8985b78e..1f1261e7 100644 --- a/src/RepoM.Api/Resources/RepoM.Sorting.yaml +++ b/src/RepoM.Api/Resources/RepoM.Sorting.yaml @@ -1,35 +1,12 @@ -Work: - type: composition-comparer@1 - comparers: - - type: score-comparer@1 - score-provider: - type: is-pinned-scorer@1 - weight: 1 - - type: score-comparer@1 - score-provider: - type: tag-scorer@1 - weight: 1 - tag: Team1 - - type: score-comparer@1 - score-provider: - type: tag-scorer@1 - weight: 1 - tag: Work - - type: az-comparer@1 - property: Name - weight: 1 -Work Dynamic: +Most Active: type: composition-comparer@1 comparers: - type: score-comparer@1 score-provider: type: is-pinned-scorer@1 - weight: 1 - - type: score-comparer@1 - score-provider: - type: tag-scorer@1 - weight: 1 - tag: Team1 + weight: 40 + - type: last-opened-comparer@1 + weight: 30 - type: score-comparer@1 score-provider: type: usage-scorer@1 @@ -46,18 +23,31 @@ Work Dynamic: max-items: 5 - until: 168:00:00 weight: 1 - max-items: 10 - - type: last-opened-comparer@1 + max-items: 10 + - type: az-comparer@1 + property: Name weight: 1 +Work: + type: composition-comparer@1 + comparers: - type: score-comparer@1 - score-provider: + score-provider: + type: is-pinned-scorer@1 + weight: 1 + - type: score-comparer@1 + score-provider: + type: tag-scorer@1 + weight: 1 + tag: Team1 + - type: score-comparer@1 + score-provider: type: tag-scorer@1 weight: 1 tag: Work - type: az-comparer@1 property: Name weight: 1 -Prive: +Work Dynamic: type: composition-comparer@1 comparers: - type: score-comparer@1 @@ -68,7 +58,7 @@ Prive: score-provider: type: tag-scorer@1 weight: 1 - tag: Prive + tag: Team1 - type: score-comparer@1 score-provider: type: usage-scorer@1 @@ -88,6 +78,11 @@ Prive: max-items: 10 - type: last-opened-comparer@1 weight: 1 + - type: score-comparer@1 + score-provider: + type: tag-scorer@1 + weight: 1 + tag: Work - type: az-comparer@1 property: Name - weight: 1 \ No newline at end of file + weight: 1 diff --git a/src/RepoM.Api/Resources/RepositoryActionsV2.yaml b/src/RepoM.Api/Resources/RepositoryActionsV2.yaml index f3890467..414ac7f6 100644 --- a/src/RepoM.Api/Resources/RepositoryActionsV2.yaml +++ b/src/RepoM.Api/Resources/RepositoryActionsV2.yaml @@ -1,16 +1,20 @@ -context: -- type: evaluate-script@1 +context: +- type : evaluate-script@1 content: |- func is_null(input) ret input == null end + func is_not_null(input) + ret input != null + end + func get_filename(path) ret path | string.split("\\") | array.last end func remotes_contain_inner(remotes, url_part) - urls = remotes | array.map "url" + urls = remotes | array.map "url" filtered = array.filter(urls, do ret string.contains($0, url_part) end) @@ -22,9 +26,9 @@ context: end func get_remote_origin() - remotes = repository.remotes; + remotes = repository.remotes; filtered = array.filter(remotes, do - remote = $0; + remote = $0; ret remote.key == "origin" end) ret array.first(filtered); @@ -47,133 +51,210 @@ context: ret repository.branch | string.replace "feature/" "" | string.strip end - remote_name_origin = get_remote_origin_name(); - is_work_repository = remotes_contain("My-Work"); + func try_get_remote_repo_long_name(long_url) + splitString = long_url | string.strip | string.split "/"; + if array.size(splitString) < 2 + ret long_url + end + name = array.last(splitString) | string.replace ".git" "" + author = splitString[^2] + ret author + "/" + name + end + + func try_get_remote_origin_long_name() + if is_null(repository.remotes) || array.size(repository.remotes) == 0 + ret repository.name + end + ret try_get_remote_repo_long_name(repository.remotes[0].url) + end + + remote_name_origin = get_remote_origin_name(); + is_work_repository = remotes_contain("My-Work"); is_github_repository = remotes_contain("github.com"); solution_files = file.find_files(repository.linux_path, "*.sln"); - solution_file = array.first(solution_files); + solution_file = array.first(solution_files); - exe_vs_code = env.LocalAppData + "/Programs/Microsoft VS Code/code.exe"; + dir_visual_studio = env.ProgramW6432 + "/Microsoft Visual Studio/"; + dir_windows_terminal_Store = env.LocalAppData + "/Packages/Microsoft.WindowsTerminal_8wekyb3d8bbwe/"; + dir_windows_terminal_Preview = env.LocalAppData + "/Packages/Microsoft.WindowsTerminalPreview_8wekyb3d8bbwe/"; + dir_windows_terminal_Unpackaged = env.LocalAppData + "/Packages/Microsoft/WindowsTerminal/"; + + exe_vs_code = env.LocalAppData + "/Programs/Microsoft VS Code/code.exe"; + exe_sourcetree = env.LocalAppData + "/SourceTree/SourceTree.exe"; + exe_gitkraken = env.LocalAppData + "/GitKraken/GitKraken.exe"; + exe_everything = env.ProgramW6432 + "/Everything/Everything.exe"; + exe_terminal_commander = env.ProgramW6432 + "/totalcmd/TOTALCMD64.EXE"; + # Specific var files -- type: render-variable@1 - name: repo_docs_directory - value: 'G:\\My Drive\\RepoDocs\\github.com\\{{ remote_name_origin }}' - enabled: is_github_repository +- type : render-variable@1 + name : repo_docs_directory + value : 'G:\\My Drive\\RepoDocs\\github.com\\{{ remote_name_origin }}' + enabled : is_github_repository # Env files -- type: render-variable@1 - name: repo_environment_file_directory - value: '{{ env.REPOZ_CONFIG_PATH }}\{{ remote_name_origin }}' +- type : render-variable@1 + name : repo_environment_file_directory + value : '{{ env.REPOZ_CONFIG_PATH }}\{{ remote_name_origin }}' -- type: render-variable@1 - name: repo_environment_file - value: '{{ env.REPOZ_CONFIG_PATH }}\{{ remote_name_origin }}\RepoM.env' +- type : render-variable@1 + name : repo_environment_file + value : '{{ env.REPOZ_CONFIG_PATH }}\{{ remote_name_origin }}\RepoM.env' -- type: render-variable@1 - name: repo_yaml_file - value: 'C:\\WorkCofigs\{{ remote_name_origin }}\RepoMV2.yaml' +- type : render-variable@1 + name : repo_yaml_file + value : 'C:\\WorkCofigs\{{ remote_name_origin }}\RepoMV2.yaml' # Runsettings -- type: load-file@1 - filename: '{{ repo_environment_file }}' - enabled: is_work_repository - -- type: load-file@1 - filename: '{{ env.REPOZ_CONFIG_PATH }}\work.env' - enabled: is_work_repository - -- type: load-file@1 - filename: '{{ repo_yaml_file }}' - enabled: is_work_repository - -action-menu: - -- type: command@1 - name: Open in Windows File Explorer - command: '"{{ repository.path }}"' - -- type: command@1 - name: Open in Windows Terminal - command: wt - arguments: -d "{{ repository.linux_path }}" - -- type: executable@1 - name: 'Open in Windows PowerShell' - executable: '{{ env.WINDIR }}/System32/WindowsPowerShell/v1.0/powershell.exe' - arguments: -executionpolicy bypass -noexit -command "Set-Location '{{ repository.linux_path }}'" - -# Open in visual studio when exactly one '.sln' file was found: -- type: command@1 - name: Open in Visual Studio - command: '{{ solution_file }}' - active: array.size(solution_files) == 1 - -# Otherwise, Visual studio folder with all '.sln' files when multiple sln files were found: -- type: folder@1 - name: Open in Visual Studio - active: array.size(solution_files) > 1 - actions: - - type: foreach@1 - enumerable: solution_files - variable: sln - actions: - - type: command@1 - name: '{{ get_filename(sln) }}' - command: '{{ sln }}' +- type : load-file@1 + filename : '{{ repo_environment_file }}' + enabled : is_work_repository + +- type : load-file@1 + filename : '{{ env.REPOZ_CONFIG_PATH }}\work.env' + enabled : is_work_repository + +- type : load-file@1 + filename : '{{ repo_yaml_file }}' + enabled : is_work_repository + +action-menu : + +- type : command@1 + name : ๐Ÿ“‚ Open in Windows File Explorer + command : explorer + arguments : '{{ repository.path }}' + +# Open in visual studio when exactly one '.sln' file was found: +- type : command@1 + name : ๐Ÿ—ƒ๏ธ Open in Visual Studio + command : '{{ solution_file }}' + active : array.size(solution_files) == 1 && file.dir_exists(dir_visual_studio) + +# Otherwise, Visual studio folder with all '.sln' files when multiple sln files were found: +- type : folder@1 + name : ๐Ÿ—ƒ๏ธ Open in Visual Studio + active : array.size(solution_files) > 1 && file.dir_exists(dir_visual_studio) + actions : + - type : foreach@1 + enumerable : solution_files + variable : sln + actions : + - type : command@1 + name : '{{ get_filename(sln) }}' + command : '{{ sln }}' -- type: executable@1 - name: Open in Visual Studio Code - executable: '{{ exe_vs_code }}' - arguments: '"{{ repository.linux_path }}"' - -- type: executable@1 - name: Open in Sourcetree - executable: '{{ env.LocalAppData }}/SourceTree/SourceTree.exe' - arguments: -f "{{ repository.windows_path }}" - -- type: executable@1 - name: Open in Everything - executable: '{{ env.ProgramW6432 }}/Everything/Everything.exe' - arguments: -s """"{{ repository.path }}""" " - -- type: executable@1 - name: Open in TotalCommander - executable: '{{ env.ProgramW6432 }}/totalcmd/TOTALCMD64.EXE' - arguments: /O /T /L="{{ repository.linux_path }}" - -- type: separator@1 - -- type: folder@1 - name: Git - actions: - - type: browse-repository@1 - - type: git-fetch@1 - - type: git-pull@1 - - type: git-push@1 - - type: git-checkout@1 - -- type: separator@1 - -- type: ignore-repository@1 - -- type: separator@1 - active: is_work_repository - -- type: folder@1 - name: '-- Examples --' - context: - - type: render-variable@1 - name: repo_docs_directory - value: 'C:\docs\{{ repository.name }}\' - actions: +- 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 + executable : '{{ exe_sourcetree }}' + arguments : -f "{{ repository.windows_path }}" + active : file.file_exists(exe_sourcetree) + +- type : executable@1 + name : ๐Ÿ“Š Open in GitKraken ๐Ÿฆ‘ + executable : '{{ exe_gitkraken }}' + arguments : --path "{{ repository.path }}" + +- type : command@1 + name : ๐Ÿงพ Open in Windows Terminal + command : wt + arguments : -d "{{ repository.path }}" + active : file.dir_exists(dir_windows_terminal_Store) || file.dir_exists(dir_windows_terminal_Preview) || file.dir_exists(dir_windows_terminal_Unpackaged) + +- type : executable@1 + name : ๐Ÿ” Open in Everything + executable : '{{ exe_everything }}' + arguments : -s """"{{ repository.path }}""" " + active : file.file_exists(exe_everything) + +- type : executable@1 + name : Open in TotalCommander + executable : '{{ exe_terminal_commander }}' + arguments : /O /T /L="{{ repository.linux_path }}" + active : file.file_exists(exe_terminal_commander) + +# - type : executable@1 +# name : 'Open in Windows PowerShell' +# executable : '{{ env.WINDIR }}/System32/WindowsPowerShell/v1.0/powershell.exe' +# arguments : -executionpolicy bypass -noexit -command "Set-Location '{{ repository.linux_path }}'" + +# - type : executable@1 +# name : 'Open in Windows Command Prompt' +# executable : '{{ env.WINDIR }}/System32/cmd.exe' +# arguments : /k cd "{{ repository.linux_path }}" + +- type : separator@1 + +- type : just-text@1 + name : 'Warning: No remote repos to browse to' + active : array.size(repository.remotes) == 0 + +# single remote, create url menu item +- type : url@1 + name : '๐Ÿ”— {{ try_get_remote_origin_long_name() }}' + url : '{{ repository.remotes[0].url }}' + active : array.size(repository.remotes) == 1 + +# Multiple remotes, create folder with multiple menu items +- type : folder@1 + name : ๐Ÿ”— Remote repos + active : array.size(repository.remotes) > 1 + actions : + - type : foreach@1 + enumerable : repository.remotes + variable : remote + actions : + - type : url@1 + name : '{{ try_get_remote_repo_long_name(remote.url) }} [{{ remote.key }}] ' + url : '{{ remote.url }}' + +# - type : browse-repository@1 +- type : folder@1 + name : ๐Ÿ“ฆ Git + actions : + - type : git-fetch@1 + - type : git-pull@1 + - type : git-push@1 + - type : git-checkout@1 + +- type : separator@1 + # active: is_work_repository + +- type : pin-repository@1 + name : "๐Ÿ“Œ Pin / Unpin Repo" + mode : toggle + +- type : folder@1 + name : โ“ About {{try_get_remote_origin_long_name()}} + actions : - - type: just-text@1 - name: 'Current branch is {{ repository.branch }}' - - - type: command@1 - name: 'Create Directory {{ repo_docs_directory }}' - command: cmd - arguments: /k mkdir "{{ repo_docs_directory }}" - active: '!file.dir_exists(repo_docs_directory)' + - type : clipboard-copy@1 + name : 'Current branch is {{ repository.branch }}' + text : '{{ repository.branch }}' + + - type : clipboard-copy@1 + name : 'Local path is {{ repository.windows_path }}' + text : '{{ repository.windows_path }}' + + - type : separator@1 + + - type : ignore-repository@1 + name : โŒ Ignore Repository + + # context : + # - type : render-variable@1 + # name : repo_docs_directory + # value : 'C:\docs\{{ repository.name }}\' + + # - type : command@1 + # name : 'Create Directory {{ repo_docs_directory }}' + # command : cmd + # arguments : /k mkdir "{{ repo_docs_directory }}" + # active : '!file.dir_exists(repo_docs_directory)' diff --git a/src/RepoM.Api/Resources/TagsV2.yaml b/src/RepoM.Api/Resources/TagsV2.yaml index 9570cc93..a86e7595 100644 --- a/src/RepoM.Api/Resources/TagsV2.yaml +++ b/src/RepoM.Api/Resources/TagsV2.yaml @@ -36,11 +36,12 @@ context: tags: -- tag: work - when: is_work_repository +- tag: github + when: 'remotes_contain("github.com")' - tag: private when: '!is_work_repository && repository_path_contains("Projects/Private")' -- tag: github - when: 'remotes_contain("github.com")' \ No newline at end of file +- tag: work + when: is_work_repository + diff --git a/src/RepoM.App/RepositoryFiltering/IRepositoryFilteringManager.cs b/src/RepoM.App/RepositoryFiltering/IRepositoryFilteringManager.cs index 5129520b..6821e4bd 100644 --- a/src/RepoM.App/RepositoryFiltering/IRepositoryFilteringManager.cs +++ b/src/RepoM.App/RepositoryFiltering/IRepositoryFilteringManager.cs @@ -27,5 +27,5 @@ public interface IRepositoryFilteringManager bool SetQueryParser(string key); - bool SetFilter(string key); + bool SetFilter(string filterName); } \ No newline at end of file diff --git a/src/RepoM.App/RepositoryFiltering/RepositoryFilteringManager.cs b/src/RepoM.App/RepositoryFiltering/RepositoryFilteringManager.cs index f6e6fcf6..9fb3fd63 100644 --- a/src/RepoM.App/RepositoryFiltering/RepositoryFilteringManager.cs +++ b/src/RepoM.App/RepositoryFiltering/RepositoryFilteringManager.cs @@ -16,7 +16,7 @@ internal class RepositoryFilteringManager : IRepositoryFilteringManager private readonly QueryParserComposition _queryParser; private readonly List _repositoryComparerKeys; private readonly List _preFilterKeys; - private readonly List _queryDictionary; + private readonly Dictionary _queryDictionary; public RepositoryFilteringManager( IAppSettingsService appSettingsService, @@ -38,28 +38,33 @@ public RepositoryFilteringManager( INamedQueryParser defaultParser = queryParsersArray.First(x => x.Name != "Lucene"); INamedQueryParser queryParser = Array.Find(queryParsersArray, x => x.Name == "Lucene") ?? defaultParser; - _queryDictionary = filterSettingsService.Configuration - .Select(x => new RepositoryFilterConfiguration - { - AlwaysVisible = Map(x.Value.AlwaysVisible), - Description = x.Value.Description, - Filter = Map(x.Value.Filter), - Name = x.Key, - }) - .ToList(); - - if (!_queryDictionary.Exists(x => x.Name.Equals("Default", StringComparison.CurrentCultureIgnoreCase))) + _queryDictionary = new Dictionary((int)StringComparison.CurrentCultureIgnoreCase); + foreach (var (key, value) in filterSettingsService.Configuration) { - _queryDictionary.Add(new RepositoryFilterConfiguration - { - AlwaysVisible = null, - Description = "Default (no filtering)", - Filter = null, - Name = "Default", - }); + _queryDictionary.Add(key, new RepositoryFilterConfiguration + { + AlwaysVisible = Map(value.AlwaysVisible), + Description = value.Description, + Filter = Map(value.Filter), + Name = key, + }); + } + + var allConfiguration = new RepositoryFilterConfiguration + { + Name = "All", + AlwaysVisible = null, + Description = "Show all (no filtering) [Default]", + Filter = null, + }; + + if (!_queryDictionary.TryAdd(allConfiguration.Name, allConfiguration)) + { + _logger.LogWarning("Invalid filter config detected: {FilterName}. Overwriting element.", allConfiguration.Name); + _queryDictionary[allConfiguration.Name] = allConfiguration; } - - _preFilterKeys = _queryDictionary.Select(x => x.Name).ToList(); + + _preFilterKeys = _queryDictionary.Keys.ToList(); _queryParser = new QueryParserComposition(queryParsersArray); @@ -69,24 +74,22 @@ public RepositoryFilteringManager( if (string.IsNullOrWhiteSpace(_appSettingsService.QueryParserKey)) { - _logger.LogInformation("Query parser was not set. Pick first one."); + _logger.LogInformation("Query parser was not set. Pick default one one."); SetQueryParser(_repositoryComparerKeys[0]); } else if (!SetQueryParser(_appSettingsService.QueryParserKey)) { - _logger.LogInformation("Could not set query parser '{Key}'. Falling back to first query parser.", _appSettingsService.QueryParserKey); + _logger.LogInformation("Could not set query parser '{Key}'. Falling back to default parser.", _appSettingsService.QueryParserKey); SetQueryParser(_repositoryComparerKeys[0]); } - RepositoryFilterConfiguration first = _queryDictionary[0]; - - if (string.IsNullOrWhiteSpace(_appSettingsService.SelectedFilter)) + if (_queryDictionary.TryGetValue("Default", out RepositoryFilterConfiguration? defaultFilterConfig) && !string.IsNullOrWhiteSpace(_appSettingsService.SelectedFilter)) { - SetFilter(first.Name); + SetFilter(defaultFilterConfig); } - else if (!SetFilter(_appSettingsService.SelectedFilter)) + else { - SetFilter(first.Name); + SetFilter(allConfiguration); } return; @@ -126,7 +129,7 @@ public RepositoryFilteringManager( public IReadOnlyList FilterKeys => _preFilterKeys; public bool SetQueryParser(string key) - { + { if (!_queryParser.SetComparer(key)) { _logger.LogWarning("Could not update/set the comparer key {Key}.", key); @@ -139,20 +142,25 @@ public bool SetQueryParser(string key) return true; } - public bool SetFilter(string key) + private void SetFilter(RepositoryFilterConfiguration filterConfig) + { + PreFilter = filterConfig.Filter ?? TrueQuery.Instance; + AlwaysVisibleFilter = filterConfig.AlwaysVisible; + _appSettingsService.SelectedFilter = filterConfig.Name; + SelectedFilterKey = filterConfig.Name; + SelectedFilterChanged?.Invoke(this, filterConfig.Name); + } + + public bool SetFilter(string filterName) { - RepositoryFilterConfiguration? value = _queryDictionary.Find(x => x.Name == key); - if (value == null) + if (_queryDictionary.TryGetValue(filterName, out RepositoryFilterConfiguration? filterConfig)) { - return false; + SetFilter(filterConfig); + return true; } - - PreFilter = value.Filter ?? TrueQuery.Instance; - AlwaysVisibleFilter = value.AlwaysVisible; - _appSettingsService.SelectedFilter = key; - SelectedFilterKey = key; - SelectedFilterChanged?.Invoke(this, key); - return true; + + _logger.LogWarning("Could not find filter with filterName {Key}.", filterName); + return false; } private sealed class RepositoryFilterConfiguration diff --git a/src/RepoM.Plugin.AzureDevOps/Internal/AzureDevOpsPullRequestService.cs b/src/RepoM.Plugin.AzureDevOps/Internal/AzureDevOpsPullRequestService.cs index a1cc2168..c883c164 100644 --- a/src/RepoM.Plugin.AzureDevOps/Internal/AzureDevOpsPullRequestService.cs +++ b/src/RepoM.Plugin.AzureDevOps/Internal/AzureDevOpsPullRequestService.cs @@ -25,7 +25,7 @@ internal sealed class AzureDevOpsPullRequestService : IAzureDevOpsPullRequestSer private readonly ILogger _logger; private readonly VssConnection? _connection; private GitHttpClient? _azureDevopsGitClient; - private readonly List _emptyList = new(0); + private readonly List _emptyList = []; private Timer? _updateTimer1; private Timer? _updateTimer2; @@ -36,7 +36,7 @@ internal sealed class AzureDevOpsPullRequestService : IAzureDevOpsPullRequestSer public AzureDevOpsPullRequestService(IAzureDevopsConfiguration configuration, ILogger logger) { - _httpClient = new(); + _httpClient = new HttpClient(); _configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); @@ -53,8 +53,8 @@ public AzureDevOpsPullRequestService(IAzureDevopsConfiguration configuration, IL _configuration.AzureDevOpsBaseUrl, new VssBasicCredential(string.Empty, _configuration.AzureDevOpsPersonalAccessToken)); _httpClient.BaseAddress = _configuration.AzureDevOpsBaseUrl; - _httpClient.DefaultRequestHeaders.Accept.Add(new("application/json")); - _httpClient.DefaultRequestHeaders.Authorization = new("Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes($":{_configuration.AzureDevOpsPersonalAccessToken}"))); + _httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes($":{_configuration.AzureDevOpsPersonalAccessToken}"))); } } diff --git a/src/RepoM.Plugin.AzureDevOps/Internal/PullRequest.cs b/src/RepoM.Plugin.AzureDevOps/Internal/PullRequest.cs index d51aa79c..0e638769 100644 --- a/src/RepoM.Plugin.AzureDevOps/Internal/PullRequest.cs +++ b/src/RepoM.Plugin.AzureDevOps/Internal/PullRequest.cs @@ -7,8 +7,8 @@ internal class PullRequest public PullRequest(Guid repositoryId, string name, string url) { RepositoryId = repositoryId; - Name = name; - Url = url; + Name = name; + Url = url; } public Guid RepositoryId { get; } diff --git a/src/RepoM.Plugin.AzureDevOps/Internal/WorkItemExtractor.cs b/src/RepoM.Plugin.AzureDevOps/Internal/WorkItemExtractor.cs index ddd3e03d..29a78c53 100644 --- a/src/RepoM.Plugin.AzureDevOps/Internal/WorkItemExtractor.cs +++ b/src/RepoM.Plugin.AzureDevOps/Internal/WorkItemExtractor.cs @@ -10,9 +10,9 @@ internal static partial class WorkItemExtractor public static string[] GetDistinctWorkItemsFromCommitMessages(IEnumerable commitMessages) { - List results = new(); + List results = []; - foreach (string commitMessage in commitMessages) + foreach (var commitMessage in commitMessages) { MatchCollection matches = _workItemRegex.Matches(commitMessage); if (matches.Any(m => m.Success)) diff --git a/src/RepoM.Plugin.Clipboard/ActionMenu/Model/ActionMenus/ClipboardCopy/RepositoryActionClipboardCopyV1.cs b/src/RepoM.Plugin.Clipboard/ActionMenu/Model/ActionMenus/ClipboardCopy/RepositoryActionClipboardCopyV1.cs index 63b612df..2b1c02f8 100644 --- a/src/RepoM.Plugin.Clipboard/ActionMenu/Model/ActionMenus/ClipboardCopy/RepositoryActionClipboardCopyV1.cs +++ b/src/RepoM.Plugin.Clipboard/ActionMenu/Model/ActionMenus/ClipboardCopy/RepositoryActionClipboardCopyV1.cs @@ -36,7 +36,7 @@ public string Type [Required] [Text] public Text Text { get; set; } = null!; - + /// public Context? Context { get; set; } diff --git a/src/RepoM.Plugin.Statistics/StatisticsModule.cs b/src/RepoM.Plugin.Statistics/StatisticsModule.cs index f5e47275..717a344b 100644 --- a/src/RepoM.Plugin.Statistics/StatisticsModule.cs +++ b/src/RepoM.Plugin.Statistics/StatisticsModule.cs @@ -17,43 +17,40 @@ namespace RepoM.Plugin.Statistics; [UsedImplicitly] internal class StatisticsModule : IModule { - private readonly IStatisticsService _service; + private readonly IStatisticsService _service; private readonly IStatisticsConfiguration _configuration; - private readonly IClock _clock; - private readonly IAppDataPathProvider _pathProvider; - private readonly IFileSystem _fileSystem; - private readonly ILogger _logger; - private string _basePath = string.Empty; - private IDisposable? _disposable; - private readonly JsonSerializerSettings _settings; - - public StatisticsModule( - IStatisticsService service, - IStatisticsConfiguration configuration, - IClock clock, - IAppDataPathProvider pathProvider, - IFileSystem fileSystem, - ILogger logger) + private readonly IClock _clock; + private readonly IAppDataPathProvider _pathProvider; + private readonly IFileSystem _fileSystem; + private readonly ILogger _logger; + private string _basePath = string.Empty; + private IDisposable? _disposable; + private readonly JsonSerializerSettings _settings = new() { - _service = service ?? throw new ArgumentNullException(nameof(service)); + Formatting = Formatting.None, + NullValueHandling = NullValueHandling.Ignore, + TypeNameHandling = TypeNameHandling.All, + }; + + public StatisticsModule(IStatisticsService service, + IStatisticsConfiguration configuration, + IClock clock, + IAppDataPathProvider pathProvider, + IFileSystem fileSystem, + ILogger logger) + { + _service = service ?? throw new ArgumentNullException(nameof(service)); _configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); - _clock = clock ?? throw new ArgumentNullException(nameof(clock)); - _pathProvider = pathProvider ?? throw new ArgumentNullException(nameof(pathProvider)); - _fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - - _settings = new JsonSerializerSettings - { - Formatting = Formatting.None, - NullValueHandling = NullValueHandling.Ignore, - TypeNameHandling = TypeNameHandling.All, - }; + _clock = clock ?? throw new ArgumentNullException(nameof(clock)); + _pathProvider = pathProvider ?? throw new ArgumentNullException(nameof(pathProvider)); + _fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } public async Task StartAsync() { _basePath = _fileSystem.Path.Combine(_pathProvider.AppDataPath, "Module", "Statistics"); - + _disposable = WriteEventsToFile(); await ProcessEventsFromFile().ConfigureAwait(false); @@ -151,12 +148,12 @@ private IDisposable WriteEventsToFile() .Buffer(buffer) .Subscribe(data => { - IEvent[] events = [.. data, ]; + IEvent[] events = [.. data,]; if (events.Length == 0) { return; } - + var json = JsonConvert.SerializeObject(events, _settings); var filename = _fileSystem.Path.Combine(_basePath, $"statistics.v1.{_clock.Now:yyyy-MM-dd HH.mm.ss}.json"); diff --git a/tests/RepoM.Plugin.Heidi.Tests/EnvironmentVariableManager.cs b/tests/RepoM.Plugin.Heidi.Tests/EnvironmentVariableManager.cs index 38622fb1..fbc72a7a 100644 --- a/tests/RepoM.Plugin.Heidi.Tests/EnvironmentVariableManager.cs +++ b/tests/RepoM.Plugin.Heidi.Tests/EnvironmentVariableManager.cs @@ -20,16 +20,16 @@ public static IDisposable SetEnvironmentVariable(string key, string value) private sealed class ReleaseDisposable : IDisposable { - private readonly object _lock = new(); - private AutoResetEvent? _are; - private readonly string _key; - private readonly string? _value; + private readonly object _lock = new(); + private AutoResetEvent? _are; + private readonly string _key; + private readonly string? _value; public ReleaseDisposable(AutoResetEvent are, string key, string? value) { - _are = are; - _key = key; + _key = key; _value = value; + _are = are; } public void Dispose()