make migration backup folder a sibling of the project root

This commit is contained in:
jonsequitur 2016-12-20 14:19:37 -08:00
parent 7a2c6ad086
commit 620e573f95
8 changed files with 276 additions and 81 deletions

View file

@ -25,7 +25,7 @@ namespace Microsoft.DotNet.Internal.ProjectModel.Utilities
return candidate.StartsWith(dir, StringComparison.OrdinalIgnoreCase); return candidate.StartsWith(dir, StringComparison.OrdinalIgnoreCase);
} }
public static string EnsureTrailingSlash(string path) public static string EnsureTrailingSlash(this string path)
{ {
return EnsureTrailingCharacter(path, Path.DirectorySeparatorChar); return EnsureTrailingCharacter(path, Path.DirectorySeparatorChar);
} }
@ -54,9 +54,15 @@ namespace Microsoft.DotNet.Internal.ProjectModel.Utilities
public static void EnsureParentDirectory(string filePath) public static void EnsureParentDirectory(string filePath)
{ {
string directory = Path.GetDirectoryName(filePath); string directory = Path.GetDirectoryName(filePath);
if (!Directory.Exists(directory))
EnsureDirectory(directory);
}
public static void EnsureDirectory(string directoryPath)
{ {
Directory.CreateDirectory(directory); if (!Directory.Exists(directoryPath))
{
Directory.CreateDirectory(directoryPath);
} }
} }

View file

@ -0,0 +1,90 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.DotNet.Internal.ProjectModel.Utilities;
namespace Microsoft.DotNet.ProjectJsonMigration
{
internal class MigrationBackupPlan
{
private readonly FileInfo globalJson;
public MigrationBackupPlan(
DirectoryInfo projectDirectory,
DirectoryInfo workspaceDirectory,
Func<DirectoryInfo, IEnumerable<FileInfo>> getFiles = null)
{
if (projectDirectory == null)
{
throw new ArgumentNullException(nameof(projectDirectory));
}
if (workspaceDirectory == null)
{
throw new ArgumentNullException(nameof(workspaceDirectory));
}
globalJson = new FileInfo(Path.Combine(
workspaceDirectory.FullName,
"global.json"));
projectDirectory = new DirectoryInfo(projectDirectory.FullName.EnsureTrailingSlash());
workspaceDirectory = new DirectoryInfo(workspaceDirectory.FullName.EnsureTrailingSlash());
RootBackupDirectory = new DirectoryInfo(
Path.Combine(
workspaceDirectory.Parent.FullName,
"backup")
.EnsureTrailingSlash());
ProjectBackupDirectory = new DirectoryInfo(
Path.Combine(
RootBackupDirectory.FullName,
projectDirectory.Name)
.EnsureTrailingSlash());
var relativeDirectory = PathUtility.GetRelativePath(
workspaceDirectory.FullName,
projectDirectory.FullName);
getFiles = getFiles ??
(dir => dir.EnumerateFiles());
FilesToMove = getFiles(projectDirectory)
.Where(f => f.Name == "project.json"
|| f.Extension == ".xproj"
|| f.FullName.EndsWith(".xproj.user")
|| f.FullName.EndsWith(".lock.json"));
}
public DirectoryInfo ProjectBackupDirectory { get; }
public DirectoryInfo RootBackupDirectory { get; }
public IEnumerable<FileInfo> FilesToMove { get; }
public void PerformBackup()
{
if (globalJson.Exists)
{
PathUtility.EnsureDirectory(RootBackupDirectory.FullName);
globalJson.MoveTo(Path.Combine(
ProjectBackupDirectory.Parent.FullName,
globalJson.Name));
}
PathUtility.EnsureDirectory(ProjectBackupDirectory.FullName);
foreach (var file in FilesToMove)
{
file.MoveTo(
Path.Combine(
ProjectBackupDirectory.FullName, file.Name));
}
}
}
}

View file

