Merge pull request #5106 from jonsequitur/make-migration-backup-folder-peer-of-project

make migration backup folder a sibling of the project root
This commit is contained in:
Jon Sequeira 2016-12-21 12:12:11 -08:00 committed by GitHub
commit 3b0a2f5d20
11 changed files with 323 additions and 114 deletions

View file

@ -73,14 +73,14 @@ namespace Microsoft.DotNet.Tools.Common
return path;
}
public static void EnsureParentDirectory(string filePath)
public static void EnsureParentDirectoryExists(string filePath)
{
string directory = Path.GetDirectoryName(filePath);
EnsureDirectory(directory);
EnsureDirectoryExists(directory);
}
public static void EnsureDirectory(string directoryPath)
public static void EnsureDirectoryExists(string directoryPath)
{
if (!Directory.Exists(directoryPath))
{

View file

@ -25,7 +25,7 @@ namespace Microsoft.DotNet.Internal.ProjectModel.Utilities
return candidate.StartsWith(dir, StringComparison.OrdinalIgnoreCase);
}
public static string EnsureTrailingSlash(string path)
public static string EnsureTrailingSlash(this string path)
{
return EnsureTrailingCharacter(path, Path.DirectorySeparatorChar);
}
@ -51,12 +51,18 @@ namespace Microsoft.DotNet.Internal.ProjectModel.Utilities
return path + trailingCharacter;
}
public static void EnsureParentDirectory(string filePath)
public static void EnsureParentDirectoryExists(string filePath)
{
string directory = Path.GetDirectoryName(filePath);
if (!Directory.Exists(directory))
EnsureDirectoryExists(directory);
}
public static void EnsureDirectoryExists(string directoryPath)
{
if (!Directory.Exists(directoryPath))
{
Directory.CreateDirectory(directory);
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.EnsureDirectoryExists(RootBackupDirectory.FullName);
globalJson.MoveTo(Path.Combine(
ProjectBackupDirectory.Parent.FullName,
globalJson.Name));
}
PathUtility.EnsureDirectoryExists(ProjectBackupDirectory.FullName);
foreach (var file in FilesToMove)
{
file.MoveTo(
Path.Combine(
ProjectBackupDirectory.FullName, file.Name));
}
}
}
}

View file

