From b4e76c292f498235044e6d81d2951527c778de96 Mon Sep 17 00:00:00 2001 From: Mark Hajek Date: Mon, 5 Apr 2021 15:17:15 +0100 Subject: [PATCH 01/11] extracted IGit and implementation --- ModernRonin.ProjectRenamer/Application.cs | 33 ++++++------------- ModernRonin.ProjectRenamer/Git.cs | 39 +++++++++++++++++++++++ ModernRonin.ProjectRenamer/IGit.cs | 12 +++++++ ModernRonin.ProjectRenamer/Program.cs | 1 + 4 files changed, 62 insertions(+), 23 deletions(-) create mode 100644 ModernRonin.ProjectRenamer/Git.cs create mode 100644 ModernRonin.ProjectRenamer/IGit.cs diff --git a/ModernRonin.ProjectRenamer/Application.cs b/ModernRonin.ProjectRenamer/Application.cs index c4262e6..ff7d5d9 100644 --- a/ModernRonin.ProjectRenamer/Application.cs +++ b/ModernRonin.ProjectRenamer/Application.cs @@ -11,6 +11,7 @@ public class Application { readonly Configuration _configuration; readonly IExecutor _executor; + readonly Git _git; readonly IInput _input; readonly ILogger _logger; readonly IRuntime _runtime; @@ -21,7 +22,8 @@ public Application(Configuration configuration, IExecutor executor, IRuntime runtime, ILogger logger, - IInput input) + IInput input, + Git git) { _configuration = configuration; _solutionPath = solutionPath; @@ -29,11 +31,12 @@ public Application(Configuration configuration, _runtime = runtime; _logger = logger; _input = input; + _git = git; } public void Run() { - EnsureGitIsClean(); + _git.EnsureIsClean(); var (wasFound, oldProjectPath, solutionFolderPath) = findProject(); if (!wasFound) @@ -47,7 +50,7 @@ public void Run() var newFileName = Path.GetFileName(_configuration.NewProjectName); var newProjectPath = Path.Combine(newDir, $"{newFileName}{Constants.ProjectFileExtension}"); var isPaketUsed = Directory.Exists(".paket"); - var gitVersion = _executor.GitRead("--version"); + var gitVersion = _git.GetVersion(); if (!_configuration.DontReviewSettings) { var lines = new[] @@ -78,7 +81,7 @@ public void Run() addNewReferences(); addToSolution(); updatePaket(); - stageAllChanges(); + _git.StageAllChanges(); build(); commit(); @@ -139,9 +142,7 @@ void commit() var msg = wasMove ? $"Moved {oldProjectPath.ToRelativePath(CurrentDirectoryAbsolute)} to {newProjectPath.ToRelativePath(CurrentDirectoryAbsolute)}" : $"Renamed {_configuration.OldProjectName} to {_configuration.NewProjectName}"; - var arguments = $"commit -m \"{msg}\""; - _executor.Git(arguments, - () => { _logger.Error($"'git {arguments}' failed"); }); + _git.Commit(msg); } } @@ -155,15 +156,13 @@ void build() "dotnet build returned an error or warning - do you want to rollback all changes?") ) { - _executor.RollbackGit(); + _git.RollbackAllChanges(); _runtime.Abort(); } }); } } - void stageAllChanges() => _executor.Git("add ."); - void updatePaket() { if (isPaketUsed && !_configuration.DontRunPaketInstall) _executor.DotNet("paket install"); @@ -194,7 +193,7 @@ bool isPaketReference(string line) void gitMove() { Directory.CreateDirectory(Path.GetDirectoryName(newDir)); - _executor.Git($"mv {oldDir} {newDir}"); + _git.Move(oldDir, newDir); var oldPath = Path.GetFileName(oldProjectPath).ToAbsolutePath(newDir); if (oldPath != newProjectPath) _executor.Git($"mv {oldPath} {newProjectPath}"); } @@ -252,18 +251,6 @@ string[] filesIn(string directory) => } } - void EnsureGitIsClean() - { - run("update-index -q --refresh"); - run("diff-index --quiet --cached HEAD --"); - run("diff-files --quiet"); - run("ls-files --exclude-standard --others"); - - void run(string arguments) => - _executor.Git(arguments, - () => _executor.Error("git does not seem to be clean, check git status")); - } - static string CurrentDirectoryAbsolute => Path.GetFullPath(Directory.GetCurrentDirectory()); } } \ No newline at end of file diff --git a/ModernRonin.ProjectRenamer/Git.cs b/ModernRonin.ProjectRenamer/Git.cs new file mode 100644 index 0000000..f547d18 --- /dev/null +++ b/ModernRonin.ProjectRenamer/Git.cs @@ -0,0 +1,39 @@ +namespace ModernRonin.ProjectRenamer +{ + public class Git : IGit + { + readonly IExecutor _executor; + readonly ILogger _logger; + + public Git(IExecutor executor, ILogger logger) + { + _executor = executor; + _logger = logger; + } + + public void Commit(string msg) + { + var arguments = $"commit -m \"{msg}\""; + _executor.Git(arguments, + () => { _logger.Error($"'git {arguments}' failed"); }); + } + + public void EnsureIsClean() + { + run("update-index -q --refresh"); + run("diff-index --quiet --cached HEAD --"); + run("diff-files --quiet"); + run("ls-files --exclude-standard --others"); + + void run(string arguments) => + _executor.Git(arguments, + () => _executor.Error("git does not seem to be clean, check git status")); + } + + public string GetVersion() => _executor.GitRead("--version"); + + public void Move(string oldDir, string newDir) => _executor.Git($"mv {oldDir} {newDir}"); + public void RollbackAllChanges() => _executor.Git("reset --hard HEAD", () => { }); + public void StageAllChanges() => _executor.Git("add ."); + } +} \ No newline at end of file diff --git a/ModernRonin.ProjectRenamer/IGit.cs b/ModernRonin.ProjectRenamer/IGit.cs new file mode 100644 index 0000000..a9d6c12 --- /dev/null +++ b/ModernRonin.ProjectRenamer/IGit.cs @@ -0,0 +1,12 @@ +namespace ModernRonin.ProjectRenamer +{ + public interface IGit + { + void Commit(string msg); + void EnsureIsClean(); + string GetVersion(); + void Move(string oldDir, string newDir); + void RollbackAllChanges(); + void StageAllChanges(); + } +} \ No newline at end of file diff --git a/ModernRonin.ProjectRenamer/Program.cs b/ModernRonin.ProjectRenamer/Program.cs index 717c243..d5c0b5a 100644 --- a/ModernRonin.ProjectRenamer/Program.cs +++ b/ModernRonin.ProjectRenamer/Program.cs @@ -24,6 +24,7 @@ static IContainer WireUp() builder.RegisterType().AsImplementedInterfaces(); builder.RegisterType().AsImplementedInterfaces(); builder.RegisterType().AsImplementedInterfaces(); + builder.RegisterType().AsImplementedInterfaces(); builder.RegisterType().AsSelf(); return builder.Build(); From 202d5fee60b47f257a8d62404c88fbc41fdc7f54 Mon Sep 17 00:00:00 2001 From: Mark Hajek Date: Mon, 5 Apr 2021 15:28:23 +0100 Subject: [PATCH 02/11] extracted IErrorHandler and implementation --- ModernRonin.ProjectRenamer/Application.cs | 12 +++--- .../ConfigurationSetup.cs | 10 +++-- ModernRonin.ProjectRenamer/ErrorHandler.cs | 39 +++++++++++++++++++ ModernRonin.ProjectRenamer/Executor.cs | 31 +++------------ ModernRonin.ProjectRenamer/Git.cs | 8 ++-- ModernRonin.ProjectRenamer/IErrorHandler.cs | 9 +++++ ModernRonin.ProjectRenamer/IExecutor.cs | 2 - ModernRonin.ProjectRenamer/IGit.cs | 2 +- ModernRonin.ProjectRenamer/Program.cs | 6 +++ 9 files changed, 79 insertions(+), 40 deletions(-) create mode 100644 ModernRonin.ProjectRenamer/ErrorHandler.cs create mode 100644 ModernRonin.ProjectRenamer/IErrorHandler.cs diff --git a/ModernRonin.ProjectRenamer/Application.cs b/ModernRonin.ProjectRenamer/Application.cs index ff7d5d9..8d7dd7f 100644 --- a/ModernRonin.ProjectRenamer/Application.cs +++ b/ModernRonin.ProjectRenamer/Application.cs @@ -10,8 +10,9 @@ namespace ModernRonin.ProjectRenamer public class Application { readonly Configuration _configuration; + readonly IErrorHandler _errors; readonly IExecutor _executor; - readonly Git _git; + readonly IGit _git; readonly IInput _input; readonly ILogger _logger; readonly IRuntime _runtime; @@ -23,7 +24,8 @@ public Application(Configuration configuration, IRuntime runtime, ILogger logger, IInput input, - Git git) + IGit git, + IErrorHandler errors) { _configuration = configuration; _solutionPath = solutionPath; @@ -32,6 +34,7 @@ public Application(Configuration configuration, _logger = logger; _input = input; _git = git; + _errors = errors; } public void Run() @@ -39,8 +42,7 @@ public void Run() _git.EnsureIsClean(); var (wasFound, oldProjectPath, solutionFolderPath) = findProject(); - if (!wasFound) - _executor.Error($"{_configuration.OldProjectName} cannot be found in the solution"); + if (!wasFound) _errors.Handle($"{_configuration.OldProjectName} cannot be found in the solution"); var oldDir = Path.GetDirectoryName(oldProjectPath); var newBase = _configuration.NewProjectName.Any(CommonExtensions.IsDirectorySeparator) @@ -195,7 +197,7 @@ void gitMove() Directory.CreateDirectory(Path.GetDirectoryName(newDir)); _git.Move(oldDir, newDir); var oldPath = Path.GetFileName(oldProjectPath).ToAbsolutePath(newDir); - if (oldPath != newProjectPath) _executor.Git($"mv {oldPath} {newProjectPath}"); + if (oldPath != newProjectPath) _git.Move(oldPath, newProjectPath); } void addToSolution() diff --git a/ModernRonin.ProjectRenamer/ConfigurationSetup.cs b/ModernRonin.ProjectRenamer/ConfigurationSetup.cs index 1fe216a..858346f 100644 --- a/ModernRonin.ProjectRenamer/ConfigurationSetup.cs +++ b/ModernRonin.ProjectRenamer/ConfigurationSetup.cs @@ -10,14 +10,16 @@ namespace ModernRonin.ProjectRenamer public class ConfigurationSetup : IConfigurationSetup { readonly ILogger _console; + readonly IErrorHandler _errors; readonly IExecutor _executor; readonly IRuntime _runtime; - public ConfigurationSetup(IExecutor executor, ILogger console, IRuntime runtime) + public ConfigurationSetup(IExecutor executor, ILogger console, IRuntime runtime, IErrorHandler errors) { _executor = executor; _console = console; _runtime = runtime; + _errors = errors; } public (Configuration configuration, string solutionPath) Get(string[] commandLineArguments) @@ -25,7 +27,7 @@ public ConfigurationSetup(IExecutor executor, ILogger console, IRuntime runtime) var solutionFiles = Directory.EnumerateFiles(".", "*.sln", SearchOption.TopDirectoryOnly).ToArray(); if (1 != solutionFiles.Length) - _executor.Error("Needs to be run from a directory with exactly one *.sln file in it."); + _errors.Handle("Needs to be run from a directory with exactly one *.sln file in it."); var solutionPath = Path.GetFullPath(solutionFiles.First()); switch (ParseCommandLine(commandLineArguments)) { @@ -36,7 +38,7 @@ public ConfigurationSetup(IExecutor executor, ILogger console, IRuntime runtime) case (var helpOverview, Configuration configuration): if (configuration.OldProjectName.Any(CommonExtensions.IsDirectorySeparator)) { - _executor.Error( + _errors.Handle( $"Do not specify paths for input/'old' project names, please.{Environment.NewLine}{Environment.NewLine}{helpOverview}"); } @@ -45,7 +47,7 @@ public ConfigurationSetup(IExecutor executor, ILogger console, IRuntime runtime) return (configuration, solutionPath); default: - _executor.Error( + _errors.Handle( "Something went seriously wrong. Please create an issue at https://github.com/ModernRonin/ProjectRenamer with as much detail as possible."); break; } diff --git a/ModernRonin.ProjectRenamer/ErrorHandler.cs b/ModernRonin.ProjectRenamer/ErrorHandler.cs new file mode 100644 index 0000000..cefa6a1 --- /dev/null +++ b/ModernRonin.ProjectRenamer/ErrorHandler.cs @@ -0,0 +1,39 @@ +using System; + +namespace ModernRonin.ProjectRenamer +{ + public class ErrorHandler : IErrorHandler + { + readonly Lazy _git; + readonly ILogger _logger; + readonly IRuntime _runtime; + + public ErrorHandler(ILogger logger, Lazy git, IRuntime runtime) + { + _logger = logger; + _git = git; + _runtime = runtime; + } + + public void Handle(string msg) => Handle(msg, false); + + public void Handle(string tool, string arguments) + { + Handle($"call '{tool} {arguments}' failed - aborting", true); + } + + public void HandleAndReset(string msg) => Handle(msg, true); + + void Handle(string msg, bool doResetGit) + { + _logger.Error(msg); + if (doResetGit) + { + _logger.Error("...running git reset to undo any changes..."); + _git.Value.RollbackAllChanges(); + } + + _runtime.Abort(); + } + } +} \ No newline at end of file diff --git a/ModernRonin.ProjectRenamer/Executor.cs b/ModernRonin.ProjectRenamer/Executor.cs index eea3589..86f8604 100644 --- a/ModernRonin.ProjectRenamer/Executor.cs +++ b/ModernRonin.ProjectRenamer/Executor.cs @@ -6,13 +6,13 @@ public class Executor : IExecutor { const string ToolDotnet = "dotnet"; const string ToolGit = "git"; - readonly ILogger _logger; + readonly IErrorHandler _errors; readonly IRuntime _runtime; - public Executor(IRuntime runtime, ILogger logger) + public Executor(IRuntime runtime, IErrorHandler errors) { _runtime = runtime; - _logger = logger; + _errors = errors; } public void DotNet(string arguments) => Tool(ToolDotnet, arguments); @@ -21,19 +21,7 @@ public void DotNet(string arguments, Action onNonZeroExitCode) => Tool(ToolDotnet, arguments, onNonZeroExitCode); public string DotNetRead(string arguments) => - ToolRead(ToolDotnet, arguments, () => StandardErrorHandler(ToolDotnet, arguments)); - - public void Error(string msg, bool doResetGit = false) - { - _logger.Error(msg); - if (doResetGit) - { - _logger.Error("...running git reset to undo any changes..."); - RollbackGit(); - } - - _runtime.Abort(); - } + ToolRead(ToolDotnet, arguments, () => _errors.Handle(ToolDotnet, arguments)); public void Git(string arguments) => Tool(ToolGit, arguments); @@ -41,14 +29,7 @@ public void Git(string arguments, Action onNonZeroExitCode) => Tool(ToolGit, arguments, onNonZeroExitCode); public string GitRead(string arguments) => - ToolRead(ToolGit, arguments, () => StandardErrorHandler(ToolGit, arguments)); - - public void RollbackGit() => Git("reset --hard HEAD", () => { }); - - void StandardErrorHandler(string tool, string arguments) - { - Error($"call '{tool} {arguments}' failed - aborting", true); - } + ToolRead(ToolGit, arguments, () => _errors.Handle(ToolGit, arguments)); void Tool(string tool, string arguments, Action onNonZeroExitCode) { @@ -57,7 +38,7 @@ void Tool(string tool, string arguments, Action onNonZeroExitCode) } void Tool(string tool, string arguments) => - Tool(tool, arguments, () => StandardErrorHandler(tool, arguments)); + Tool(tool, arguments, () => _errors.Handle(tool, arguments)); string ToolRead(string tool, string arguments, Action onNonZeroExitCode) { diff --git a/ModernRonin.ProjectRenamer/Git.cs b/ModernRonin.ProjectRenamer/Git.cs index f547d18..6f52092 100644 --- a/ModernRonin.ProjectRenamer/Git.cs +++ b/ModernRonin.ProjectRenamer/Git.cs @@ -2,13 +2,15 @@ { public class Git : IGit { + readonly IErrorHandler _errors; readonly IExecutor _executor; readonly ILogger _logger; - public Git(IExecutor executor, ILogger logger) + public Git(IExecutor executor, ILogger logger, IErrorHandler errors) { _executor = executor; _logger = logger; + _errors = errors; } public void Commit(string msg) @@ -27,12 +29,12 @@ public void EnsureIsClean() void run(string arguments) => _executor.Git(arguments, - () => _executor.Error("git does not seem to be clean, check git status")); + () => _errors.Handle("git does not seem to be clean, check git status")); } public string GetVersion() => _executor.GitRead("--version"); - public void Move(string oldDir, string newDir) => _executor.Git($"mv {oldDir} {newDir}"); + public void Move(string oldPath, string newPath) => _executor.Git($"mv {oldPath} {newPath}"); public void RollbackAllChanges() => _executor.Git("reset --hard HEAD", () => { }); public void StageAllChanges() => _executor.Git("add ."); } diff --git a/ModernRonin.ProjectRenamer/IErrorHandler.cs b/ModernRonin.ProjectRenamer/IErrorHandler.cs new file mode 100644 index 0000000..d0589c6 --- /dev/null +++ b/ModernRonin.ProjectRenamer/IErrorHandler.cs @@ -0,0 +1,9 @@ +namespace ModernRonin.ProjectRenamer +{ + public interface IErrorHandler + { + void Handle(string msg); + void Handle(string tool, string arguments); + void HandleAndReset(string msg); + } +} \ No newline at end of file diff --git a/ModernRonin.ProjectRenamer/IExecutor.cs b/ModernRonin.ProjectRenamer/IExecutor.cs index d5db880..70831d3 100644 --- a/ModernRonin.ProjectRenamer/IExecutor.cs +++ b/ModernRonin.ProjectRenamer/IExecutor.cs @@ -7,10 +7,8 @@ public interface IExecutor void DotNet(string arguments); void DotNet(string arguments, Action onNonZeroExitCode); string DotNetRead(string arguments); - void Error(string msg, bool doResetGit = false); void Git(string arguments); void Git(string arguments, Action onNonZeroExitCode); string GitRead(string arguments); - void RollbackGit(); } } \ No newline at end of file diff --git a/ModernRonin.ProjectRenamer/IGit.cs b/ModernRonin.ProjectRenamer/IGit.cs index a9d6c12..58d2f0c 100644 --- a/ModernRonin.ProjectRenamer/IGit.cs +++ b/ModernRonin.ProjectRenamer/IGit.cs @@ -5,7 +5,7 @@ public interface IGit void Commit(string msg); void EnsureIsClean(); string GetVersion(); - void Move(string oldDir, string newDir); + void Move(string oldPath, string newPath); void RollbackAllChanges(); void StageAllChanges(); } diff --git a/ModernRonin.ProjectRenamer/Program.cs b/ModernRonin.ProjectRenamer/Program.cs index d5c0b5a..40e1695 100644 --- a/ModernRonin.ProjectRenamer/Program.cs +++ b/ModernRonin.ProjectRenamer/Program.cs @@ -16,6 +16,11 @@ static void Main(string[] args) .Run(); } + void StandardErrorHandler(string tool, string arguments) + { + Error($"call '{tool} {arguments}' failed - aborting", true); + } + static IContainer WireUp() { var builder = new ContainerBuilder(); @@ -23,6 +28,7 @@ static IContainer WireUp() builder.RegisterType().AsImplementedInterfaces(); builder.RegisterType().AsImplementedInterfaces(); builder.RegisterType().AsImplementedInterfaces(); + builder.RegisterType().AsImplementedInterfaces(); builder.RegisterType().AsImplementedInterfaces(); builder.RegisterType().AsImplementedInterfaces(); builder.RegisterType().AsSelf(); From 98e34bf13f52be86669bfe3f1b5741b8f5eef710 Mon Sep 17 00:00:00 2001 From: Mark Hajek Date: Mon, 5 Apr 2021 15:45:41 +0100 Subject: [PATCH 03/11] moved the git specific bits of IExecutor to IGit --- ModernRonin.ProjectRenamer/Executor.cs | 15 +++---------- ModernRonin.ProjectRenamer/Git.cs | 28 ++++++++++++++++++------- ModernRonin.ProjectRenamer/IExecutor.cs | 6 +++--- ModernRonin.ProjectRenamer/Program.cs | 5 ----- 4 files changed, 26 insertions(+), 28 deletions(-) diff --git a/ModernRonin.ProjectRenamer/Executor.cs b/ModernRonin.ProjectRenamer/Executor.cs index 86f8604..e26dac7 100644 --- a/ModernRonin.ProjectRenamer/Executor.cs +++ b/ModernRonin.ProjectRenamer/Executor.cs @@ -5,7 +5,6 @@ namespace ModernRonin.ProjectRenamer public class Executor : IExecutor { const string ToolDotnet = "dotnet"; - const string ToolGit = "git"; readonly IErrorHandler _errors; readonly IRuntime _runtime; @@ -23,24 +22,16 @@ public void DotNet(string arguments, Action onNonZeroExitCode) => public string DotNetRead(string arguments) => ToolRead(ToolDotnet, arguments, () => _errors.Handle(ToolDotnet, arguments)); - public void Git(string arguments) => Tool(ToolGit, arguments); - - public void Git(string arguments, Action onNonZeroExitCode) => - Tool(ToolGit, arguments, onNonZeroExitCode); - - public string GitRead(string arguments) => - ToolRead(ToolGit, arguments, () => _errors.Handle(ToolGit, arguments)); - - void Tool(string tool, string arguments, Action onNonZeroExitCode) + public void Tool(string tool, string arguments, Action onNonZeroExitCode) { _runtime.DoWithTool(tool, arguments, onNonZeroExitCode, psi => psi.RedirectStandardOutput = false, _ => { }); } - void Tool(string tool, string arguments) => + public void Tool(string tool, string arguments) => Tool(tool, arguments, () => _errors.Handle(tool, arguments)); - string ToolRead(string tool, string arguments, Action onNonZeroExitCode) + public string ToolRead(string tool, string arguments, Action onNonZeroExitCode) { var result = string.Empty; _runtime.DoWithTool(tool, arguments, onNonZeroExitCode, psi => psi.RedirectStandardOutput = true, diff --git a/ModernRonin.ProjectRenamer/Git.cs b/ModernRonin.ProjectRenamer/Git.cs index 6f52092..23c17f4 100644 --- a/ModernRonin.ProjectRenamer/Git.cs +++ b/ModernRonin.ProjectRenamer/Git.cs @@ -1,7 +1,11 @@ -namespace ModernRonin.ProjectRenamer +using System; + +namespace ModernRonin.ProjectRenamer { public class Git : IGit { + const string ToolGit = "git"; + readonly IErrorHandler _errors; readonly IExecutor _executor; readonly ILogger _logger; @@ -16,8 +20,8 @@ public Git(IExecutor executor, ILogger logger, IErrorHandler errors) public void Commit(string msg) { var arguments = $"commit -m \"{msg}\""; - _executor.Git(arguments, - () => { _logger.Error($"'git {arguments}' failed"); }); + Run(arguments, + () => _logger.Error($"'git {arguments}' failed")); } public void EnsureIsClean() @@ -28,14 +32,22 @@ public void EnsureIsClean() run("ls-files --exclude-standard --others"); void run(string arguments) => - _executor.Git(arguments, + Run(arguments, () => _errors.Handle("git does not seem to be clean, check git status")); } - public string GetVersion() => _executor.GitRead("--version"); + public string GetVersion() => Read("--version"); + + public void Move(string oldPath, string newPath) => Run($"mv {oldPath} {newPath}"); + public void RollbackAllChanges() => Run("reset --hard HEAD", () => { }); + public void StageAllChanges() => Run("add ."); + + string Read(string arguments) => + _executor.ToolRead(ToolGit, arguments, () => _errors.Handle(ToolGit, arguments)); + + void Run(string arguments) => _executor.Tool(ToolGit, arguments); - public void Move(string oldPath, string newPath) => _executor.Git($"mv {oldPath} {newPath}"); - public void RollbackAllChanges() => _executor.Git("reset --hard HEAD", () => { }); - public void StageAllChanges() => _executor.Git("add ."); + void Run(string arguments, Action onNonZeroExitCode) => + _executor.Tool(ToolGit, arguments, onNonZeroExitCode); } } \ No newline at end of file diff --git a/ModernRonin.ProjectRenamer/IExecutor.cs b/ModernRonin.ProjectRenamer/IExecutor.cs index 70831d3..7e22dcd 100644 --- a/ModernRonin.ProjectRenamer/IExecutor.cs +++ b/ModernRonin.ProjectRenamer/IExecutor.cs @@ -7,8 +7,8 @@ public interface IExecutor void DotNet(string arguments); void DotNet(string arguments, Action onNonZeroExitCode); string DotNetRead(string arguments); - void Git(string arguments); - void Git(string arguments, Action onNonZeroExitCode); - string GitRead(string arguments); + void Tool(string tool, string arguments, Action onNonZeroExitCode); + void Tool(string tool, string arguments); + string ToolRead(string tool, string arguments, Action onNonZeroExitCode); } } \ No newline at end of file diff --git a/ModernRonin.ProjectRenamer/Program.cs b/ModernRonin.ProjectRenamer/Program.cs index 40e1695..668a2e3 100644 --- a/ModernRonin.ProjectRenamer/Program.cs +++ b/ModernRonin.ProjectRenamer/Program.cs @@ -16,11 +16,6 @@ static void Main(string[] args) .Run(); } - void StandardErrorHandler(string tool, string arguments) - { - Error($"call '{tool} {arguments}' failed - aborting", true); - } - static IContainer WireUp() { var builder = new ContainerBuilder(); From 517f0372e08b40d9b71d5f403e21c965cb1b6391 Mon Sep 17 00:00:00 2001 From: Mark Hajek Date: Mon, 5 Apr 2021 15:58:36 +0100 Subject: [PATCH 04/11] extracted IDotnet --- ModernRonin.ProjectRenamer/Application.cs | 42 ++++++------------ ModernRonin.ProjectRenamer/Dotnet.cs | 54 +++++++++++++++++++++++ ModernRonin.ProjectRenamer/Executor.cs | 9 ---- ModernRonin.ProjectRenamer/IDotnet.cs | 17 +++++++ ModernRonin.ProjectRenamer/IExecutor.cs | 3 -- ModernRonin.ProjectRenamer/Program.cs | 1 + 6 files changed, 86 insertions(+), 40 deletions(-) create mode 100644 ModernRonin.ProjectRenamer/Dotnet.cs create mode 100644 ModernRonin.ProjectRenamer/IDotnet.cs diff --git a/ModernRonin.ProjectRenamer/Application.cs b/ModernRonin.ProjectRenamer/Application.cs index 8d7dd7f..02e3d5f 100644 --- a/ModernRonin.ProjectRenamer/Application.cs +++ b/ModernRonin.ProjectRenamer/Application.cs @@ -10,6 +10,7 @@ namespace ModernRonin.ProjectRenamer public class Application { readonly Configuration _configuration; + readonly IDotnet _dotnet; readonly IErrorHandler _errors; readonly IExecutor _executor; readonly IGit _git; @@ -25,7 +26,8 @@ public Application(Configuration configuration, ILogger logger, IInput input, IGit git, - IErrorHandler errors) + IErrorHandler errors, + IDotnet dotnet) { _configuration = configuration; _solutionPath = solutionPath; @@ -35,6 +37,7 @@ public Application(Configuration configuration, _input = input; _git = git; _errors = errors; + _dotnet = dotnet; } public void Run() @@ -89,26 +92,16 @@ public void Run() void addNewReferences() { - dependents.ForEach(p => addReference(p, newProjectPath)); - dependencies.ForEach(d => addReference(newProjectPath, d)); - - void addReference(string project, string reference) => - projectReferenceCommand("add", project, reference); + dependents.ForEach(p => _dotnet.AddReference(p, newProjectPath)); + dependencies.ForEach(d => _dotnet.AddReference(newProjectPath, d)); } void removeOldReferences() { - dependents.ForEach(p => removeReference(p, oldProjectPath)); - dependencies.ForEach(d => removeReference(oldProjectPath, d)); - - void removeReference(string project, string reference) => - projectReferenceCommand("remove", project, reference); + dependents.ForEach(p => _dotnet.RemoveReference(p, oldProjectPath)); + dependencies.ForEach(d => _dotnet.RemoveReference(oldProjectPath, d)); } - void projectReferenceCommand(string command, string project, string reference) => - _executor.DotNet( - $"{command} {project.EscapeForShell()} reference {reference.EscapeForShell()}"); - (string[] dependents, string[] dependencies) analyzeReferences() { _logger.Info( @@ -127,10 +120,8 @@ bool hasReferenceToOldProject(string p) => IEnumerable getReferencedProjects(string project) { + var relativeReferences = _dotnet.GetReferencedProjects(project); var baseDirectory = Path.GetFullPath(Path.GetDirectoryName(project)); - var relativeReferences = _executor.DotNetRead($"list {project.EscapeForShell()} reference") - .Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries) - .Skip(2); return relativeReferences.Select(r => r.ToAbsolutePath(baseDirectory)); } @@ -152,7 +143,7 @@ void build() { if (_configuration.DoRunBuild) { - _executor.DotNet("build", () => + _dotnet.BuildSolution(() => { if (_input.AskUser( "dotnet build returned an error or warning - do you want to rollback all changes?") @@ -167,7 +158,7 @@ void build() void updatePaket() { - if (isPaketUsed && !_configuration.DontRunPaketInstall) _executor.DotNet("paket install"); + if (isPaketUsed && !_configuration.DontRunPaketInstall) _dotnet.PaketInstall(); } void updatePaketReference() @@ -202,16 +193,11 @@ void gitMove() void addToSolution() { - var solutionFolderArgument = string.IsNullOrWhiteSpace(solutionFolderPath) - ? string.Empty - : $"-s \"{solutionFolderPath}\""; - solutionCommand($"add {solutionFolderArgument}", newProjectPath); + if (string.IsNullOrWhiteSpace(solutionFolderPath)) _dotnet.AddToSolution(newProjectPath); + else _dotnet.AddToSolution(newProjectPath, solutionFolderPath); } - void removeFromSolution() => solutionCommand("remove", oldProjectPath); - - void solutionCommand(string command, string projectPath) => - _executor.DotNet($"sln {command} {projectPath.EscapeForShell()}"); + void removeFromSolution() => _dotnet.RemoveFromSolution(oldProjectPath); (bool wasFound, string projectPath, string solutionFolder) findProject() { diff --git a/ModernRonin.ProjectRenamer/Dotnet.cs b/ModernRonin.ProjectRenamer/Dotnet.cs new file mode 100644 index 0000000..a433418 --- /dev/null +++ b/ModernRonin.ProjectRenamer/Dotnet.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace ModernRonin.ProjectRenamer +{ + public class Dotnet : IDotnet + { + const string ToolDotnet = "dotnet"; + readonly IErrorHandler _errors; + readonly IExecutor _executor; + + public Dotnet(IExecutor executor, IErrorHandler errors) + { + _executor = executor; + _errors = errors; + } + + public void AddReference(string project, string reference) => + ProjectReferenceCommand("add", project, reference); + + public void AddToSolution(string pathToProject, string solutionFolder) => + SolutionCommand($"add -s {solutionFolder.EscapeForShell()}", pathToProject); + + public void AddToSolution(string pathToProject) => SolutionCommand("add", pathToProject); + + public void BuildSolution(Action onNonZeroExitCode) => Run("build", onNonZeroExitCode); + + public IEnumerable GetReferencedProjects(string project) => + Read($"list {project.EscapeForShell()} reference") + .Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries) + .Skip(2); + + public void PaketInstall() => Run("paket install"); + public void RemoveFromSolution(string pathToProject) => SolutionCommand("remove", pathToProject); + + public void RemoveReference(string project, string reference) => + ProjectReferenceCommand("remove", project, reference); + + void ProjectReferenceCommand(string command, string project, string reference) => + Run($"{command} {project.EscapeForShell()} reference {reference.EscapeForShell()}"); + + string Read(string arguments) => + _executor.ToolRead(ToolDotnet, arguments, () => _errors.Handle(ToolDotnet, arguments)); + + void Run(string arguments) => _executor.Tool(ToolDotnet, arguments); + + void Run(string arguments, Action onNonZeroExitCode) => + _executor.Tool(ToolDotnet, arguments, onNonZeroExitCode); + + void SolutionCommand(string command, string projectPath) => + Run($"sln {command} {projectPath.EscapeForShell()}"); + } +} \ No newline at end of file diff --git a/ModernRonin.ProjectRenamer/Executor.cs b/ModernRonin.ProjectRenamer/Executor.cs index e26dac7..ac73692 100644 --- a/ModernRonin.ProjectRenamer/Executor.cs +++ b/ModernRonin.ProjectRenamer/Executor.cs @@ -4,7 +4,6 @@ namespace ModernRonin.ProjectRenamer { public class Executor : IExecutor { - const string ToolDotnet = "dotnet"; readonly IErrorHandler _errors; readonly IRuntime _runtime; @@ -14,14 +13,6 @@ public Executor(IRuntime runtime, IErrorHandler errors) _errors = errors; } - public void DotNet(string arguments) => Tool(ToolDotnet, arguments); - - public void DotNet(string arguments, Action onNonZeroExitCode) => - Tool(ToolDotnet, arguments, onNonZeroExitCode); - - public string DotNetRead(string arguments) => - ToolRead(ToolDotnet, arguments, () => _errors.Handle(ToolDotnet, arguments)); - public void Tool(string tool, string arguments, Action onNonZeroExitCode) { _runtime.DoWithTool(tool, arguments, onNonZeroExitCode, psi => psi.RedirectStandardOutput = false, diff --git a/ModernRonin.ProjectRenamer/IDotnet.cs b/ModernRonin.ProjectRenamer/IDotnet.cs new file mode 100644 index 0000000..dc1ece3 --- /dev/null +++ b/ModernRonin.ProjectRenamer/IDotnet.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; + +namespace ModernRonin.ProjectRenamer +{ + public interface IDotnet + { + void AddReference(string project, string reference); + void AddToSolution(string pathToProject, string solutionFolder); + void AddToSolution(string pathToProject); + void BuildSolution(Action onNonZeroExitCode); + IEnumerable GetReferencedProjects(string project); + void PaketInstall(); + void RemoveFromSolution(string pathToProject); + void RemoveReference(string project, string reference); + } +} \ No newline at end of file diff --git a/ModernRonin.ProjectRenamer/IExecutor.cs b/ModernRonin.ProjectRenamer/IExecutor.cs index 7e22dcd..51f056e 100644 --- a/ModernRonin.ProjectRenamer/IExecutor.cs +++ b/ModernRonin.ProjectRenamer/IExecutor.cs @@ -4,9 +4,6 @@ namespace ModernRonin.ProjectRenamer { public interface IExecutor { - void DotNet(string arguments); - void DotNet(string arguments, Action onNonZeroExitCode); - string DotNetRead(string arguments); void Tool(string tool, string arguments, Action onNonZeroExitCode); void Tool(string tool, string arguments); string ToolRead(string tool, string arguments, Action onNonZeroExitCode); diff --git a/ModernRonin.ProjectRenamer/Program.cs b/ModernRonin.ProjectRenamer/Program.cs index 668a2e3..a7fd444 100644 --- a/ModernRonin.ProjectRenamer/Program.cs +++ b/ModernRonin.ProjectRenamer/Program.cs @@ -26,6 +26,7 @@ static IContainer WireUp() builder.RegisterType().AsImplementedInterfaces(); builder.RegisterType().AsImplementedInterfaces(); builder.RegisterType().AsImplementedInterfaces(); + builder.RegisterType().AsImplementedInterfaces(); builder.RegisterType().AsSelf(); return builder.Build(); From 844e1938c0765a6ec82df84b7df7406861b0fc49 Mon Sep 17 00:00:00 2001 From: Mark Hajek Date: Mon, 5 Apr 2021 16:10:48 +0100 Subject: [PATCH 05/11] extracted module so we can unit-test --- ModernRonin.ProjectRenamer/Program.cs | 11 +---------- ModernRonin.ProjectRenamer/RenamerModule.cs | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 10 deletions(-) create mode 100644 ModernRonin.ProjectRenamer/RenamerModule.cs diff --git a/ModernRonin.ProjectRenamer/Program.cs b/ModernRonin.ProjectRenamer/Program.cs index a7fd444..05990f1 100644 --- a/ModernRonin.ProjectRenamer/Program.cs +++ b/ModernRonin.ProjectRenamer/Program.cs @@ -19,16 +19,7 @@ static void Main(string[] args) static IContainer WireUp() { var builder = new ContainerBuilder(); - - builder.RegisterType().AsImplementedInterfaces(); - builder.RegisterType().AsImplementedInterfaces(); - builder.RegisterType().AsImplementedInterfaces(); - builder.RegisterType().AsImplementedInterfaces(); - builder.RegisterType().AsImplementedInterfaces(); - builder.RegisterType().AsImplementedInterfaces(); - builder.RegisterType().AsImplementedInterfaces(); - builder.RegisterType().AsSelf(); - + builder.RegisterModule(); return builder.Build(); } } diff --git a/ModernRonin.ProjectRenamer/RenamerModule.cs b/ModernRonin.ProjectRenamer/RenamerModule.cs new file mode 100644 index 0000000..be90c1a --- /dev/null +++ b/ModernRonin.ProjectRenamer/RenamerModule.cs @@ -0,0 +1,19 @@ +using Autofac; + +namespace ModernRonin.ProjectRenamer +{ + public class RenamerModule : Module + { + protected override void Load(ContainerBuilder builder) + { + builder.RegisterType().AsImplementedInterfaces(); + builder.RegisterType().AsImplementedInterfaces(); + builder.RegisterType().AsImplementedInterfaces(); + builder.RegisterType().AsImplementedInterfaces(); + builder.RegisterType().AsImplementedInterfaces(); + builder.RegisterType().AsImplementedInterfaces(); + builder.RegisterType().AsImplementedInterfaces(); + builder.RegisterType().AsSelf(); + } + } +} \ No newline at end of file From 7a715d46f1f7f1d1c477b98b7564afc48746c446 Mon Sep 17 00:00:00 2001 From: Mark Hajek Date: Mon, 5 Apr 2021 16:14:01 +0100 Subject: [PATCH 06/11] added tests for RenamerModule --- .../RenamerModuleTests.cs | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 ModernRonin.ProjectRenamer.Tests/RenamerModuleTests.cs diff --git a/ModernRonin.ProjectRenamer.Tests/RenamerModuleTests.cs b/ModernRonin.ProjectRenamer.Tests/RenamerModuleTests.cs new file mode 100644 index 0000000..a08b330 --- /dev/null +++ b/ModernRonin.ProjectRenamer.Tests/RenamerModuleTests.cs @@ -0,0 +1,35 @@ +using System; +using Autofac; +using FluentAssertions; +using NUnit.Framework; + +namespace ModernRonin.ProjectRenamer.Tests +{ + [TestFixture] + public class RenamerModuleTests + { + [SetUp] + public void Setup() + { + var builder = new ContainerBuilder(); + builder.RegisterModule(); + _container = builder.Build(); + } + + IContainer _container; + + [Test] + public void Application_factory_can_be_resolved() + { + var factory = _container.Resolve>(); + factory.Should().NotBeNull(); + factory(new Configuration(), string.Empty).Should().NotBeNull(); + } + + [Test] + public void IConfigurationSetup_can_be_resolved() + { + _container.Resolve().Should().NotBeNull(); + } + } +} \ No newline at end of file From 2a66037c8951f8fe48eb3376037a62f4eed015a3 Mon Sep 17 00:00:00 2001 From: Mark Hajek Date: Mon, 5 Apr 2021 16:15:52 +0100 Subject: [PATCH 07/11] Application no longer depends on IExecutor --- ModernRonin.ProjectRenamer/Application.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/ModernRonin.ProjectRenamer/Application.cs b/ModernRonin.ProjectRenamer/Application.cs index 02e3d5f..ea824a1 100644 --- a/ModernRonin.ProjectRenamer/Application.cs +++ b/ModernRonin.ProjectRenamer/Application.cs @@ -12,7 +12,6 @@ public class Application readonly Configuration _configuration; readonly IDotnet _dotnet; readonly IErrorHandler _errors; - readonly IExecutor _executor; readonly IGit _git; readonly IInput _input; readonly ILogger _logger; @@ -21,7 +20,6 @@ public class Application public Application(Configuration configuration, string solutionPath, - IExecutor executor, IRuntime runtime, ILogger logger, IInput input, @@ -31,7 +29,6 @@ public Application(Configuration configuration, { _configuration = configuration; _solutionPath = solutionPath; - _executor = executor; _runtime = runtime; _logger = logger; _input = input; From 262601c915729df6b5b199de57d630c468f7f651 Mon Sep 17 00:00:00 2001 From: Mark Hajek Date: Mon, 5 Apr 2021 16:28:20 +0100 Subject: [PATCH 08/11] exttracted IFilesystem --- ModernRonin.ProjectRenamer/Application.cs | 17 +++++++------ .../ConfigurationSetup.cs | 12 ++++++---- ModernRonin.ProjectRenamer/Constants.cs | 1 + ModernRonin.ProjectRenamer/Filesystem.cs | 24 +++++++++++++++++++ ModernRonin.ProjectRenamer/IFilesystem.cs | 11 +++++++++ ModernRonin.ProjectRenamer/RenamerModule.cs | 1 + 6 files changed, 52 insertions(+), 14 deletions(-) create mode 100644 ModernRonin.ProjectRenamer/Filesystem.cs create mode 100644 ModernRonin.ProjectRenamer/IFilesystem.cs diff --git a/ModernRonin.ProjectRenamer/Application.cs b/ModernRonin.ProjectRenamer/Application.cs index ea824a1..9afdf9f 100644 --- a/ModernRonin.ProjectRenamer/Application.cs +++ b/ModernRonin.ProjectRenamer/Application.cs @@ -12,6 +12,7 @@ public class Application readonly Configuration _configuration; readonly IDotnet _dotnet; readonly IErrorHandler _errors; + readonly IFilesystem _filesystem; readonly IGit _git; readonly IInput _input; readonly ILogger _logger; @@ -25,7 +26,8 @@ public Application(Configuration configuration, IInput input, IGit git, IErrorHandler errors, - IDotnet dotnet) + IDotnet dotnet, + IFilesystem filesystem) { _configuration = configuration; _solutionPath = solutionPath; @@ -35,6 +37,7 @@ public Application(Configuration configuration, _git = git; _errors = errors; _dotnet = dotnet; + _filesystem = filesystem; } public void Run() @@ -51,7 +54,7 @@ public void Run() var newDir = _configuration.NewProjectName.ToAbsolutePath(newBase); var newFileName = Path.GetFileName(_configuration.NewProjectName); var newProjectPath = Path.Combine(newDir, $"{newFileName}{Constants.ProjectFileExtension}"); - var isPaketUsed = Directory.Exists(".paket"); + var isPaketUsed = _filesystem.DoesDirectoryExist(".paket"); var gitVersion = _git.GetVersion(); if (!_configuration.DontReviewSettings) { @@ -182,7 +185,7 @@ bool isPaketReference(string line) void gitMove() { - Directory.CreateDirectory(Path.GetDirectoryName(newDir)); + _filesystem.EnsureDirectoryExists(Path.GetDirectoryName(newDir)); _git.Move(oldDir, newDir); var oldPath = Path.GetFileName(oldProjectPath).ToAbsolutePath(newDir); if (oldPath != newProjectPath) _git.Move(oldPath, newProjectPath); @@ -228,14 +231,10 @@ string[] allProjects() return all.Except(excluded).ToArray(); - string[] filesIn(string directory) => - Directory - .EnumerateFiles(directory, $"*{Constants.ProjectFileExtension}", - SearchOption.AllDirectories) - .ToArray(); + string[] filesIn(string directory) => _filesystem.FindProjectFiles(directory, true); } } - static string CurrentDirectoryAbsolute => Path.GetFullPath(Directory.GetCurrentDirectory()); + string CurrentDirectoryAbsolute => Path.GetFullPath(_filesystem.CurrentDirectory); } } \ No newline at end of file diff --git a/ModernRonin.ProjectRenamer/ConfigurationSetup.cs b/ModernRonin.ProjectRenamer/ConfigurationSetup.cs index 858346f..19d6ed6 100644 --- a/ModernRonin.ProjectRenamer/ConfigurationSetup.cs +++ b/ModernRonin.ProjectRenamer/ConfigurationSetup.cs @@ -11,21 +11,23 @@ public class ConfigurationSetup : IConfigurationSetup { readonly ILogger _console; readonly IErrorHandler _errors; - readonly IExecutor _executor; + readonly IFilesystem _filesystem; readonly IRuntime _runtime; - public ConfigurationSetup(IExecutor executor, ILogger console, IRuntime runtime, IErrorHandler errors) + public ConfigurationSetup(ILogger console, + IRuntime runtime, + IErrorHandler errors, + IFilesystem filesystem) { - _executor = executor; _console = console; _runtime = runtime; _errors = errors; + _filesystem = filesystem; } public (Configuration configuration, string solutionPath) Get(string[] commandLineArguments) { - var solutionFiles = - Directory.EnumerateFiles(".", "*.sln", SearchOption.TopDirectoryOnly).ToArray(); + var solutionFiles = _filesystem.FindSolutionFiles(".", false); if (1 != solutionFiles.Length) _errors.Handle("Needs to be run from a directory with exactly one *.sln file in it."); var solutionPath = Path.GetFullPath(solutionFiles.First()); diff --git a/ModernRonin.ProjectRenamer/Constants.cs b/ModernRonin.ProjectRenamer/Constants.cs index eed3b95..cf546ee 100644 --- a/ModernRonin.ProjectRenamer/Constants.cs +++ b/ModernRonin.ProjectRenamer/Constants.cs @@ -3,5 +3,6 @@ public static class Constants { public const string ProjectFileExtension = ".csproj"; + public const string SolutionFileExtension = ".sln"; } } \ No newline at end of file diff --git a/ModernRonin.ProjectRenamer/Filesystem.cs b/ModernRonin.ProjectRenamer/Filesystem.cs new file mode 100644 index 0000000..c4f1bf8 --- /dev/null +++ b/ModernRonin.ProjectRenamer/Filesystem.cs @@ -0,0 +1,24 @@ +using System.IO; +using System.Linq; + +namespace ModernRonin.ProjectRenamer +{ + public class Filesystem : IFilesystem + { + public string CurrentDirectory => Directory.GetCurrentDirectory(); + public bool DoesDirectoryExist(string directory) => Directory.Exists(directory); + public void EnsureDirectoryExists(string directory) => Directory.CreateDirectory(directory); + + public string[] FindProjectFiles(string directory, bool doRecurse) => + Directory + .EnumerateFiles(directory, $"*{Constants.ProjectFileExtension}", SearchOption(doRecurse)) + .ToArray(); + + public string[] FindSolutionFiles(string directory, bool doRecurse) => + Directory.EnumerateFiles(".", $"*{Constants.SolutionFileExtension}", SearchOption(doRecurse)) + .ToArray(); + + static SearchOption SearchOption(bool doRecurse) => + doRecurse ? System.IO.SearchOption.AllDirectories : System.IO.SearchOption.TopDirectoryOnly; + } +} \ No newline at end of file diff --git a/ModernRonin.ProjectRenamer/IFilesystem.cs b/ModernRonin.ProjectRenamer/IFilesystem.cs new file mode 100644 index 0000000..f1d9445 --- /dev/null +++ b/ModernRonin.ProjectRenamer/IFilesystem.cs @@ -0,0 +1,11 @@ +namespace ModernRonin.ProjectRenamer +{ + public interface IFilesystem + { + string CurrentDirectory { get; } + bool DoesDirectoryExist(string directory); + void EnsureDirectoryExists(string directory); + string[] FindProjectFiles(string directory, bool doRecurse); + string[] FindSolutionFiles(string directory, bool doRecurse); + } +} \ No newline at end of file diff --git a/ModernRonin.ProjectRenamer/RenamerModule.cs b/ModernRonin.ProjectRenamer/RenamerModule.cs index be90c1a..c60e9fc 100644 --- a/ModernRonin.ProjectRenamer/RenamerModule.cs +++ b/ModernRonin.ProjectRenamer/RenamerModule.cs @@ -13,6 +13,7 @@ protected override void Load(ContainerBuilder builder) builder.RegisterType().AsImplementedInterfaces(); builder.RegisterType().AsImplementedInterfaces(); builder.RegisterType().AsImplementedInterfaces(); + builder.RegisterType().AsImplementedInterfaces(); builder.RegisterType().AsSelf(); } } From 25959fc8c83770e2cc82f8777ab7b3ebf25db705 Mon Sep 17 00:00:00 2001 From: Mark Hajek Date: Mon, 5 Apr 2021 16:28:20 +0100 Subject: [PATCH 09/11] exttracted IFilesystem --- ModernRonin.ProjectRenamer/Application.cs | 17 +++++++------ .../ConfigurationSetup.cs | 12 ++++++---- ModernRonin.ProjectRenamer/Constants.cs | 1 + ModernRonin.ProjectRenamer/Filesystem.cs | 24 +++++++++++++++++++ ModernRonin.ProjectRenamer/IFilesystem.cs | 11 +++++++++ ModernRonin.ProjectRenamer/RenamerModule.cs | 1 + 6 files changed, 52 insertions(+), 14 deletions(-) create mode 100644 ModernRonin.ProjectRenamer/Filesystem.cs create mode 100644 ModernRonin.ProjectRenamer/IFilesystem.cs diff --git a/ModernRonin.ProjectRenamer/Application.cs b/ModernRonin.ProjectRenamer/Application.cs index ea824a1..9afdf9f 100644 --- a/ModernRonin.ProjectRenamer/Application.cs +++ b/ModernRonin.ProjectRenamer/Application.cs @@ -12,6 +12,7 @@ public class Application readonly Configuration _configuration; readonly IDotnet _dotnet; readonly IErrorHandler _errors; + readonly IFilesystem _filesystem; readonly IGit _git; readonly IInput _input; readonly ILogger _logger; @@ -25,7 +26,8 @@ public Application(Configuration configuration, IInput input, IGit git, IErrorHandler errors, - IDotnet dotnet) + IDotnet dotnet, + IFilesystem filesystem) { _configuration = configuration; _solutionPath = solutionPath; @@ -35,6 +37,7 @@ public Application(Configuration configuration, _git = git; _errors = errors; _dotnet = dotnet; + _filesystem = filesystem; } public void Run() @@ -51,7 +54,7 @@ public void Run() var newDir = _configuration.NewProjectName.ToAbsolutePath(newBase); var newFileName = Path.GetFileName(_configuration.NewProjectName); var newProjectPath = Path.Combine(newDir, $"{newFileName}{Constants.ProjectFileExtension}"); - var isPaketUsed = Directory.Exists(".paket"); + var isPaketUsed = _filesystem.DoesDirectoryExist(".paket"); var gitVersion = _git.GetVersion(); if (!_configuration.DontReviewSettings) { @@ -182,7 +185,7 @@ bool isPaketReference(string line) void gitMove() { - Directory.CreateDirectory(Path.GetDirectoryName(newDir)); + _filesystem.EnsureDirectoryExists(Path.GetDirectoryName(newDir)); _git.Move(oldDir, newDir); var oldPath = Path.GetFileName(oldProjectPath).ToAbsolutePath(newDir); if (oldPath != newProjectPath) _git.Move(oldPath, newProjectPath); @@ -228,14 +231,10 @@ string[] allProjects() return all.Except(excluded).ToArray(); - string[] filesIn(string directory) => - Directory - .EnumerateFiles(directory, $"*{Constants.ProjectFileExtension}", - SearchOption.AllDirectories) - .ToArray(); + string[] filesIn(string directory) => _filesystem.FindProjectFiles(directory, true); } } - static string CurrentDirectoryAbsolute => Path.GetFullPath(Directory.GetCurrentDirectory()); + string CurrentDirectoryAbsolute => Path.GetFullPath(_filesystem.CurrentDirectory); } } \ No newline at end of file diff --git a/ModernRonin.ProjectRenamer/ConfigurationSetup.cs b/ModernRonin.ProjectRenamer/ConfigurationSetup.cs index 858346f..19d6ed6 100644 --- a/ModernRonin.ProjectRenamer/ConfigurationSetup.cs +++ b/ModernRonin.ProjectRenamer/ConfigurationSetup.cs @@ -11,21 +11,23 @@ public class ConfigurationSetup : IConfigurationSetup { readonly ILogger _console; readonly IErrorHandler _errors; - readonly IExecutor _executor; + readonly IFilesystem _filesystem; readonly IRuntime _runtime; - public ConfigurationSetup(IExecutor executor, ILogger console, IRuntime runtime, IErrorHandler errors) + public ConfigurationSetup(ILogger console, + IRuntime runtime, + IErrorHandler errors, + IFilesystem filesystem) { - _executor = executor; _console = console; _runtime = runtime; _errors = errors; + _filesystem = filesystem; } public (Configuration configuration, string solutionPath) Get(string[] commandLineArguments) { - var solutionFiles = - Directory.EnumerateFiles(".", "*.sln", SearchOption.TopDirectoryOnly).ToArray(); + var solutionFiles = _filesystem.FindSolutionFiles(".", false); if (1 != solutionFiles.Length) _errors.Handle("Needs to be run from a directory with exactly one *.sln file in it."); var solutionPath = Path.GetFullPath(solutionFiles.First()); diff --git a/ModernRonin.ProjectRenamer/Constants.cs b/ModernRonin.ProjectRenamer/Constants.cs index eed3b95..cf546ee 100644 --- a/ModernRonin.ProjectRenamer/Constants.cs +++ b/ModernRonin.ProjectRenamer/Constants.cs @@ -3,5 +3,6 @@ public static class Constants { public const string ProjectFileExtension = ".csproj"; + public const string SolutionFileExtension = ".sln"; } } \ No newline at end of file diff --git a/ModernRonin.ProjectRenamer/Filesystem.cs b/ModernRonin.ProjectRenamer/Filesystem.cs new file mode 100644 index 0000000..c4f1bf8 --- /dev/null +++ b/ModernRonin.ProjectRenamer/Filesystem.cs @@ -0,0 +1,24 @@ +using System.IO; +using System.Linq; + +namespace ModernRonin.ProjectRenamer +{ + public class Filesystem : IFilesystem + { + public string CurrentDirectory => Directory.GetCurrentDirectory(); + public bool DoesDirectoryExist(string directory) => Directory.Exists(directory); + public void EnsureDirectoryExists(string directory) => Directory.CreateDirectory(directory); + + public string[] FindProjectFiles(string directory, bool doRecurse) => + Directory + .EnumerateFiles(directory, $"*{Constants.ProjectFileExtension}", SearchOption(doRecurse)) + .ToArray(); + + public string[] FindSolutionFiles(string directory, bool doRecurse) => + Directory.EnumerateFiles(".", $"*{Constants.SolutionFileExtension}", SearchOption(doRecurse)) + .ToArray(); + + static SearchOption SearchOption(bool doRecurse) => + doRecurse ? System.IO.SearchOption.AllDirectories : System.IO.SearchOption.TopDirectoryOnly; + } +} \ No newline at end of file diff --git a/ModernRonin.ProjectRenamer/IFilesystem.cs b/ModernRonin.ProjectRenamer/IFilesystem.cs new file mode 100644 index 0000000..f1d9445 --- /dev/null +++ b/ModernRonin.ProjectRenamer/IFilesystem.cs @@ -0,0 +1,11 @@ +namespace ModernRonin.ProjectRenamer +{ + public interface IFilesystem + { + string CurrentDirectory { get; } + bool DoesDirectoryExist(string directory); + void EnsureDirectoryExists(string directory); + string[] FindProjectFiles(string directory, bool doRecurse); + string[] FindSolutionFiles(string directory, bool doRecurse); + } +} \ No newline at end of file diff --git a/ModernRonin.ProjectRenamer/RenamerModule.cs b/ModernRonin.ProjectRenamer/RenamerModule.cs index be90c1a..c60e9fc 100644 --- a/ModernRonin.ProjectRenamer/RenamerModule.cs +++ b/ModernRonin.ProjectRenamer/RenamerModule.cs @@ -13,6 +13,7 @@ protected override void Load(ContainerBuilder builder) builder.RegisterType().AsImplementedInterfaces(); builder.RegisterType().AsImplementedInterfaces(); builder.RegisterType().AsImplementedInterfaces(); + builder.RegisterType().AsImplementedInterfaces(); builder.RegisterType().AsSelf(); } } From 3944771fd0e1a99ead3402f87fa84eba9042052d Mon Sep 17 00:00:00 2001 From: Mark Hajek Date: Sat, 10 Apr 2021 15:54:39 +0100 Subject: [PATCH 10/11] fixes #24 --- .../CommonExtensionsTests.cs | 9 ++++++ .../DotnetTests.cs | 31 +++++++++++++++++++ .../CommonExtensions.cs | 3 ++ ModernRonin.ProjectRenamer/Dotnet.cs | 3 +- .../Properties/launchSettings.json | 5 +++ 5 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 ModernRonin.ProjectRenamer.Tests/DotnetTests.cs diff --git a/ModernRonin.ProjectRenamer.Tests/CommonExtensionsTests.cs b/ModernRonin.ProjectRenamer.Tests/CommonExtensionsTests.cs index e7b1ef2..faaf63e 100644 --- a/ModernRonin.ProjectRenamer.Tests/CommonExtensionsTests.cs +++ b/ModernRonin.ProjectRenamer.Tests/CommonExtensionsTests.cs @@ -40,6 +40,15 @@ public void IsDirectorySeparator_returns_true_for_linux_separator() => public void IsDirectorySeparator_returns_true_for_windows_separator() => '\\'.IsDirectorySeparator().Should().BeTrue(); + [Test] + public void + On_Windows_EnsurePlatformDirectorySeparators_replaces_forward_slashes_with_backward_slashes() + { + @"alpha/bravo\charlie/delta".EnsurePlatformDirectorySeparators() + .Should() + .Be(@"alpha\bravo\charlie\delta"); + } + [Test] public void Repeat_returns_the_string_repeated_the_passed_number_of_times() => "alpha".Repeat(3).Should().Be("alphaalphaalpha"); diff --git a/ModernRonin.ProjectRenamer.Tests/DotnetTests.cs b/ModernRonin.ProjectRenamer.Tests/DotnetTests.cs new file mode 100644 index 0000000..2c2abf9 --- /dev/null +++ b/ModernRonin.ProjectRenamer.Tests/DotnetTests.cs @@ -0,0 +1,31 @@ +using AutofacContrib.NSubstitute; +using NSubstitute; +using NUnit.Framework; + +namespace ModernRonin.ProjectRenamer.Tests +{ + [TestFixture] + public class DotnetTests + { + [SetUp] + public void Setup() + { + _dependencies = new AutoSubstitute(); + _underTest = _dependencies.Resolve(); + } + + AutoSubstitute _dependencies; + Dotnet _underTest; + + IExecutor Executor => _dependencies.Resolve(); + + [Test(Description = "fix for https://github.com/ModernRonin/ProjectRenamer/issues/24")] + public void AddToSolution_replaces_forward_slashes_with_backward_slashes_in_solutionFolder() + { + _underTest.AddToSolution("c:/myproject/myproject.csproj", "Features/.shared"); + + Executor.Received() + .Tool("dotnet", @"sln add -s ""Features\.shared"" ""c:/myproject/myproject.csproj"""); + } + } +} \ No newline at end of file diff --git a/ModernRonin.ProjectRenamer/CommonExtensions.cs b/ModernRonin.ProjectRenamer/CommonExtensions.cs index 24a0ad9..8381007 100644 --- a/ModernRonin.ProjectRenamer/CommonExtensions.cs +++ b/ModernRonin.ProjectRenamer/CommonExtensions.cs @@ -8,6 +8,9 @@ public static class CommonExtensions { public static string AsText(this bool self) => self ? "yes" : "no"; + public static string EnsurePlatformDirectorySeparators(this string self) => + self.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); + public static string EscapeForShell(this string self) => $"\"{self}\""; public static bool IsDirectorySeparator(this char self) => diff --git a/ModernRonin.ProjectRenamer/Dotnet.cs b/ModernRonin.ProjectRenamer/Dotnet.cs index a433418..aa86e35 100644 --- a/ModernRonin.ProjectRenamer/Dotnet.cs +++ b/ModernRonin.ProjectRenamer/Dotnet.cs @@ -20,7 +20,8 @@ public void AddReference(string project, string reference) => ProjectReferenceCommand("add", project, reference); public void AddToSolution(string pathToProject, string solutionFolder) => - SolutionCommand($"add -s {solutionFolder.EscapeForShell()}", pathToProject); + SolutionCommand($"add -s {solutionFolder.EnsurePlatformDirectorySeparators().EscapeForShell()}", + pathToProject); public void AddToSolution(string pathToProject) => SolutionCommand("add", pathToProject); diff --git a/ModernRonin.ProjectRenamer/Properties/launchSettings.json b/ModernRonin.ProjectRenamer/Properties/launchSettings.json index 7b78760..f28315b 100644 --- a/ModernRonin.ProjectRenamer/Properties/launchSettings.json +++ b/ModernRonin.ProjectRenamer/Properties/launchSettings.json @@ -20,6 +20,11 @@ "commandLineArgs": "LibraryInSubFolder ProjectInSubFolder --no-commit --no-paket", "workingDirectory": "C:\\Projects\\Github\\ProjectRenamerTestBed" }, + "rename library, in nested solution folder starting with ., no commit": { + "commandName": "Project", + "commandLineArgs": "LibraryInNestedSolutionFolderWithLeadingPeriod Changed --no-commit --no-paket", + "workingDirectory": "C:\\Projects\\Github\\ProjectRenamerTestBed" + }, "move root library to subfolder, no paket": { "commandName": "Project", "commandLineArgs": "LibraryInRoot subfolder/LibraryInRoot --no-paket", From baaa1bebb897107e137e4e0e19d1640b09ba84e4 Mon Sep 17 00:00:00 2001 From: Mark Hajek Date: Sat, 10 Apr 2021 15:59:59 +0100 Subject: [PATCH 11/11] updated release history --- ModernRonin.ProjectRenamer/release.history | 4 +++- README.md | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/ModernRonin.ProjectRenamer/release.history b/ModernRonin.ProjectRenamer/release.history index 260ff1d..96b3e1f 100644 --- a/ModernRonin.ProjectRenamer/release.history +++ b/ModernRonin.ProjectRenamer/release.history @@ -1,7 +1,9 @@  - 2.1.2 + 2.1.3 +2.1.3: +* bugfix: fixed a bug concerning nested solution folders; thanks to @Mike-E-angelo for reporting the bug 2.1.2: * bugfix: fixed another whitespace related scenario; thanks to @sejohnson-at-griffis for reporting the bug 2.1.1: diff --git a/README.md b/README.md index 4ab58bb..2da036f 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,9 @@ dotnet tool update --global ModernRonin.ProjectRenamer When I publish a new version, I always post at [my blog](https://modernronin.github.io/) under the [renameproject tag](https://modernronin.github.io/tags/renameproject/), aside from updating this readme here. ### Release History +2.1.3: +* bugfix: fixed a bug concerning nested solution folders; thanks to @Mike-E-angelo for reporting the bug + 2.1.2: * bugfix: fixed another whitespace related scenario; thanks to [@sejohnson-at-griffis](https://github.com/sejohnson-at-griffis) for reporting the bug