@ -27,9 +27,19 @@ namespace Microsoft.DotNet.TestFramework
private static void EnsureExistsAndEmpty(string path) private static void EnsureExistsAndEmpty(string path)
{ {
if (Directory.Exists(path)) var testDirectory = new DirectoryInfo(path);
var migrationBackupDirectory = new DirectoryInfo(
System.IO.Path.Combine(testDirectory.Parent.FullName, "backup"));
if (testDirectory.Exists)
{ {
Directory.Delete(path, true); testDirectory.Delete(true);
}
if (migrationBackupDirectory.Exists)
{
migrationBackupDirectory.Delete(true);
} }
Directory.CreateDirectory(path); Directory.CreateDirectory(path);

View file

@ -21,7 +21,6 @@ namespace Microsoft.DotNet.Tools.Migrate
{ {
private SlnFile _slnFile; private SlnFile _slnFile;
private readonly DirectoryInfo _workspaceDirectory; private readonly DirectoryInfo _workspaceDirectory;
private readonly DirectoryInfo _backupDirectory;
private readonly string _templateFile; private readonly string _templateFile;
private readonly string _projectArg; private readonly string _projectArg;
private readonly string _sdkVersion; private readonly string _sdkVersion;
@ -46,7 +45,6 @@ namespace Microsoft.DotNet.Tools.Migrate
_workspaceDirectory = File.Exists(_projectArg) _workspaceDirectory = File.Exists(_projectArg)
? new FileInfo(_projectArg).Directory ? new FileInfo(_projectArg).Directory
: new DirectoryInfo(_projectArg); : new DirectoryInfo(_projectArg);
_backupDirectory = new DirectoryInfo(Path.Combine(_workspaceDirectory.FullName, "backup"));
_sdkVersion = sdkVersion; _sdkVersion = sdkVersion;
_xprojFilePath = xprojFilePath; _xprojFilePath = xprojFilePath;
_skipProjectReferences = skipProjectReferences; _skipProjectReferences = skipProjectReferences;
@ -142,54 +140,18 @@ namespace Microsoft.DotNet.Tools.Migrate
return; return;
} }
BackupGlobalJson();
BackupProjects(migrationReport); BackupProjects(migrationReport);
}
private void BackupGlobalJson()
{
_backupDirectory.Create();
var globalJson = Path.Combine(_workspaceDirectory.FullName, GlobalSettings.FileName);
if (File.Exists(globalJson))
{
File.Move(globalJson, Path.Combine(_backupDirectory.FullName, GlobalSettings.FileName));
}
} }
private void BackupProjects(MigrationReport migrationReport) private void BackupProjects(MigrationReport migrationReport)
{ {
foreach (var report in migrationReport.ProjectMigrationReports) foreach (var report in migrationReport.ProjectMigrationReports)
{ {
MigrateProject(report); var backupPlan = new MigrationBackupPlan(
} new DirectoryInfo(report.ProjectDirectory),
} _workspaceDirectory);
private void MigrateProject(ProjectMigrationReport report) backupPlan.PerformBackup();
{
var projectDirectory = PathUtility.EnsureTrailingSlash(report.ProjectDirectory);
var relativeDirectory = PathUtility.GetRelativePath(PathUtility.EnsureTrailingSlash(_workspaceDirectory.FullName), projectDirectory);
var targetDirectory = String.IsNullOrEmpty(relativeDirectory)
? _backupDirectory.FullName
: Path.Combine(_backupDirectory.FullName, relativeDirectory);
PathUtility.EnsureDirectory(PathUtility.EnsureTrailingSlash(targetDirectory));
var movableFiles = new DirectoryInfo(projectDirectory)
.EnumerateFiles()
.Where(f => f.Name == Project.FileName
|| f.Extension == ".xproj"
|| f.FullName.EndsWith(".xproj.user")
|| f.FullName.EndsWith(".lock.json"));
foreach (var movableFile in movableFiles)
{
movableFile.MoveTo(Path.Combine(targetDirectory, movableFile.Name));
} }
} }
@ -338,7 +300,7 @@ namespace Microsoft.DotNet.Tools.Migrate
throw new Exception($"Invalid project argument - '{projectArg}' is not a project.json, global.json, or solution.sln file and a directory named '{projectArg}' doesn't exist."); throw new Exception($"Invalid project argument - '{projectArg}' is not a project.json, global.json, or solution.sln file and a directory named '{projectArg}' doesn't exist.");
} }
foreach(var project in projects) foreach (var project in projects)
{ {
yield return GetProjectJsonPath(project); yield return GetProjectJsonPath(project);
} }

View file

