dotnet-migrate built in command for cli

This commit is contained in:
Bryan Thornbury 2016-08-22 12:21:52 -07:00
parent 46818ff3fa
commit 611e4ccfde
8 changed files with 311 additions and 1 deletions

View file

@ -21,6 +21,7 @@ using Microsoft.DotNet.Tools.Restore;
using Microsoft.DotNet.Tools.Restore3;
using Microsoft.DotNet.Tools.Run;
using Microsoft.DotNet.Tools.Test;
using Microsoft.DotNet.Tools.Migrate;
using NuGet.Frameworks;
namespace Microsoft.DotNet.Cli
@ -42,6 +43,7 @@ namespace Microsoft.DotNet.Cli
["run3"] = Run3Command.Run,
["restore3"] = Restore3Command.Run,
["pack3"] = Pack3Command.Run,
["migrate"] = MigrateCommand.Run
};
public static int Main(string[] args)

View file

@ -3,3 +3,4 @@ using System.Runtime.CompilerServices;
[assembly: AssemblyMetadataAttribute("Serviceable", "True")]
[assembly: InternalsVisibleTo("dotnet.Tests")]
[assembly: InternalsVisibleTo("Microsoft.DotNet.ProjectJsonMigration.Tests")]

View file

@ -15,6 +15,5 @@ namespace Microsoft.DotNet.Cli
{
return new MSBuildForwardingApp(args).Execute();
}
}
}

View file

@ -0,0 +1,84 @@
// 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.IO;
using Microsoft.Build.Construction;
using Microsoft.DotNet.Cli;
using Microsoft.DotNet.ProjectJsonMigration;
namespace Microsoft.DotNet.Tools.Migrate
{
public partial class MigrateCommand
{
private string _templateFile;
private string _outputDirectory;
private string _projectJson;
private string _sdkVersion;
private TemporaryDotnetNewTemplateProject _temporaryDotnetNewProject;
public MigrateCommand(string templateFile, string outputDirectory, string projectJson, string sdkVersion)
{
_templateFile = templateFile;
_outputDirectory = outputDirectory;
_projectJson = projectJson;
_sdkVersion = sdkVersion;
_temporaryDotnetNewProject = new TemporaryDotnetNewTemplateProject();
}
public int Start()
{
var project = GetProjectJsonPath(_projectJson) ?? _temporaryDotnetNewProject.ProjectJsonPath;
EnsureNotNull(project, "Unable to find project.json");
var projectDirectory = Path.GetDirectoryName(project);
var templateFile = _templateFile ?? _temporaryDotnetNewProject.MSBuildProjectPath;
EnsureNotNull(templateFile, "Unable to find default msbuild template");
var outputDirectory = _outputDirectory ?? Path.GetDirectoryName(project);
EnsureNotNull(outputDirectory, "Null output directory");
var sdkVersion = _sdkVersion ?? new ProjectJsonParser(_temporaryDotnetNewProject.ProjectJson).SdkPackageVersion;
EnsureNotNull(sdkVersion, "Null Sdk Version");
var migrationSettings = new MigrationSettings(projectDirectory, outputDirectory, sdkVersion, templateFile);
new ProjectMigrator().Migrate(migrationSettings);
return 0;
}
private void EnsureNotNull(string variable, string message)
{
if (variable == null)
{
throw new Exception(message);
}
}
private string GetProjectJsonPath(string projectJson)
{
if (projectJson == null)
{
return null;
}
if (File.Exists(projectJson))
{
return projectJson;
}
if (Directory.Exists(projectJson))
{
var projectCandidate = Path.Combine(projectJson, "project.json");
if (File.Exists(projectCandidate))
{
return projectCandidate;
}
}
throw new Exception($"Unable to find project file at {projectJson}");
}
}
}

View file

@ -0,0 +1,55 @@
// 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 Microsoft.DotNet.Cli.CommandLine;
using Microsoft.DotNet.Cli.Utils;
namespace Microsoft.DotNet.Tools.Migrate
{
public partial class MigrateCommand
{
public static int Run(string[] args)
{
DebugHelper.HandleDebugSwitch(ref args);
CommandLineApplication app = new CommandLineApplication(throwOnUnexpectedArg: false);
app.Name = "dotnet migrate";
app.FullName = ".NET Migrate Command";
app.Description = "Command used to migrate project.json projects to msbuild";
app.HandleResponseFiles = true;
app.AllowArgumentSeparator = true;
app.HelpOption("-h|--help");
CommandOption template = app.Option("-t|--template-file", "Base MSBuild template to use for migrated app. The default is the project included in dotnet new -t msbuild", CommandOptionType.SingleValue);
CommandOption output = app.Option("-o|--output", "Directory to output migrated project to. The default is the project directory", CommandOptionType.SingleValue);
CommandOption project = app.Option("-p|--project", "The path to the project to run (defaults to the current directory). Can be a path to a project.json or a project directory", CommandOptionType.SingleValue);
CommandOption sdkVersion = app.Option("-v|--sdk-package-version", "The version of the sdk package that will be referenced in the migrated app. The default is the version of the sdk in dotnet new -t msbuild", CommandOptionType.SingleValue);
app.OnExecute(() =>
{
MigrateCommand migrateCommand = new MigrateCommand(
template.Value(),
output.Value(),
project.Value(),
sdkVersion.Value());
return migrateCommand.Start();
});
try
{
return app.Execute(args);
}
catch (Exception ex)
{
#if DEBUG
Reporter.Error.WriteLine(ex.ToString());
#else
Reporter.Error.WriteLine(ex.Message);
#endif
return 1;
}
}
}
}