@ -16,35 +16,47 @@ namespace Microsoft.DotNet.TestFramework
{
public class TestAssetInstance
{
private TestAssetInfo _testAssetInfo;
private DirectoryInfo _root;
public DirectoryInfo Root
{
get
{
return _root;
}
}
public TestAssetInstance(TestAssetInfo testAssetInfo, DirectoryInfo root)
{
_testAssetInfo = testAssetInfo;
_root = root;
if (root.Exists)
if (testAssetInfo == null)
{
root.Delete(recursive: true);
throw new ArgumentException(nameof(testAssetInfo));
}
if (root == null)
{
throw new ArgumentException(nameof(root));
}
root.Create();
TestAssetInfo = testAssetInfo;
Root = root;
MigrationBackupRoot = new DirectoryInfo(Path.Combine(root.Parent.FullName, "backup"));
if (Root.Exists)
{
Root.Delete(recursive: true);
}
Root.Create();
if (MigrationBackupRoot.Exists)
{
MigrationBackupRoot.Delete(recursive: true);
}
}
public DirectoryInfo MigrationBackupRoot { get; }
public DirectoryInfo Root { get; }
public TestAssetInfo TestAssetInfo { get; }
public TestAssetInstance WithSourceFiles()
{
var filesToCopy = _testAssetInfo.GetSourceFiles();
var filesToCopy = TestAssetInfo.GetSourceFiles();
CopyFiles(filesToCopy);
@ -53,7 +65,7 @@ namespace Microsoft.DotNet.TestFramework
public TestAssetInstance WithRestoreFiles()
{
var filesToCopy = _testAssetInfo.GetRestoreFiles();
var filesToCopy = TestAssetInfo.GetRestoreFiles();
CopyFiles(filesToCopy);
@ -62,7 +74,7 @@ namespace Microsoft.DotNet.TestFramework
public TestAssetInstance WithBuildFiles()
{
var filesToCopy = _testAssetInfo.GetBuildFiles();
var filesToCopy = TestAssetInfo.GetBuildFiles();
CopyFiles(filesToCopy);
@ -81,7 +93,7 @@ namespace Microsoft.DotNet.TestFramework
</packageSources>
</configuration>";
content = content.Replace("$fullpath$", nugetCache);
using (var newNuGetConfigStream =
new FileStream(newNuGetConfig.FullName, FileMode.Create, FileAccess.Write))
{
@ -96,13 +108,13 @@ namespace Microsoft.DotNet.TestFramework
{
foreach (var file in filesToCopy)
{
var relativePath = file.FullName.Substring(_testAssetInfo.Root.FullName.Length + 1);
var relativePath = file.FullName.Substring(TestAssetInfo.Root.FullName.Length + 1);
var newPath = Path.Combine(Root.FullName, relativePath);
var newFile = new FileInfo(newPath);
PathUtility.EnsureDirectory(newFile.Directory.FullName);
PathUtility.EnsureDirectoryExists(newFile.Directory.FullName);
file.CopyTo(newPath);
}

View file

@ -20,16 +20,26 @@ namespace Microsoft.DotNet.TestFramework
Path = path;
EnsureExistsAndEmpty(Path);
EnsureDirectoryAndBackupDirectoryExistAndAreEmpty(Path);
}
public string Path { get; private set; }
private static void EnsureExistsAndEmpty(string path)
private static void EnsureDirectoryAndBackupDirectoryExistAndAreEmpty(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);

View file

@ -33,7 +33,7 @@ namespace Microsoft.DotNet.Cli
{
var profileOptimizationRootPath = new MulticoreJitProfilePathCalculator().MulticoreJitProfilePath;
if (!TryEnsureDirectory(profileOptimizationRootPath))
if (!TryEnsureDirectoryExists(profileOptimizationRootPath))
{
return;
}
@ -43,11 +43,11 @@ namespace Microsoft.DotNet.Cli
AssemblyLoadContext.Default.StartProfileOptimization("dotnet");
}
private bool TryEnsureDirectory(string directoryPath)
private bool TryEnsureDirectoryExists(string directoryPath)
{
try
{
PathUtility.EnsureDirectory(directoryPath);
PathUtility.EnsureDirectoryExists(directoryPath);
return true;
}

View file

@ -22,7 +22,6 @@ namespace Microsoft.DotNet.Tools.Migrate
{
private SlnFile _slnFile;
private readonly DirectoryInfo _workspaceDirectory;
private readonly DirectoryInfo _backupDirectory;
private readonly string _templateFile;
private readonly string _projectArg;
private readonly string _sdkVersion;
@ -33,21 +32,20 @@ namespace Microsoft.DotNet.Tools.Migrate
private readonly bool _skipBackup;
public MigrateCommand(
string templateFile,
string projectArg,
string sdkVersion,
string xprojFilePath,
string reportFile,
bool skipProjectReferences,
string templateFile,
string projectArg,
string sdkVersion,
string xprojFilePath,
string reportFile,
bool skipProjectReferences,
bool reportFormatJson,
bool skipBackup)
{
{
_templateFile = templateFile;
_projectArg = projectArg ?? Directory.GetCurrentDirectory();
_workspaceDirectory = File.Exists(_projectArg)
? new FileInfo(_projectArg).Directory
: new DirectoryInfo(_projectArg);
_backupDirectory = new DirectoryInfo(Path.Combine(_workspaceDirectory.FullName, "backup"));
_sdkVersion = sdkVersion;
_xprojFilePath = xprojFilePath;
_skipProjectReferences = skipProjectReferences;
@ -172,60 +170,24 @@ namespace Microsoft.DotNet.Tools.Migrate
{
return;
}
if (migrationReport.FailedProjectsCount > 0)
{
return;
}
BackupGlobalJson();
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)
{
foreach (var report in migrationReport.ProjectMigrationReports)
{
MigrateProject(report);
}
}
var backupPlan = new MigrationBackupPlan(
new DirectoryInfo(report.ProjectDirectory),
_workspaceDirectory);
private void MigrateProject(ProjectMigrationReport report)
{
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));
backupPlan.PerformBackup();
}
}
@ -373,8 +335,8 @@ 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.");
}
foreach(var project in projects)
foreach (var project in projects)
{
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;
}
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));

View file

@ -12,18 +12,47 @@ namespace Microsoft.DotNet.Migration.Tests
public class GivenThatAnAppWasMigrated : TestBase
{
[Theory]
[InlineData("PJTestAppSimple")]
[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
.CreateTestInstance(testProjectName, identifier: testProjectName)
.CreateTestInstance(testProjectName)
.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);
new MigrateCommand()
.WithWorkingDirectory(testRoot)
.Execute()
@ -40,7 +69,7 @@ namespace Microsoft.DotNet.Migration.Tests
[Theory]
[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"))
.CreateTestInstance(testProjectName, identifier: testProjectName)
@ -54,7 +83,7 @@ namespace Microsoft.DotNet.Migration.Tests
.WithWorkingDirectory(testRoot)
.Execute()
.Should().Fail();
new DirectoryInfo(backupRoot).Should().NotExist("Because migration failed and therefore no backup is needed.");
new DirectoryInfo(testRoot).Should().HaveTextFiles(migratableArtifacts, "Because migration failed so nothing was moved to backup.");
@ -62,7 +91,7 @@ namespace Microsoft.DotNet.Migration.Tests
[Theory]
[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;
@ -74,7 +103,7 @@ namespace Microsoft.DotNet.Migration.Tests
.WithWorkingDirectory(testRoot)
.Execute("--skip-backup")
.Should().Pass();
new DirectoryInfo(backupRoot).Should().NotExist("Because --skip-backup was specified.");
new DirectoryInfo(testRoot).Should().HaveTextFiles(migratableArtifacts, "Because --skip-backup was specified.");
@ -83,14 +112,14 @@ namespace Microsoft.DotNet.Migration.Tests
private Dictionary<string, string> GetProjectJsonArtifacts(string rootPath)
{
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)
{
AddArtifactsToCatalog(catalog, rootPath, pattern);
}
return catalog;
}
@ -104,7 +133,8 @@ namespace Microsoft.DotNet.Migration.Tests
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

@ -213,7 +213,9 @@ namespace Microsoft.DotNet.Migration.Tests
public void ItMigratesRootProjectAndReferences(string projectName, string expectedProjects)
{
var projectDirectory =
TestAssetsManager.CreateTestInstance("TestAppDependencyGraph", callingMethod: $"{projectName}.RefsTest").Path;
TestAssetsManager.CreateTestInstance(
"TestAppDependencyGraph",
identifier: $"{projectName}.RefsTest").Path;
MigrateProject(new [] { Path.Combine(projectDirectory, projectName) });
@ -231,7 +233,7 @@ namespace Microsoft.DotNet.Migration.Tests
public void ItMigratesRootProjectAndSkipsReferences(string projectName)
{
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" });
@ -424,7 +426,7 @@ namespace Microsoft.DotNet.Migration.Tests
{
const string projectName = "ProjectA";
var solutionDirectory =
TestAssetsManager.CreateTestInstance("TestAppDependencyGraph", callingMethod: "p").Path;
TestAssetsManager.CreateTestInstance("TestAppDependencyGraph").Path;
var projectDirectory = Path.Combine(solutionDirectory, projectName);
MigrateProject(new string[] { projectDirectory });
@ -474,8 +476,9 @@ namespace Microsoft.DotNet.Migration.Tests
[InlineData("LibraryWithNetStandardLibRef")]
public void ItMigratesAndBuildsLibrary(string projectName)
{
var projectDirectory = TestAssetsManager.CreateTestInstance(projectName,
callingMethod: $"{nameof(ItMigratesAndBuildsLibrary)}-projectName").Path;
var projectDirectory = TestAssetsManager.CreateTestInstance(
projectName,
identifier: $"{projectName}").Path;
MigrateProject(projectDirectory);
Restore(projectDirectory, projectName);
@ -654,6 +657,7 @@ namespace Microsoft.DotNet.Migration.Tests
var result = new BuildPJCommand()
.WithCapturedOutput()
.WithForwardingToConsole()
.Execute(projectFile);
result.Should().Pass();
@ -661,10 +665,11 @@ namespace Microsoft.DotNet.Migration.Tests
private void MigrateProject(params string[] migrateArgs)
{
var result =
MigrateCommand.Run(migrateArgs);
result.Should().Be(0);
new TestCommand("dotnet")
.WithForwardingToConsole()
.Execute($"migrate {string.Join(" ", migrateArgs)}")
.Should()
.Pass();
}
private void RestoreProjectJson(string projectDirectory)