@ -0,0 +1,94 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using FluentAssertions;
using Microsoft.DotNet.Internal.ProjectModel.Utilities;
using Microsoft.DotNet.ProjectJsonMigration;
using System;
using System.IO;
using System.Linq;
using Xunit;
namespace Microsoft.DotNet.ProjectJsonMigration.Tests
{
public partial class MigrationBackupPlanTests
{
[Fact]
public void TheRootBackupDirectoryIsASiblingOfTheRootProject()
{
var dir = new DirectoryInfo(Path.Combine("src", "some-proj"));
System.Console.WriteLine(dir.FullName);
WhenMigrating(
projectDirectory: dir.FullName,
workspaceDirectory: Path.Combine("src", "RootProject"))
.RootBackupDirectory
.FullName
.Should()
.Be(new DirectoryInfo(Path.Combine("src", "backup")).FullName.EnsureTrailingSlash());
}
[Fact]
public void TheRootProjectsBackupDirectoryIsASubfolderOfTheRootBackupDirectory()
{
WhenMigrating(
projectDirectory: Path.Combine("src", "RootProject"),
workspaceDirectory: Path.Combine("src", "RootProject"))
.ProjectBackupDirectory
.FullName
.Should()
.Be(new DirectoryInfo(Path.Combine("src", "backup", "RootProject")).FullName.EnsureTrailingSlash());
}
[Fact]
public void ADependentProjectsMigrationBackupDirectoryIsASubfolderOfTheRootBackupDirectory()
{
WhenMigrating(
projectDirectory: Path.Combine("src", "Dependency"),
workspaceDirectory: Path.Combine("src", "RootProject"))
.ProjectBackupDirectory
.FullName
.Should()
.Be(new DirectoryInfo(Path.Combine("src", "backup", "Dependency")).FullName.EnsureTrailingSlash());
}
[Fact]
public void FilesToBackUpAreIdentifiedInTheTheRootProjectDirectory()
{
var root = new DirectoryInfo(Path.Combine("src", "RootProject"));
WhenMigrating(
projectDirectory: root.FullName,
workspaceDirectory: root.FullName)
.FilesToMove
.Should()
.Contain(_ => _.FullName == Path.Combine(root.FullName, "project.json"));
}
[Fact]
public void FilesToBackUpAreIdentifiedInTheTheDependencyProjectDirectory()
{
var root = new DirectoryInfo(Path.Combine("src", "RootProject"));
var dependency = new DirectoryInfo(Path.Combine("src", "RootProject"));
WhenMigrating(
projectDirectory: dependency.FullName,
workspaceDirectory: root.FullName)
.FilesToMove
.Should()
.Contain(_ => _.FullName == Path.Combine(dependency.FullName, "project.json"));
}
private MigrationBackupPlan WhenMigrating(
string projectDirectory,
string workspaceDirectory) =>
new MigrationBackupPlan(
new DirectoryInfo(projectDirectory),
new DirectoryInfo(workspaceDirectory),
dir => new[] { new FileInfo(Path.Combine(dir.FullName, "project.json")) });
}
}

View file