View file

@ -0,0 +1,81 @@
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.DotNet.Cli
{
/// <summary>
/// Parses select data from a project.json without relying on ProjectModel.
/// Used to parse simple information.
/// </summary>
internal class ProjectJsonParser
{
public static string SdkPackageName => "Microsoft.DotNet.Core.Sdk";
public string SdkPackageVersion { get; }
public ProjectJsonParser(JObject projectJson)
{
SdkPackageVersion = GetSdkPackageVersion(projectJson);
}
private string GetSdkPackageVersion(JObject projectJson)
{
var sdkPackageNode = SelectJsonNodes(projectJson, property => property.Name == SdkPackageName).First();
if (sdkPackageNode.Value.Type == JTokenType.String)
{
return (string)sdkPackageNode.Value;
}
else if (sdkPackageNode.Type == JTokenType.Object)
{
var sdkPackageNodeValue = (JObject)sdkPackageNode.Value;
JToken sdkVersionNode;
if (sdkPackageNodeValue.TryGetValue("version", out sdkVersionNode))
{
return sdkVersionNode.Value<string>();
}
else
{
throw new Exception("Unable to determine sdk version, no version node in default template.");
}
}
else
{
throw new Exception("Unable to determine sdk version, no version information found");
}
}
private IEnumerable<JProperty> SelectJsonNodes(
JToken jsonNode,
Func<JProperty, bool> condition,
List<JProperty> nodeAccumulator = null)
{
nodeAccumulator = nodeAccumulator ?? new List<JProperty>();
if (jsonNode.Type == JTokenType.Object)
{
var eligibleNodes = jsonNode.Children<JProperty>().Where(j => condition(j));
nodeAccumulator.AddRange(eligibleNodes);
foreach (var child in jsonNode.Children<JProperty>())
{
SelectJsonNodes(child.Value, condition, nodeAccumulator: nodeAccumulator);
}
}
else if (jsonNode.Type == JTokenType.Array)
{
foreach (var child in jsonNode.Children())
{
SelectJsonNodes(child, condition, nodeAccumulator: nodeAccumulator);
}
}
return nodeAccumulator;
}
}
}

View file

@ -0,0 +1,85 @@
using Microsoft.Build.Construction;
using Microsoft.DotNet.Cli;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.DotNet.Cli
{
internal class TemporaryDotnetNewTemplateProject
{
private static string s_temporaryDotnetNewMSBuildProjectName = "p";
public TemporaryDotnetNewTemplateProject()
{
ProjectDirectory = CreateDotnetNewMSBuild(s_temporaryDotnetNewMSBuildProjectName);
MSBuildProject = GetMSBuildProject(ProjectDirectory);
ProjectJson = GetProjectJson(ProjectDirectory);
}
public ProjectRootElement MSBuildProject { get; }
public JObject ProjectJson { get; }
public string ProjectDirectory { get; }
public string ProjectJsonPath => Path.Combine(ProjectDirectory, "project.json");
public string MSBuildProjectPath => Path.Combine(ProjectDirectory, s_temporaryDotnetNewMSBuildProjectName);
public void Clean()
{
Directory.Delete(ProjectDirectory, true);
}
private string CreateDotnetNewMSBuild(string projectName)
{
var guid = Guid.NewGuid().ToString();
var tempDir = Path.Combine(
Path.GetTempPath(),
this.GetType().Namespace,
guid,
s_temporaryDotnetNewMSBuildProjectName);
if (Directory.Exists(tempDir))
{
Directory.Delete(tempDir, true);
}
Directory.CreateDirectory(tempDir);
RunCommand("new", new string[] { "-t", "msbuild" }, tempDir);
return tempDir;
}
private ProjectRootElement GetMSBuildProject(string temporaryDotnetNewMSBuildDirectory)
{
var templateProjPath = Path.Combine(temporaryDotnetNewMSBuildDirectory,
s_temporaryDotnetNewMSBuildProjectName + ".csproj");
return ProjectRootElement.Open(templateProjPath);
}
private JObject GetProjectJson(string temporaryDotnetNewMSBuildDirectory)
{
var projectJsonFile = Path.Combine(temporaryDotnetNewMSBuildDirectory, "project.json");
return JObject.Parse(File.ReadAllText(projectJsonFile));
}
private void RunCommand(string commandToExecute, IEnumerable<string> args, string workingDirectory)
{
var command = new DotNetCommandFactory()
.Create(commandToExecute, args)
.WorkingDirectory(workingDirectory)
.CaptureStdOut()
.CaptureStdErr();
var commandResult = command.Execute();
if (commandResult.ExitCode != 0)
{
throw new Exception($"Failed to run {commandToExecute} in directory: {workingDirectory}");
}
}
}
}

View file

@ -45,6 +45,9 @@
"Microsoft.DotNet.Configurer": {
"target": "project"
},
"Microsoft.DotNet.ProjectJsonMigration": {
"target": "project"
},
"Microsoft.DotNet.Tools.Test": {
"target": "project"
},