-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #14 from ModernRonin/AddCommandLineSwitches
Add command line switches
- Loading branch information
Showing
13 changed files
with
387 additions
and
209 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
using System; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Text; | ||
using Microsoft.Build.Construction; | ||
using static ModernRonin.ProjectRenamer.Executor; | ||
using static ModernRonin.ProjectRenamer.Runtime; | ||
|
||
namespace ModernRonin.ProjectRenamer | ||
{ | ||
public class Application | ||
{ | ||
readonly Configuration _configuration; | ||
readonly Encoding _projectFileEncoding = Encoding.UTF8; | ||
readonly string _solutionPath; | ||
|
||
public Application(Configuration configuration, string solutionPath) | ||
{ | ||
_configuration = configuration; | ||
_solutionPath = solutionPath; | ||
} | ||
|
||
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) => | ||
Tool("git", arguments, | ||
() => Error("git does not seem to be clean, check git status")); | ||
} | ||
|
||
public void Run() | ||
{ | ||
EnsureGitIsClean(); | ||
|
||
var (wasFound, oldProjectPath, solutionFolderPath) = findProject(); | ||
if (!wasFound) Error($"{_configuration.OldProjectName} cannot be found in the solution"); | ||
|
||
var oldDir = Path.GetDirectoryName(oldProjectPath); | ||
var newDir = Path.Combine(Path.GetDirectoryName(oldDir), _configuration.NewProjectName); | ||
var newProjectPath = | ||
Path.Combine(newDir, $"{_configuration.NewProjectName}{Constants.ProjectFileExtension}"); | ||
var isPaketUsed = Directory.Exists(".paket"); | ||
|
||
if (!_configuration.DontReviewSettings) | ||
{ | ||
var lines = new[] | ||
{ | ||
"Please review the following settings:", | ||
$"Project: {_configuration.OldProjectName}", | ||
$"found at: {oldProjectPath}", | ||
$"Rename to: {_configuration.NewProjectName}", | ||
$"at: {newProjectPath})", | ||
$"Paket in use: {isPaketUsed.AsText()}", | ||
$"Run paket install: {(!_configuration.DontRunPaketInstall).AsText()}", | ||
$"Run build after rename: {_configuration.DoRunBuild.AsText()}", | ||
$"Create automatic commit: {(!_configuration.DontCreateCommit).AsText()}", | ||
"-----------------------------------------------", | ||
"Do you want to continue with the rename operation?" | ||
}; | ||
if (!DoesUserAgree(string.Join(Environment.NewLine, lines))) Abort(); | ||
} | ||
|
||
removeFromSolution(); | ||
gitMove(); | ||
replaceReferences(); | ||
addToSolution(); | ||
updatePaket(); | ||
stageAllChanges(); | ||
build(); | ||
commit(); | ||
|
||
void commit() | ||
{ | ||
if (!_configuration.DontCreateCommit) | ||
{ | ||
var arguments = | ||
$"commit -m \"Renamed {_configuration.OldProjectName} to {_configuration.NewProjectName}\""; | ||
Tool("git", arguments, | ||
() => { Console.Error.WriteLine($"'git {arguments}' failed"); }); | ||
} | ||
} | ||
|
||
void build() | ||
{ | ||
if (_configuration.DoRunBuild) | ||
{ | ||
Tool("dotnet", "build", () => | ||
{ | ||
if (DoesUserAgree( | ||
"dotnet build returned an error or warning - do you want to rollback all changes?") | ||
) | ||
{ | ||
RollbackGit(); | ||
Abort(); | ||
} | ||
}); | ||
} | ||
} | ||
|
||
void stageAllChanges() => Git("add ."); | ||
|
||
void updatePaket() | ||
{ | ||
if (isPaketUsed && !_configuration.DontRunPaketInstall) DotNet("paket install"); | ||
} | ||
|
||
void replaceReferences() | ||
{ | ||
var projectFiles = Directory | ||
.EnumerateFiles(".", $"*{Constants.ProjectFileExtension}", SearchOption.AllDirectories) | ||
.ToList(); | ||
var (oldReference, newReference) = | ||
(searchPattern(_configuration.OldProjectName), | ||
searchPattern(_configuration.NewProjectName)); | ||
projectFiles.ForEach(replaceIn); | ||
|
||
void replaceIn(string projectFile) | ||
{ | ||
var contents = File.ReadAllText(projectFile, _projectFileEncoding); | ||
contents = contents.Replace(oldReference, newReference); | ||
File.WriteAllText(projectFile, contents, _projectFileEncoding); | ||
} | ||
|
||
string searchPattern(string name) => | ||
Path.Combine(name, name) + Constants.ProjectFileExtension; | ||
} | ||
|
||
void gitMove() | ||
{ | ||
Git($"mv {oldDir} {newDir}"); | ||
var oldPath = Path.Combine(newDir, Path.GetFileName(oldProjectPath)); | ||
Git($"mv {oldPath} {newProjectPath}"); | ||
} | ||
|
||
void addToSolution() | ||
{ | ||
var solutionFolderArgument = string.IsNullOrWhiteSpace(solutionFolderPath) | ||
? string.Empty | ||
: $"-s {solutionFolderPath}"; | ||
DotNet($"sln add {solutionFolderArgument} {newProjectPath}"); | ||
} | ||
|
||
void removeFromSolution() => DotNet($"sln remove {oldProjectPath}"); | ||
|
||
(bool wasFound, string projectPath, string solutionFolder) findProject() | ||
{ | ||
var solution = SolutionFile.Parse(_solutionPath); | ||
var project = solution.ProjectsInOrder.FirstOrDefault(p => | ||
p.ProjectName.EndsWith(_configuration.OldProjectName, | ||
StringComparison.InvariantCultureIgnoreCase)); | ||
return project switch | ||
{ | ||
null => (false, null, null), | ||
_ when project.ParentProjectGuid == null => (true, project.AbsolutePath, null), | ||
_ => (true, project.AbsolutePath, | ||
path(solution.ProjectsByGuid[project.ParentProjectGuid])) | ||
}; | ||
|
||
string path(ProjectInSolution p) | ||
{ | ||
if (p.ParentProjectGuid == null) return p.ProjectName; | ||
var parent = solution.ProjectsByGuid[p.ParentProjectGuid]; | ||
var parentPath = path(parent); | ||
return $"{parentPath}/{p.ProjectName}"; | ||
} | ||
} | ||
} | ||
|
||
bool DoesUserAgree(string question) | ||
{ | ||
Console.WriteLine($"{question} [Enter=Yes, any other key=No]"); | ||
var key = Console.ReadKey(); | ||
return key.Key == ConsoleKey.Enter; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
namespace ModernRonin.ProjectRenamer | ||
{ | ||
public static class BooleanExtensions | ||
{ | ||
public static string AsText(this bool self) => self ? "yes" : "no"; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
namespace ModernRonin.ProjectRenamer | ||
{ | ||
public class Configuration | ||
{ | ||
public bool DontRunPaketInstall { get; set; } | ||
public bool DoRunBuild { get; set; } | ||
public bool DontCreateCommit { get; set; } | ||
public bool DontReviewSettings { get; set; } | ||
public string OldProjectName { get; set; } | ||
public string NewProjectName { get; set; } | ||
|
||
public string[] ProjectNames => | ||
new[] | ||
{ | ||
OldProjectName, | ||
NewProjectName | ||
}; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
namespace ModernRonin.ProjectRenamer | ||
{ | ||
public static class Constants | ||
{ | ||
public const string ProjectFileExtension = ".csproj"; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
using System; | ||
using System.ComponentModel; | ||
using System.Diagnostics; | ||
using System.IO; | ||
|
||
namespace ModernRonin.ProjectRenamer | ||
{ | ||
public static class Executor | ||
{ | ||
public static void Git(string arguments) => Tool("git", arguments); | ||
|
||
public static void Tool(string tool, string arguments) => | ||
Tool(tool, arguments, () => Runtime.Error($"call '{tool} {arguments}' failed - aborting", true)); | ||
|
||
public static void Tool(string tool, string arguments, Action onNonZeroExitCode) | ||
{ | ||
var psi = new ProcessStartInfo | ||
{ | ||
FileName = tool, | ||
Arguments = arguments, | ||
UseShellExecute = false, | ||
CreateNoWindow = false, | ||
RedirectStandardOutput = false | ||
}; | ||
try | ||
{ | ||
var process = Process.Start(psi); | ||
process.WaitForExit(); | ||
if (process.ExitCode != 0) onNonZeroExitCode(); | ||
} | ||
catch (Win32Exception) | ||
{ | ||
onProcessStartProblem(); | ||
} | ||
catch (FileNotFoundException) | ||
{ | ||
onProcessStartProblem(); | ||
} | ||
|
||
void onProcessStartProblem() | ||
{ | ||
Console.Error.WriteLine($"{tool} could not be found - make sure it's on your PATH."); | ||
onNonZeroExitCode(); | ||
} | ||
} | ||
|
||
public static void DotNet(string arguments) => Tool("dotnet", arguments); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.