@ -48,7 +48,7 @@ namespace Microsoft.DotNet.Tools.Test.Utilities
return subject; return subject;
} }
public static TCommand WithForwardingToConsole<TCommand>(this TCommand subject, Action<string> writeLine) where TCommand : TestCommand public static TCommand WithForwardingToConsole<TCommand>(this TCommand subject) where TCommand : TestCommand
{ {
subject.WithOutputDataReceivedHandler(s => Console.Out.WriteLine(s)); subject.WithOutputDataReceivedHandler(s => Console.Out.WriteLine(s));

View file

@ -12,15 +12,44 @@ namespace Microsoft.DotNet.Migration.Tests
public class GivenThatAnAppWasMigrated : TestBase public class GivenThatAnAppWasMigrated : TestBase
{ {
[Theory] [Theory]
[InlineData("PJTestAppSimple")]
[InlineData("TestAppWithLibrary")] [InlineData("TestAppWithLibrary")]
public void When_migration_succeeds_Then_project_json_artifacts_get_moved_to_backup(string testProjectName) public void WhenProjectMigrationSucceedsThenProjectJsonArtifactsGetMovedToBackup(string testProjectName)
{ {
var testRoot = TestAssetsManager var testRoot = TestAssetsManager
.CreateTestInstance(testProjectName, identifier: testProjectName) .CreateTestInstance(testProjectName)
.Path; .Path;
var backupRoot = Path.Combine(testRoot, "backup"); var testRootParent = new DirectoryInfo(testRoot).Parent.FullName;
var backupRoot = Path.Combine(testRootParent, "backup");
var migratableArtifacts = GetProjectJsonArtifacts(testRoot);
new MigrateCommand()
.WithWorkingDirectory(testRoot)
.Execute()
.Should().Pass();
var backupArtifacts = GetProjectJsonArtifacts(backupRoot);
backupArtifacts.Should().Equal(migratableArtifacts, "Because all of and only these artifacts should have been moved");
new DirectoryInfo(testRoot).Should().NotHaveFiles(backupArtifacts.Keys);
new DirectoryInfo(backupRoot).Should().HaveTextFiles(backupArtifacts);
}
[Theory]
[InlineData("PJTestAppSimple")]
public void WhenFolderMigrationSucceedsThenProjectJsonArtifactsGetMovedToBackup(string testProjectName)
{
var testRoot = TestAssetsManager
.CreateTestInstance(testProjectName)
.Path;
var testRootParent = new DirectoryInfo(testRoot).Parent.FullName;
var backupRoot = Path.Combine(testRootParent, "backup", testProjectName);
var migratableArtifacts = GetProjectJsonArtifacts(testRoot); var migratableArtifacts = GetProjectJsonArtifacts(testRoot);
@ -40,7 +69,7 @@ namespace Microsoft.DotNet.Migration.Tests
[Theory] [Theory]
[InlineData("TestAppWithLibraryAndMissingP2P")] [InlineData("TestAppWithLibraryAndMissingP2P")]
public void When_migration_fails_Then_project_json_artifacts_do_not_get_moved_to_backup(string testProjectName) public void WhenMigrationFailsThenProjectJsonArtifactsDoNotGetMovedToBackup(string testProjectName)
{ {
var testRoot = new TestAssetsManager(Path.Combine(RepoRoot, "TestAssets", "NonRestoredTestProjects")) var testRoot = new TestAssetsManager(Path.Combine(RepoRoot, "TestAssets", "NonRestoredTestProjects"))
.CreateTestInstance(testProjectName, identifier: testProjectName) .CreateTestInstance(testProjectName, identifier: testProjectName)
@ -62,7 +91,7 @@ namespace Microsoft.DotNet.Migration.Tests
[Theory] [Theory]
[InlineData("PJTestAppSimple")] [InlineData("PJTestAppSimple")]
public void When_skipbackup_specified_Then_project_json_artifacts_do_not_get_moved_to_backup(string testProjectName) public void WhenSkipbackupSpecifiedThenProjectJsonArtifactsDoNotGetMovedToBackup(string testProjectName)
{ {
var testRoot = TestAssetsManager.CreateTestInstance(testProjectName, identifier: testProjectName).Path; var testRoot = TestAssetsManager.CreateTestInstance(testProjectName, identifier: testProjectName).Path;
@ -84,7 +113,7 @@ namespace Microsoft.DotNet.Migration.Tests
{ {
var catalog = new Dictionary<string, string>(); var catalog = new Dictionary<string, string>();
var patterns = new [] { "global.json", "project.json", "project.lock.json", "*.xproj", "*.xproj.user" }; var patterns = new[] { "global.json", "project.json", "project.lock.json", "*.xproj", "*.xproj.user" };
foreach (var pattern in patterns) foreach (var pattern in patterns)
{ {
@ -104,7 +133,8 @@ namespace Microsoft.DotNet.Migration.Tests
foreach (var file in files) foreach (var file in files)
{ {
catalog.Add(PathUtility.GetRelativePath(basePath, file.FullName), File.ReadAllText(file.FullName)); var key = PathUtility.GetRelativePath(basePath, file.FullName);
catalog.Add(key, File.ReadAllText(file.FullName));
} }
} }
} }

View file

@ -210,7 +210,9 @@ namespace Microsoft.DotNet.Migration.Tests
public void ItMigratesRootProjectAndReferences(string projectName, string expectedProjects) public void ItMigratesRootProjectAndReferences(string projectName, string expectedProjects)
{ {
var projectDirectory = var projectDirectory =
TestAssetsManager.CreateTestInstance("TestAppDependencyGraph", callingMethod: $"{projectName}.RefsTest").Path; TestAssetsManager.CreateTestInstance(
"TestAppDependencyGraph",
identifier: $"{projectName}.RefsTest").Path;
MigrateProject(new [] { Path.Combine(projectDirectory, projectName) }); MigrateProject(new [] { Path.Combine(projectDirectory, projectName) });
@ -228,7 +230,7 @@ namespace Microsoft.DotNet.Migration.Tests
public void ItMigratesRootProjectAndSkipsReferences(string projectName) public void ItMigratesRootProjectAndSkipsReferences(string projectName)
{ {
var projectDirectory = var projectDirectory =
TestAssetsManager.CreateTestInstance("TestAppDependencyGraph", callingMethod: $"{projectName}.SkipRefsTest").Path; TestAssetsManager.CreateTestInstance("TestAppDependencyGraph", identifier: $"{projectName}.SkipRefsTest").Path;
MigrateProject(new [] { Path.Combine(projectDirectory, projectName), "--skip-project-references" }); MigrateProject(new [] { Path.Combine(projectDirectory, projectName), "--skip-project-references" });
@ -421,7 +423,7 @@ namespace Microsoft.DotNet.Migration.Tests
{ {
const string projectName = "ProjectA"; const string projectName = "ProjectA";
var solutionDirectory = var solutionDirectory =
TestAssetsManager.CreateTestInstance("TestAppDependencyGraph", callingMethod: "p").Path; TestAssetsManager.CreateTestInstance("TestAppDependencyGraph").Path;
var projectDirectory = Path.Combine(solutionDirectory, projectName); var projectDirectory = Path.Combine(solutionDirectory, projectName);
MigrateProject(new string[] { projectDirectory }); MigrateProject(new string[] { projectDirectory });
@ -472,7 +474,7 @@ namespace Microsoft.DotNet.Migration.Tests
public void ItMigratesAndBuildsLibrary(string projectName) public void ItMigratesAndBuildsLibrary(string projectName)
{ {
var projectDirectory = TestAssetsManager.CreateTestInstance(projectName, var projectDirectory = TestAssetsManager.CreateTestInstance(projectName,
callingMethod: $"{nameof(ItMigratesAndBuildsLibrary)}-projectName").Path; identifier: $"{nameof(ItMigratesAndBuildsLibrary)}-{projectName}").Path;
MigrateProject(projectDirectory); MigrateProject(projectDirectory);
Restore(projectDirectory, projectName); Restore(projectDirectory, projectName);
@ -496,8 +498,7 @@ namespace Microsoft.DotNet.Migration.Tests
public void ItMigratesSln() public void ItMigratesSln()
{ {
var rootDirectory = TestAssetsManager.CreateTestInstance( var rootDirectory = TestAssetsManager.CreateTestInstance(
"TestAppWithSlnAndMultipleProjects", "TestAppWithSlnAndMultipleProjects").Path;
callingMethod: "a").Path;
var testAppProjectDirectory = Path.Combine(rootDirectory, "TestApp"); var testAppProjectDirectory = Path.Combine(rootDirectory, "TestApp");
var testLibProjectDirectory = Path.Combine(rootDirectory, "TestLibrary"); var testLibProjectDirectory = Path.Combine(rootDirectory, "TestLibrary");
@ -670,6 +671,7 @@ namespace Microsoft.DotNet.Migration.Tests
var result = new BuildPJCommand() var result = new BuildPJCommand()
.WithCapturedOutput() .WithCapturedOutput()
.WithForwardingToConsole()
.Execute(projectFile); .Execute(projectFile);
result.Should().Pass(); result.Should().Pass();
@ -677,10 +679,11 @@ namespace Microsoft.DotNet.Migration.Tests
private void MigrateProject(params string[] migrateArgs) private void MigrateProject(params string[] migrateArgs)
{ {
var result = new TestCommand("dotnet")
MigrateCommand.Run(migrateArgs); .WithForwardingToConsole()
.Execute($"migrate {string.Join(" ", migrateArgs)}")
result.Should().Be(0); .Should()
.Pass();
} }
private void RestoreProjectJson(string projectDirectory) private void RestoreProjectJson(string projectDirectory)