dotnet-migrate built in command for cli
This commit is contained in:
parent
46818ff3fa
commit
611e4ccfde
8 changed files with 311 additions and 1 deletions
|
@ -21,6 +21,7 @@ using Microsoft.DotNet.Tools.Restore;
|
||||||
using Microsoft.DotNet.Tools.Restore3;
|
using Microsoft.DotNet.Tools.Restore3;
|
||||||
using Microsoft.DotNet.Tools.Run;
|
using Microsoft.DotNet.Tools.Run;
|
||||||
using Microsoft.DotNet.Tools.Test;
|
using Microsoft.DotNet.Tools.Test;
|
||||||
|
using Microsoft.DotNet.Tools.Migrate;
|
||||||
using NuGet.Frameworks;
|
using NuGet.Frameworks;
|
||||||
|
|
||||||
namespace Microsoft.DotNet.Cli
|
namespace Microsoft.DotNet.Cli
|
||||||
|
@ -42,6 +43,7 @@ namespace Microsoft.DotNet.Cli
|
||||||
["run3"] = Run3Command.Run,
|
["run3"] = Run3Command.Run,
|
||||||
["restore3"] = Restore3Command.Run,
|
["restore3"] = Restore3Command.Run,
|
||||||
["pack3"] = Pack3Command.Run,
|
["pack3"] = Pack3Command.Run,
|
||||||
|
["migrate"] = MigrateCommand.Run
|
||||||
};
|
};
|
||||||
|
|
||||||
public static int Main(string[] args)
|
public static int Main(string[] args)
|
||||||
|
|
|
@ -3,3 +3,4 @@ using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
[assembly: AssemblyMetadataAttribute("Serviceable", "True")]
|
[assembly: AssemblyMetadataAttribute("Serviceable", "True")]
|
||||||
[assembly: InternalsVisibleTo("dotnet.Tests")]
|
[assembly: InternalsVisibleTo("dotnet.Tests")]
|
||||||
|
[assembly: InternalsVisibleTo("Microsoft.DotNet.ProjectJsonMigration.Tests")]
|
|
@ -15,6 +15,5 @@ namespace Microsoft.DotNet.Cli
|
||||||
{
|
{
|
||||||
return new MSBuildForwardingApp(args).Execute();
|
return new MSBuildForwardingApp(args).Execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
84
src/dotnet/commands/dotnet-migrate/MigrateCommand.cs
Normal file
84
src/dotnet/commands/dotnet-migrate/MigrateCommand.cs
Normal 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}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
55
src/dotnet/commands/dotnet-migrate/Program.cs
Normal file
55
src/dotnet/commands/dotnet-migrate/Program.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
81
src/dotnet/commands/dotnet-migrate/ProjectJsonParser.cs
Normal file
81
src/dotnet/commands/dotnet-migrate/ProjectJsonParser.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -45,6 +45,9 @@
|
||||||
"Microsoft.DotNet.Configurer": {
|
"Microsoft.DotNet.Configurer": {
|
||||||
"target": "project"
|
"target": "project"
|
||||||
},
|
},
|
||||||
|
"Microsoft.DotNet.ProjectJsonMigration": {
|
||||||
|
"target": "project"
|
||||||
|
},
|
||||||
"Microsoft.DotNet.Tools.Test": {
|
"Microsoft.DotNet.Tools.Test": {
|
||||||
"target": "project"
|
"target": "project"
|
||||||
},
|
},
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue