Global tools package obtain (#8035)

Add ExeutablePackageObtainer

Given a tools package id, it can create a fake project and restore to correct folder

- DI, aka no circular dependency of commands
- Parser of config XML
- I try to create test nupkg at build time, so I can run test and debug
easily with VSCode. The code is in test csproj.
This commit is contained in:
William Lee 2017-11-21 20:10:06 -08:00 committed by GitHub
parent 2fc86b6d01
commit 584d3f0502
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 1169 additions and 29 deletions

View file

@ -1,6 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26730.0
VisualStudioVersion = 15.0.27004.2008
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{ED2FE3E2-F7E7-4389-8231-B65123F2076F}"
EndProject
@ -230,6 +230,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.TestFramew
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Msbuild.Tests.Utilities", "test\Msbuild.Tests.Utilities\Msbuild.Tests.Utilities.csproj", "{E7C72EF2-8480-48B4-AAE8-A596F1A6048E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.ToolPackageObtainer.Tests", "test\Microsoft.DotNet.ToolPackageObtainer.Tests\Microsoft.DotNet.ToolPackageObtainer.Tests.csproj", "{F0D50831-9468-4ACB-8FD8-E9883DD553FB}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -1614,6 +1616,30 @@ Global
{E7C72EF2-8480-48B4-AAE8-A596F1A6048E}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
{E7C72EF2-8480-48B4-AAE8-A596F1A6048E}.RelWithDebInfo|x86.ActiveCfg = Release|Any CPU
{E7C72EF2-8480-48B4-AAE8-A596F1A6048E}.RelWithDebInfo|x86.Build.0 = Release|Any CPU
{F0D50831-9468-4ACB-8FD8-E9883DD553FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F0D50831-9468-4ACB-8FD8-E9883DD553FB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F0D50831-9468-4ACB-8FD8-E9883DD553FB}.Debug|x64.ActiveCfg = Debug|Any CPU
{F0D50831-9468-4ACB-8FD8-E9883DD553FB}.Debug|x64.Build.0 = Debug|Any CPU
{F0D50831-9468-4ACB-8FD8-E9883DD553FB}.Debug|x86.ActiveCfg = Debug|Any CPU
{F0D50831-9468-4ACB-8FD8-E9883DD553FB}.Debug|x86.Build.0 = Debug|Any CPU
{F0D50831-9468-4ACB-8FD8-E9883DD553FB}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU
{F0D50831-9468-4ACB-8FD8-E9883DD553FB}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU
{F0D50831-9468-4ACB-8FD8-E9883DD553FB}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU
{F0D50831-9468-4ACB-8FD8-E9883DD553FB}.MinSizeRel|x64.Build.0 = Debug|Any CPU
{F0D50831-9468-4ACB-8FD8-E9883DD553FB}.MinSizeRel|x86.ActiveCfg = Debug|Any CPU
{F0D50831-9468-4ACB-8FD8-E9883DD553FB}.MinSizeRel|x86.Build.0 = Debug|Any CPU
{F0D50831-9468-4ACB-8FD8-E9883DD553FB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F0D50831-9468-4ACB-8FD8-E9883DD553FB}.Release|Any CPU.Build.0 = Release|Any CPU
{F0D50831-9468-4ACB-8FD8-E9883DD553FB}.Release|x64.ActiveCfg = Release|Any CPU
{F0D50831-9468-4ACB-8FD8-E9883DD553FB}.Release|x64.Build.0 = Release|Any CPU
{F0D50831-9468-4ACB-8FD8-E9883DD553FB}.Release|x86.ActiveCfg = Release|Any CPU
{F0D50831-9468-4ACB-8FD8-E9883DD553FB}.Release|x86.Build.0 = Release|Any CPU
{F0D50831-9468-4ACB-8FD8-E9883DD553FB}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU
{F0D50831-9468-4ACB-8FD8-E9883DD553FB}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU
{F0D50831-9468-4ACB-8FD8-E9883DD553FB}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU
{F0D50831-9468-4ACB-8FD8-E9883DD553FB}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
{F0D50831-9468-4ACB-8FD8-E9883DD553FB}.RelWithDebInfo|x86.ActiveCfg = Release|Any CPU
{F0D50831-9468-4ACB-8FD8-E9883DD553FB}.RelWithDebInfo|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -1686,6 +1712,7 @@ Global
{B4EE3671-C103-4A37-8DEB-C74E0134104E} = {17735A9D-BFD9-4585-A7CB-3208CA6EA8A7}
{44759218-B558-4AF0-8991-515F1100DCF5} = {17735A9D-BFD9-4585-A7CB-3208CA6EA8A7}
{E7C72EF2-8480-48B4-AAE8-A596F1A6048E} = {17735A9D-BFD9-4585-A7CB-3208CA6EA8A7}
{F0D50831-9468-4ACB-8FD8-E9883DD553FB} = {17735A9D-BFD9-4585-A7CB-3208CA6EA8A7}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B526D2CE-EE2D-4AD4-93EF-1867D90FF1F5}

View file

@ -0,0 +1,47 @@
// 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;
namespace Microsoft.Extensions.EnvironmentAbstractions
{
public struct DirectoryPath
{
public string Value { get; }
public DirectoryPath(string value)
{
Value = value;
}
public DirectoryPath WithSubDirectories(params string[] paths)
{
string[] insertValueInFront = new string[paths.Length + 1];
insertValueInFront[0] = Value;
Array.Copy(paths, 0, insertValueInFront, 1, paths.Length);
return new DirectoryPath(Path.Combine(insertValueInFront));
}
public FilePath WithFile(string fileName)
{
return new FilePath(Path.Combine(Value, fileName));
}
public string ToQuotedString()
{
return $"\"{Value}\"";
}
public override string ToString()
{
return ToQuotedString();
}
public DirectoryPath GetParentPath()
{
return new DirectoryPath(Directory.GetParent(Path.GetFullPath(Value)).FullName);
}
}
}

View file

@ -0,0 +1,32 @@
// 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.IO;
namespace Microsoft.Extensions.EnvironmentAbstractions
{
public struct FilePath
{
public string Value { get; }
public FilePath(string value)
{
Value = value;
}
public string ToQuotedString()
{
return $"\"{Value}\"";
}
public override string ToString()
{
return ToQuotedString();
}
public DirectoryPath GetDirectoryPath()
{
return new DirectoryPath(Path.GetDirectoryName(Value));
}
}
}

View file

@ -0,0 +1,21 @@
using System.Reflection;
using System.Runtime.Versioning;
using NuGet.Frameworks;
namespace Microsoft.DotNet.Cli
{
internal static class BundledTargetFramework
{
public static string GetTargetFrameworkMoniker()
{
TargetFrameworkAttribute targetFrameworkAttribute = typeof(BundledTargetFramework)
.GetTypeInfo()
.Assembly
.GetCustomAttribute<TargetFrameworkAttribute>();
return NuGetFramework
.Parse(targetFrameworkAttribute.FrameworkName)
.GetShortFolderName();
}
}
}

View file

@ -17,3 +17,4 @@ using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("dotnet-sln-remove.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("dotnet-msbuild.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("dotnet-run.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.DotNet.ToolPackageObtainer.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]

View file

@ -0,0 +1,12 @@
// 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 Microsoft.Extensions.EnvironmentAbstractions;
namespace Microsoft.DotNet.ToolPackageObtainer
{
internal interface IPackageToProjectFileAdder
{
void Add(FilePath projectPath, string packageId);
}
}

View file

@ -0,0 +1,15 @@
// 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 Microsoft.Extensions.EnvironmentAbstractions;
namespace Microsoft.DotNet.ToolPackageObtainer
{
internal interface IProjectRestorer
{
void Restore(
FilePath projectPath,
DirectoryPath assetJsonOutput,
FilePath? nugetconfig);
}
}

View file

@ -0,0 +1,22 @@
// 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;
namespace Microsoft.DotNet.ToolPackageObtainer
{
internal class PackageObtainException : Exception
{
public PackageObtainException()
{
}
public PackageObtainException(string message) : base(message)
{
}
public PackageObtainException(string message, Exception innerException) : base(message, innerException)
{
}
}
}

View file

@ -0,0 +1,28 @@
// 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.IO;
namespace Microsoft.DotNet.ToolPackageObtainer
{
internal class PackageVersion
{
public PackageVersion(string packageVersion)
{
if (packageVersion == null)
{
Value = Path.GetRandomFileName();
IsPlaceholder = true;
}
else
{
Value = packageVersion;
IsPlaceholder = false;
}
}
public bool IsPlaceholder { get; }
public string Value { get; }
public bool IsConcreteValue => !IsPlaceholder;
}
}

View file

@ -0,0 +1,46 @@
// 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;
namespace Microsoft.DotNet.ToolPackageObtainer
{
internal class ToolConfiguration
{
public ToolConfiguration(
string commandName,
string toolAssemblyEntryPoint)
{
if (string.IsNullOrWhiteSpace(commandName))
{
throw new ArgumentNullException(nameof(commandName), "Cannot be null or whitespace");
}
EnsureNoInvalidFilenameCharacters(commandName, nameof(toolAssemblyEntryPoint));
if (string.IsNullOrWhiteSpace(toolAssemblyEntryPoint))
{
throw new ArgumentNullException(nameof(toolAssemblyEntryPoint), "Cannot be null or whitespace");
}
CommandName = commandName;
ToolAssemblyEntryPoint = toolAssemblyEntryPoint;
}
private void EnsureNoInvalidFilenameCharacters(string commandName, string nameOfParam)
{
// https://stackoverflow.com/questions/1976007/what-characters-are-forbidden-in-windows-and-linux-directory-names
char[] invalidCharactors = {'/', '<', '>', ':', '"', '/', '\\', '|', '?', '*'};
if (commandName.IndexOfAny(invalidCharactors) != -1)
{
throw new ArgumentException(
paramName: nameof(nameOfParam),
message: "Cannot contain following character " + new string(invalidCharactors));
}
}
public string CommandName { get; }
public string ToolAssemblyEntryPoint { get; }
}
}

View file

@ -0,0 +1,21 @@
// 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 Microsoft.Extensions.EnvironmentAbstractions;
namespace Microsoft.DotNet.ToolPackageObtainer
{
internal class ToolConfigurationAndExecutableDirectory
{
public ToolConfigurationAndExecutableDirectory(
ToolConfiguration toolConfiguration,
DirectoryPath executableDirectory)
{
Configuration = toolConfiguration;
ExecutableDirectory = executableDirectory;
}
public ToolConfiguration Configuration { get; }
public DirectoryPath ExecutableDirectory { get; }
}
}

View file

@ -0,0 +1,13 @@
using System.Diagnostics;
using System.Xml.Serialization;
namespace Microsoft.DotNet.ToolPackageObtainer.ToolConfigurationDeserialization
{
[DebuggerStepThrough]
[XmlRoot(Namespace = "", IsNullable = false)]
public class DotNetCliTool
{
[XmlArrayItem("Command", IsNullable = false)]
public DotNetCliToolCommand[] Commands { get; set; }
}
}

View file

@ -0,0 +1,21 @@
using System;
using System.Diagnostics;
using System.Xml.Serialization;
namespace Microsoft.DotNet.ToolPackageObtainer.ToolConfigurationDeserialization
{
[Serializable]
[DebuggerStepThrough]
[XmlType(AnonymousType = true)]
public class DotNetCliToolCommand
{
[XmlAttribute]
public string Name { get; set; }
[XmlAttribute]
public string EntryPoint { get; set; }
[XmlAttribute]
public string Runner { get; set; }
}
}

View file

@ -0,0 +1,61 @@
// 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 System.Xml;
using System.Xml.Serialization;
using Microsoft.DotNet.ToolPackageObtainer.ToolConfigurationDeserialization;
namespace Microsoft.DotNet.ToolPackageObtainer
{
internal static class ToolConfigurationDeserializer
{
public static ToolConfiguration Deserialize(string pathToXml)
{
var serializer = new XmlSerializer(typeof(DotNetCliTool));
DotNetCliTool dotNetCliTool;
using (var fs = new FileStream(pathToXml, FileMode.Open))
{
var reader = XmlReader.Create(fs);
try
{
dotNetCliTool = (DotNetCliTool)serializer.Deserialize(reader);
}
catch (InvalidOperationException e) when (e.InnerException is XmlException)
{
throw new ToolConfigurationException(
"Failed to retrive tool configuration exception, configuration is malformed xml. " +
e.InnerException.Message);
}
}
if (dotNetCliTool.Commands.Length != 1)
{
throw new ToolConfigurationException(
"Failed to retrive tool configuration exception, one and only one command is supported.");
}
if (dotNetCliTool.Commands[0].Runner != "dotnet")
{
throw new ToolConfigurationException(
"Failed to retrive tool configuration exception, only dotnet as runner is supported.");
}
var commandName = dotNetCliTool.Commands[0].Name;
var toolAssemblyEntryPoint = dotNetCliTool.Commands[0].EntryPoint;
try
{
return new ToolConfiguration(commandName, toolAssemblyEntryPoint);
}
catch (ArgumentException e)
{
throw new ToolConfigurationException("Configuration content error. " + e.Message);
}
}
}
}

View file

@ -0,0 +1,14 @@
// 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;
namespace Microsoft.DotNet.ToolPackageObtainer
{
internal class ToolConfigurationException : ArgumentException
{
public ToolConfigurationException(string message) : base(message)
{
}
}
}

View file

@ -0,0 +1,190 @@
using System;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using Microsoft.Extensions.EnvironmentAbstractions;
namespace Microsoft.DotNet.ToolPackageObtainer
{
internal class ToolPackageObtainer
{
private readonly Lazy<string> _bundledTargetFrameworkMoniker;
private readonly Func<FilePath> _getTempProjectPath;
private readonly IPackageToProjectFileAdder _packageToProjectFileAdder;
private readonly IProjectRestorer _projectRestorer;
private readonly DirectoryPath _toolsPath;
public ToolPackageObtainer(
DirectoryPath toolsPath,
Func<FilePath> getTempProjectPath,
Lazy<string> bundledTargetFrameworkMoniker,
IPackageToProjectFileAdder packageToProjectFileAdder,
IProjectRestorer projectRestorer
)
{
_getTempProjectPath = getTempProjectPath;
_bundledTargetFrameworkMoniker = bundledTargetFrameworkMoniker;
_projectRestorer = projectRestorer ?? throw new ArgumentNullException(nameof(projectRestorer));
_packageToProjectFileAdder = packageToProjectFileAdder ??
throw new ArgumentNullException(nameof(packageToProjectFileAdder));
_toolsPath = toolsPath;
}
public ToolConfigurationAndExecutableDirectory ObtainAndReturnExecutablePath(
string packageId,
string packageVersion = null,
FilePath? nugetconfig = null,
string targetframework = null)
{
if (packageId == null)
{
throw new ArgumentNullException(nameof(packageId));
}
if (targetframework == null)
{
targetframework = _bundledTargetFrameworkMoniker.Value;
}
var packageVersionOrPlaceHolder = new PackageVersion(packageVersion);
DirectoryPath individualToolVersion =
CreateIndividualToolVersionDirectory(packageId, packageVersionOrPlaceHolder);
FilePath tempProjectPath = CreateTempProject(
packageId,
packageVersionOrPlaceHolder,
targetframework,
individualToolVersion);
if (packageVersionOrPlaceHolder.IsPlaceholder)
{
InvokeAddPackageRestore(
nugetconfig,
tempProjectPath,
packageId);
}
InvokeRestore(nugetconfig, tempProjectPath, individualToolVersion);
if (packageVersionOrPlaceHolder.IsPlaceholder)
{
var concreteVersion =
new DirectoryInfo(
Directory.GetDirectories(
individualToolVersion.WithSubDirectories(packageId).Value).Single()).Name;
DirectoryPath concreteVersionIndividualToolVersion =
individualToolVersion.GetParentPath().WithSubDirectories(concreteVersion);
Directory.Move(individualToolVersion.Value, concreteVersionIndividualToolVersion.Value);
individualToolVersion = concreteVersionIndividualToolVersion;
packageVersion = concreteVersion;
}
ToolConfiguration toolConfiguration = GetConfiguration(packageId, packageVersion, individualToolVersion);
return new ToolConfigurationAndExecutableDirectory(
toolConfiguration,
individualToolVersion.WithSubDirectories(
packageId,
packageVersion,
"tools",
targetframework));
}
private static ToolConfiguration GetConfiguration(
string packageId,
string packageVersion,
DirectoryPath individualToolVersion)
{
FilePath toolConfigurationPath =
individualToolVersion
.WithSubDirectories(packageId, packageVersion, "tools")
.WithFile("DotnetToolsConfig.xml");
ToolConfiguration toolConfiguration =
ToolConfigurationDeserializer.Deserialize(toolConfigurationPath.Value);
return toolConfiguration;
}
private void InvokeRestore(
FilePath? nugetconfig,
FilePath tempProjectPath,
DirectoryPath individualToolVersion)
{
_projectRestorer.Restore(tempProjectPath, individualToolVersion, nugetconfig);
}
private FilePath CreateTempProject(
string packageId,
PackageVersion packageVersion,
string targetframework,
DirectoryPath individualToolVersion)
{
FilePath tempProjectPath = _getTempProjectPath();
if (Path.GetExtension(tempProjectPath.Value) != "csproj")
{
tempProjectPath = new FilePath(Path.ChangeExtension(tempProjectPath.Value, "csproj"));
}
EnsureDirectoryExists(tempProjectPath.GetDirectoryPath());
var tempProjectContent = new XDocument(
new XElement("Project",
new XAttribute("Sdk", "Microsoft.NET.Sdk"),
new XElement("PropertyGroup",
new XElement("TargetFramework", targetframework),
new XElement("RestorePackagesPath", individualToolVersion.Value),
new XElement("DisableImplicitFrameworkReferences", "true")
),
packageVersion.IsConcreteValue
? new XElement("ItemGroup",
new XElement("PackageReference",
new XAttribute("Include", packageId),
new XAttribute("Version", packageVersion.Value)
))
: null));
File.WriteAllText(tempProjectPath.Value,
tempProjectContent.ToString());
return tempProjectPath;
}
private void InvokeAddPackageRestore(
FilePath? nugetconfig,
FilePath tempProjectPath,
string packageId)
{
if (nugetconfig != null)
{
File.Copy(
nugetconfig.Value.Value,
tempProjectPath
.GetDirectoryPath()
.WithFile("nuget.config")
.Value);
}
_packageToProjectFileAdder.Add(tempProjectPath, packageId);
}
private DirectoryPath CreateIndividualToolVersionDirectory(
string packageId,
PackageVersion packageVersion)
{
DirectoryPath individualTool = _toolsPath.WithSubDirectories(packageId);
DirectoryPath individualToolVersion = individualTool.WithSubDirectories(packageVersion.Value);
EnsureDirectoryExists(individualToolVersion);
return individualToolVersion;
}
private static void EnsureDirectoryExists(DirectoryPath path)
{
if (!Directory.Exists(path.Value))
{
Directory.CreateDirectory(path.Value);
}
}
}
}

View file

@ -0,0 +1,49 @@
// 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 Microsoft.DotNet.ToolPackageObtainer;
using Microsoft.Extensions.EnvironmentAbstractions;
namespace Microsoft.DotNet.Cli
{
internal class PackageToProjectFileAdder : IPackageToProjectFileAdder
{
public void Add(FilePath projectPath, string packageId)
{
if (packageId == null)
{
throw new ArgumentNullException(nameof(packageId));
}
var argsToPassToRestore = new List<string>
{
projectPath.Value,
"package",
packageId,
"--no-restore"
};
var command = new DotNetCommandFactory(alwaysRunOutOfProc: true)
.Create(
"add",
argsToPassToRestore)
.CaptureStdOut()
.CaptureStdErr();
var result = command.Execute();
if (result.ExitCode != 0)
{
throw new PackageObtainException("Failed to add package. " +
$"{Environment.NewLine}WorkingDirectory: " +
result.StartInfo.WorkingDirectory +
$"{Environment.NewLine}Arguments: " +
result.StartInfo.Arguments +
$"{Environment.NewLine}Output: " +
result.StdErr + result.StdOut);
}
}
}
}

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 System.Collections.Generic;
using Microsoft.DotNet.ToolPackageObtainer;
using Microsoft.DotNet.PlatformAbstractions;
using Microsoft.Extensions.EnvironmentAbstractions;
namespace Microsoft.DotNet.Cli
{
internal class ProjectRestorer : IProjectRestorer
{
public void Restore(
FilePath projectPath,
DirectoryPath assetJsonOutput,
FilePath? nugetconfig)
{
var argsToPassToRestore = new List<string>();
argsToPassToRestore.Add(projectPath.ToQuotedString());
if (nugetconfig != null)
{
argsToPassToRestore.Add("--configfile");
argsToPassToRestore.Add(nugetconfig.Value.ToQuotedString());
}
argsToPassToRestore.AddRange(new List<string>
{
"--runtime",
RuntimeEnvironment.GetRuntimeIdentifier(),
$"/p:BaseIntermediateOutputPath={assetJsonOutput.ToQuotedString()}"
});
var command = new DotNetCommandFactory(alwaysRunOutOfProc: true)
.Create(
"restore",
argsToPassToRestore)
.CaptureStdOut()
.CaptureStdErr();
var result = command.Execute();
if (result.ExitCode != 0)
{
throw new PackageObtainException("Failed to restore package. " +
$"{Environment.NewLine}WorkingDirectory: " +
result.StartInfo.WorkingDirectory +
$"{Environment.NewLine}Arguments: " +
result.StartInfo.Arguments +
$"{Environment.NewLine}Output: " +
result.StdErr + result.StdOut);
}
}
}
}

View file

@ -1,6 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26419.0
VisualStudioVersion = 15.0.27004.2008
MinimumVisualStudioVersion = 10.0.40219.1
Project("{13B669BE-BB05-4DDF-9536-439F39A36129}") = "dotnet-add-reference.Tests", "dotnet-add-reference.Tests\dotnet-add-reference.Tests.csproj", "{AB63A3E5-76A3-4EE9-A380-8E0C7B7644DC}"
EndProject
@ -74,13 +74,15 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dotnet-store.Tests", "dotne
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dotnet-back-compat.Tests", "dotnet-back-compat.Tests\dotnet-back-compat.Tests.csproj", "{27351B2F-325B-4843-9F4C-BC53FD06A7B5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dotnet-remove-package.Tests", "dotnet-remove-package.Tests\dotnet-remove-package.Tests.csproj", "{CF517B15-B307-4660-87D5-CC036ADECD4B}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dotnet-remove-package.Tests", "dotnet-remove-package.Tests\dotnet-remove-package.Tests.csproj", "{CF517B15-B307-4660-87D5-CC036ADECD4B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.MSBuildSdkResolver.Tests", "Microsoft.DotNet.MSBuildSdkResolver.Tests\Microsoft.DotNet.MSBuildSdkResolver.Tests.csproj", "{42A0CAB4-FFAD-47D4-9880-C0F4EDCF93DE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dotnet-clean.Tests", "dotnet-clean.Tests\dotnet-clean.Tests.csproj", "{D9A582B8-1FE2-45D5-B139-0BA828FE3691}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dotnet-clean.Tests", "dotnet-clean.Tests\dotnet-clean.Tests.csproj", "{D9A582B8-1FE2-45D5-B139-0BA828FE3691}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.DotNet.TestFramework", "Microsoft.DotNet.TestFramework\Microsoft.DotNet.TestFramework.csproj", "{D23AFC9C-8FD5-45DD-A84C-0E6528C42F0E}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.TestFramework", "Microsoft.DotNet.TestFramework\Microsoft.DotNet.TestFramework.csproj", "{D23AFC9C-8FD5-45DD-A84C-0E6528C42F0E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.ToolPackageObtainer.Tests", "Microsoft.DotNet.ToolPackageObtainer.Tests\Microsoft.DotNet.ToolPackageObtainer.Tests.csproj", "{C2A907A3-677B-4C73-9AA4-E53613E13C78}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -490,16 +492,16 @@ Global
{27351B2F-325B-4843-9F4C-BC53FD06A7B5}.Release|x86.Build.0 = Release|Any CPU
{CF517B15-B307-4660-87D5-CC036ADECD4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CF517B15-B307-4660-87D5-CC036ADECD4B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CF517B15-B307-4660-87D5-CC036ADECD4B}.Debug|x64.ActiveCfg = Debug|x64
{CF517B15-B307-4660-87D5-CC036ADECD4B}.Debug|x64.Build.0 = Debug|x64
{CF517B15-B307-4660-87D5-CC036ADECD4B}.Debug|x86.ActiveCfg = Debug|x86
{CF517B15-B307-4660-87D5-CC036ADECD4B}.Debug|x86.Build.0 = Debug|x86
{CF517B15-B307-4660-87D5-CC036ADECD4B}.Debug|x64.ActiveCfg = Debug|Any CPU
{CF517B15-B307-4660-87D5-CC036ADECD4B}.Debug|x64.Build.0 = Debug|Any CPU
{CF517B15-B307-4660-87D5-CC036ADECD4B}.Debug|x86.ActiveCfg = Debug|Any CPU
{CF517B15-B307-4660-87D5-CC036ADECD4B}.Debug|x86.Build.0 = Debug|Any CPU
{CF517B15-B307-4660-87D5-CC036ADECD4B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CF517B15-B307-4660-87D5-CC036ADECD4B}.Release|Any CPU.Build.0 = Release|Any CPU
{CF517B15-B307-4660-87D5-CC036ADECD4B}.Release|x64.ActiveCfg = Release|x64
{CF517B15-B307-4660-87D5-CC036ADECD4B}.Release|x64.Build.0 = Release|x64
{CF517B15-B307-4660-87D5-CC036ADECD4B}.Release|x86.ActiveCfg = Release|x86
{CF517B15-B307-4660-87D5-CC036ADECD4B}.Release|x86.Build.0 = Release|x86
{CF517B15-B307-4660-87D5-CC036ADECD4B}.Release|x64.ActiveCfg = Release|Any CPU
{CF517B15-B307-4660-87D5-CC036ADECD4B}.Release|x64.Build.0 = Release|Any CPU
{CF517B15-B307-4660-87D5-CC036ADECD4B}.Release|x86.ActiveCfg = Release|Any CPU
{CF517B15-B307-4660-87D5-CC036ADECD4B}.Release|x86.Build.0 = Release|Any CPU
{42A0CAB4-FFAD-47D4-9880-C0F4EDCF93DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{42A0CAB4-FFAD-47D4-9880-C0F4EDCF93DE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{42A0CAB4-FFAD-47D4-9880-C0F4EDCF93DE}.Debug|x64.ActiveCfg = Debug|Any CPU
@ -514,28 +516,40 @@ Global
{42A0CAB4-FFAD-47D4-9880-C0F4EDCF93DE}.Release|x86.Build.0 = Release|Any CPU
{D9A582B8-1FE2-45D5-B139-0BA828FE3691}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D9A582B8-1FE2-45D5-B139-0BA828FE3691}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D9A582B8-1FE2-45D5-B139-0BA828FE3691}.Debug|x64.ActiveCfg = Debug|x64
{D9A582B8-1FE2-45D5-B139-0BA828FE3691}.Debug|x64.Build.0 = Debug|x64
{D9A582B8-1FE2-45D5-B139-0BA828FE3691}.Debug|x86.ActiveCfg = Debug|x86
{D9A582B8-1FE2-45D5-B139-0BA828FE3691}.Debug|x86.Build.0 = Debug|x86
{D9A582B8-1FE2-45D5-B139-0BA828FE3691}.Debug|x64.ActiveCfg = Debug|Any CPU
{D9A582B8-1FE2-45D5-B139-0BA828FE3691}.Debug|x64.Build.0 = Debug|Any CPU
{D9A582B8-1FE2-45D5-B139-0BA828FE3691}.Debug|x86.ActiveCfg = Debug|Any CPU
{D9A582B8-1FE2-45D5-B139-0BA828FE3691}.Debug|x86.Build.0 = Debug|Any CPU
{D9A582B8-1FE2-45D5-B139-0BA828FE3691}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D9A582B8-1FE2-45D5-B139-0BA828FE3691}.Release|Any CPU.Build.0 = Release|Any CPU
{D9A582B8-1FE2-45D5-B139-0BA828FE3691}.Release|x64.ActiveCfg = Release|x64
{D9A582B8-1FE2-45D5-B139-0BA828FE3691}.Release|x64.Build.0 = Release|x64
{D9A582B8-1FE2-45D5-B139-0BA828FE3691}.Release|x86.ActiveCfg = Release|x86
{D9A582B8-1FE2-45D5-B139-0BA828FE3691}.Release|x86.Build.0 = Release|x86
{D9A582B8-1FE2-45D5-B139-0BA828FE3691}.Release|x64.ActiveCfg = Release|Any CPU
{D9A582B8-1FE2-45D5-B139-0BA828FE3691}.Release|x64.Build.0 = Release|Any CPU
{D9A582B8-1FE2-45D5-B139-0BA828FE3691}.Release|x86.ActiveCfg = Release|Any CPU
{D9A582B8-1FE2-45D5-B139-0BA828FE3691}.Release|x86.Build.0 = Release|Any CPU
{D23AFC9C-8FD5-45DD-A84C-0E6528C42F0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D23AFC9C-8FD5-45DD-A84C-0E6528C42F0E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D23AFC9C-8FD5-45DD-A84C-0E6528C42F0E}.Debug|x64.ActiveCfg = Debug|x64
{D23AFC9C-8FD5-45DD-A84C-0E6528C42F0E}.Debug|x64.Build.0 = Debug|x64
{D23AFC9C-8FD5-45DD-A84C-0E6528C42F0E}.Debug|x86.ActiveCfg = Debug|x86
{D23AFC9C-8FD5-45DD-A84C-0E6528C42F0E}.Debug|x86.Build.0 = Debug|x86
{D23AFC9C-8FD5-45DD-A84C-0E6528C42F0E}.Debug|x64.ActiveCfg = Debug|Any CPU
{D23AFC9C-8FD5-45DD-A84C-0E6528C42F0E}.Debug|x64.Build.0 = Debug|Any CPU
{D23AFC9C-8FD5-45DD-A84C-0E6528C42F0E}.Debug|x86.ActiveCfg = Debug|Any CPU
{D23AFC9C-8FD5-45DD-A84C-0E6528C42F0E}.Debug|x86.Build.0 = Debug|Any CPU
{D23AFC9C-8FD5-45DD-A84C-0E6528C42F0E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D23AFC9C-8FD5-45DD-A84C-0E6528C42F0E}.Release|Any CPU.Build.0 = Release|Any CPU
{D23AFC9C-8FD5-45DD-A84C-0E6528C42F0E}.Release|x64.ActiveCfg = Release|x64
{D23AFC9C-8FD5-45DD-A84C-0E6528C42F0E}.Release|x64.Build.0 = Release|x64
{D23AFC9C-8FD5-45DD-A84C-0E6528C42F0E}.Release|x86.ActiveCfg = Release|x86
{D23AFC9C-8FD5-45DD-A84C-0E6528C42F0E}.Release|x86.Build.0 = Release|x86
{D23AFC9C-8FD5-45DD-A84C-0E6528C42F0E}.Release|x64.ActiveCfg = Release|Any CPU
{D23AFC9C-8FD5-45DD-A84C-0E6528C42F0E}.Release|x64.Build.0 = Release|Any CPU
{D23AFC9C-8FD5-45DD-A84C-0E6528C42F0E}.Release|x86.ActiveCfg = Release|Any CPU
{D23AFC9C-8FD5-45DD-A84C-0E6528C42F0E}.Release|x86.Build.0 = Release|Any CPU
{C2A907A3-677B-4C73-9AA4-E53613E13C78}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C2A907A3-677B-4C73-9AA4-E53613E13C78}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C2A907A3-677B-4C73-9AA4-E53613E13C78}.Debug|x64.ActiveCfg = Debug|Any CPU
{C2A907A3-677B-4C73-9AA4-E53613E13C78}.Debug|x64.Build.0 = Debug|Any CPU
{C2A907A3-677B-4C73-9AA4-E53613E13C78}.Debug|x86.ActiveCfg = Debug|Any CPU
{C2A907A3-677B-4C73-9AA4-E53613E13C78}.Debug|x86.Build.0 = Debug|Any CPU
{C2A907A3-677B-4C73-9AA4-E53613E13C78}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C2A907A3-677B-4C73-9AA4-E53613E13C78}.Release|Any CPU.Build.0 = Release|Any CPU
{C2A907A3-677B-4C73-9AA4-E53613E13C78}.Release|x64.ActiveCfg = Release|Any CPU
{C2A907A3-677B-4C73-9AA4-E53613E13C78}.Release|x64.Build.0 = Release|Any CPU
{C2A907A3-677B-4C73-9AA4-E53613E13C78}.Release|x86.ActiveCfg = Release|Any CPU
{C2A907A3-677B-4C73-9AA4-E53613E13C78}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -545,4 +559,7 @@ Global
{5767D8F0-4ED9-4083-8BDC-ED9E65AA86EF} = {15DDC326-69C3-4081-8AA1-B578B2BDE2C6}
{92BA9F90-E25B-4A1C-9598-2295D3DFC12F} = {BB393A93-1770-4753-B7D6-56F0DD378177}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {7AF6777A-0133-453A-B302-FDD974B8F39C}
EndGlobalSection
EndGlobal

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<DotNetCliTool>
<Commands>
<Command Name="sayhello" EntryPoint="console.dll" Runner="dotnet" />
</Commands>
</DotNetCliTool>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<DotNetCliTool>
<Commands Malformed=""
<Command Name="sayhello" EntryPoint="console.dll" Runner="dotnet" />
</Commands>
</DotNetCliTool>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<DotNetCliTool>
<Commands>
<Command EntryPoint="console.dll" Runner="dotnet" />
</Commands>
</DotNetCliTool>

View file

@ -0,0 +1,56 @@
<Project Sdk="Microsoft.NET.Sdk" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<TargetFramework>$(CliTargetFramework)</TargetFramework>
<RuntimeFrameworkVersion>$(CLI_SharedFrameworkVersion)</RuntimeFrameworkVersion>
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
<AssemblyOriginatorKeyFile>../../tools/Key.snk</AssemblyOriginatorKeyFile>
<SignAssembly>true</SignAssembly>
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
<AssetTargetFallback>$(AssetTargetFallback);dotnet5.4;portable-net451+win8</AssetTargetFallback>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\dotnet\dotnet.csproj" />
<ProjectReference Include="..\..\src\Microsoft.DotNet.Configurer\Microsoft.DotNet.Configurer.csproj" />
<ProjectReference Include="..\..\src\Microsoft.DotNet.InternalAbstractions\Microsoft.DotNet.InternalAbstractions.csproj" />
<ProjectReference Include="..\Microsoft.DotNet.Tools.Tests.Utilities\Microsoft.DotNet.Tools.Tests.Utilities.csproj" />
<ProjectReference Include="..\..\src\Microsoft.DotNet.Cli.Utils\Microsoft.DotNet.Cli.Utils.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.3.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />
<PackageReference Include="FluentAssertions" Version="4.18.0" />
<PackageReference Include="Moq" Version="4.7.25" />
<PackageReference Include="xunit" Version="2.2.0" />
<PackageReference Include="System.ComponentModel.TypeConverter" Version="4.3.0" />
</ItemGroup>
<ItemGroup>
<None Update="DotnetToolsConfigMissing.xml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="DotnetToolsConfigMalformed.xml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="DotnetToolsConfigGolden.xml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="TestAssetLocalNugetFeed/*.*">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<Compile Remove="SampleGlobalTool/**" />
<Content Remove="SampleGlobalTool/**" />
<EmbeddedResource Remove="SampleGlobalTool/**" />
<None Remove="SampleGlobalTool/**" />
</ItemGroup>
<Target Name="CreateNupkgFromSource" BeforeTargets="Build">
<PropertyGroup>
<testAssetSourceRoot>$(BaseOutputPath)/TestAsset/SampleGlobalTool</testAssetSourceRoot>
</PropertyGroup>
<Copy SourceFiles="SampleGlobalTool/DotnetToolsConfig.xml" DestinationFolder="$(testAssetSourceRoot)" />
<MSBuild BuildInParallel="False" Projects="SampleGlobalTool/consoledemo.csproj" Targets="Restore;Build;Publish" Properties="Configuration=Release;BaseOutputPath=$(testAssetSourceRoot)/bin/">
</MSBuild>
<MSBuild BuildInParallel="False" Projects="SampleGlobalTool/consoledemo.csproj" Targets="pack" Properties="Configuration=Release;NuspecFile=includepublish.nuspec;NuspecBasePath=$(testAssetSourceRoot);PackageOutputPath=$(OutputPath)/TestAssetLocalNugetFeed">
</MSBuild>
</Target>
</Project>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<DotNetCliTool>
<Commands>
<Command Name="demo" EntryPoint="consoledemo.dll" Runner="dotnet" />
</Commands>
</DotNetCliTool>

View file

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
namespace consoledemo
{
class Program
{
static void Main(string[] args)
{
var greeting = "Hello World from Global Tool";
Console.WriteLine(greeting);
}
}
}

View file

@ -0,0 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
<AssemblyName>consoledemo</AssemblyName>
</PropertyGroup>
</Project>

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>global.tool.console.demo</id>
<version>1.0.4</version>
<description>test app</description>
<authors>testauthor</authors>
</metadata>
<files>
<file src="bin\Release\netcoreapp2.1\publish\*.*" target="tools\netcoreapp2.1\" />
<file src="DotnetToolsConfig.xml" target="tools\DotnetToolsConfig.xml" />
</files>
</package>

View file

@ -0,0 +1,52 @@
// 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 FluentAssertions;
using NuGet.Protocol.Core.Types;
using Xunit;
namespace Microsoft.DotNet.ToolPackageObtainer.Tests
{
public class ToolConfigurationDeserializerTests
{
[Fact]
public void GivenXmlPathItShouldGetToolConfiguration()
{
ToolConfiguration toolConfiguration = ToolConfigurationDeserializer.Deserialize("DotnetToolsConfigGolden.xml");
toolConfiguration.CommandName.Should().Be("sayhello");
toolConfiguration.ToolAssemblyEntryPoint.Should().Be("console.dll");
}
[Fact]
public void GivenMalformedPathItThrows()
{
Action a = () => ToolConfigurationDeserializer.Deserialize("DotnetToolsConfigMalformed.xml");
a.ShouldThrow<ToolConfigurationException>()
.And.Message.Should()
.Contain("Failed to retrive tool configuration exception, configuration is malformed xml");
}
[Fact]
public void GivenMissingContentItThrows()
{
Action a = () => ToolConfigurationDeserializer.Deserialize("DotnetToolsConfigMissing.xml");
a.ShouldThrow<ToolConfigurationException>()
.And.Message.Should()
.Contain("Configuration content error");
}
[Fact]
public void GivenInvalidCharAsFileNameItThrows()
{
Action a = () => new ToolConfiguration("na***me", "my.dll");
a.ShouldThrow<ArgumentException>()
.And.Message.Should()
.Contain("Cannot contain following character");
}
}
}

View file

@ -0,0 +1,208 @@
// 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 FluentAssertions;
using Microsoft.DotNet.Tools.Test.Utilities;
using Microsoft.Extensions.EnvironmentAbstractions;
using Microsoft.DotNet.Cli;
using Xunit;
namespace Microsoft.DotNet.ToolPackageObtainer.Tests
{
public class ToolPackageObtainerTests : TestBase
{
[Fact]
public void GivenNugetConfigAndPackageNameAndVersionAndTargetFrameworkWhenCallItCanDownloadThePackage()
{
FilePath nugetConfigPath = WriteNugetConfigFileToPointToTheFeed();
var toolsPath = Path.Combine(Directory.GetCurrentDirectory(), Path.GetRandomFileName());
var packageObtainer =
ConstructDefaultPackageObtainer(toolsPath);
ToolConfigurationAndExecutableDirectory toolConfigurationAndExecutableDirectory = packageObtainer.ObtainAndReturnExecutablePath(
packageId: TestPackageId,
packageVersion: TestPackageVersion,
nugetconfig: nugetConfigPath,
targetframework: _testTargetframework);
var executable = toolConfigurationAndExecutableDirectory
.ExecutableDirectory
.WithFile(
toolConfigurationAndExecutableDirectory
.Configuration
.ToolAssemblyEntryPoint);
File.Exists(executable.Value)
.Should()
.BeTrue(executable + " should have the executable");
}
[Fact]
public void GivenNugetConfigAndPackageNameAndVersionAndTargetFrameworkWhenCallItCreateAssetFile()
{
var nugetConfigPath = WriteNugetConfigFileToPointToTheFeed();
var toolsPath = Path.Combine(Directory.GetCurrentDirectory(), Path.GetRandomFileName());
var packageObtainer =
ConstructDefaultPackageObtainer(toolsPath);
ToolConfigurationAndExecutableDirectory toolConfigurationAndExecutableDirectory =
packageObtainer.ObtainAndReturnExecutablePath(
packageId: TestPackageId,
packageVersion: TestPackageVersion,
nugetconfig: nugetConfigPath,
targetframework: _testTargetframework);
var assetJsonPath = toolConfigurationAndExecutableDirectory
.ExecutableDirectory
.GetParentPath()
.GetParentPath()
.GetParentPath()
.GetParentPath()
.WithFile("project.assets.json").Value;
File.Exists(assetJsonPath)
.Should()
.BeTrue(assetJsonPath + " should be created");
}
[Fact]
public void GivenAllButNoNugetConfigFilePathItCanDownloadThePackage()
{
var uniqueTempProjectPath = GetUniqueTempProjectPathEachTest();
var tempProjectDirectory = uniqueTempProjectPath.GetDirectoryPath();
var nugetConfigPath = WriteNugetConfigFileToPointToTheFeed();
var toolsPath = Path.Combine(Directory.GetCurrentDirectory(), Path.GetRandomFileName());
Directory.CreateDirectory(tempProjectDirectory.Value);
/*
* No nuget config means you don't need nuget config passed in during call
* NuGet needs a way to find the package, in production, it will keep look up folders for Nuget.Config
* and use the feed there.
* In test, we don't want NuGet to keep look up, so we just copy paste beside the project.
*/
File.Copy(nugetConfigPath.Value,
tempProjectDirectory.WithFile("nuget.config").Value);
var packageObtainer =
new ToolPackageObtainer(
new DirectoryPath(toolsPath),
() => uniqueTempProjectPath,
new Lazy<string>(),
new PackageToProjectFileAdder(),
new ProjectRestorer());
ToolConfigurationAndExecutableDirectory toolConfigurationAndExecutableDirectory = packageObtainer.ObtainAndReturnExecutablePath(
packageId: TestPackageId,
packageVersion: TestPackageVersion,
targetframework: _testTargetframework);
var executable = toolConfigurationAndExecutableDirectory
.ExecutableDirectory
.WithFile(
toolConfigurationAndExecutableDirectory
.Configuration
.ToolAssemblyEntryPoint);
File.Exists(executable.Value)
.Should()
.BeTrue(executable + " should have the executable");
}
[Fact]
public void GivenAllButNoPackageVersionItCanDownloadThePackage()
{
var nugetConfigPath = WriteNugetConfigFileToPointToTheFeed();
var toolsPath = Path.Combine(Directory.GetCurrentDirectory(), Path.GetRandomFileName());
var packageObtainer =
ConstructDefaultPackageObtainer(toolsPath);
ToolConfigurationAndExecutableDirectory toolConfigurationAndExecutableDirectory = packageObtainer.ObtainAndReturnExecutablePath(
packageId: TestPackageId,
nugetconfig: nugetConfigPath,
targetframework: _testTargetframework);
var executable = toolConfigurationAndExecutableDirectory
.ExecutableDirectory
.WithFile(
toolConfigurationAndExecutableDirectory
.Configuration
.ToolAssemblyEntryPoint);
File.Exists(executable.Value)
.Should()
.BeTrue(executable + " should have the executable");
}
[Fact]
public void GivenAllButNoTargetFrameworkItCanDownloadThePackage()
{
var nugetConfigPath = WriteNugetConfigFileToPointToTheFeed();
var toolsPath = Path.Combine(Directory.GetCurrentDirectory(), Path.GetRandomFileName());
var packageObtainer =
new ToolPackageObtainer(
new DirectoryPath(toolsPath),
GetUniqueTempProjectPathEachTest,
new Lazy<string>(() => BundledTargetFramework.GetTargetFrameworkMoniker()),
new PackageToProjectFileAdder(),
new ProjectRestorer());
ToolConfigurationAndExecutableDirectory toolConfigurationAndExecutableDirectory =
packageObtainer.ObtainAndReturnExecutablePath(
packageId: TestPackageId,
packageVersion: TestPackageVersion,
nugetconfig: nugetConfigPath);
var executable = toolConfigurationAndExecutableDirectory
.ExecutableDirectory
.WithFile(
toolConfigurationAndExecutableDirectory
.Configuration
.ToolAssemblyEntryPoint);
File.Exists(executable.Value)
.Should()
.BeTrue(executable + " should have the executable");
}
private static readonly Func<FilePath> GetUniqueTempProjectPathEachTest = () =>
{
var tempProjectDirectory =
new DirectoryPath(Path.GetTempPath()).WithSubDirectories(Path.GetRandomFileName());
var tempProjectPath =
tempProjectDirectory.WithFile(Path.GetRandomFileName() + ".csproj");
return tempProjectPath;
};
private static ToolPackageObtainer ConstructDefaultPackageObtainer(string toolsPath)
{
return new ToolPackageObtainer(
new DirectoryPath(toolsPath),
GetUniqueTempProjectPathEachTest,
new Lazy<string>(),
new PackageToProjectFileAdder(),
new ProjectRestorer());
}
private static FilePath WriteNugetConfigFileToPointToTheFeed()
{
var nugetConfigName = Path.GetRandomFileName() + ".config";
var executeDirectory =
Path.GetDirectoryName(
System.Reflection
.Assembly
.GetExecutingAssembly()
.Location);
NuGetConfig.Write(
directory: executeDirectory,
configname: nugetConfigName,
localFeedPath: Path.Combine(executeDirectory, "TestAssetLocalNugetFeed"));
return new FilePath(Path.GetFullPath(nugetConfigName));
}
private readonly string _testTargetframework = BundledTargetFramework.GetTargetFrameworkMoniker();
private const string TestPackageVersion = "1.0.4";
private const string TestPackageId = "global.tool.console.demo";
}
}

View file

@ -23,5 +23,23 @@ namespace Microsoft.DotNet.Tools.Test.Utilities
File.WriteAllText(path, contents);
}
public static void Write(string directory, string configname, string localFeedPath)
{
const string template = @"<?xml version=""1.0"" encoding=""utf-8""?>
<configuration>
<packageSources>
<!--To inherit the global NuGet package sources remove the <clear/> line below -->
<clear />
<add key=""Test Source"" value=""{0}"" />
<add key=""api.nuget.org"" value=""https://api.nuget.org/v3/index.json"" />
<add key=""dotnet-core"" value=""https://dotnet.myget.org/F/dotnet-core/api/v3/index.json"" />
</packageSources>
</configuration>";
var path = Path.Combine(directory, configname);
File.WriteAllText(path, string.Format(template, localFeedPath));
}
}
}

View file

@ -0,0 +1,39 @@
// 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.DotNet.Cli;
using Microsoft.DotNet.Tools.Test.Utilities;
using Xunit;
using FluentAssertions;
using NuGet.Frameworks;
namespace Microsoft.DotNet.Tests
{
public class BundledTargetFrameworkTests : TestBase
{
[Fact]
public void VersionCommandDisplaysCorrectVersion()
{
var filePath = Path.Combine(
AppContext.BaseDirectory,
"ExpectedTargetFrameworkMoniker.txt");
var targetFrameworkMoniker = GetTargetFrameworkMonikerFromFile(filePath);
var shortFolderName = NuGetFramework
.Parse(targetFrameworkMoniker)
.GetShortFolderName();
BundledTargetFramework
.GetTargetFrameworkMoniker()
.Should().Be(shortFolderName);
}
private static string GetTargetFrameworkMonikerFromFile(string versionFilePath)
{
using (var reader = new StreamReader(File.OpenRead(versionFilePath)))
{
return reader.ReadLine();
}
}
}
}

View file

@ -52,6 +52,23 @@
</ItemGroup>
</Target>
<Target Name="WriteExpectedTargetFrameworkMoniker" BeforeTargets="BeforeBuild" DependsOnTargets="PrepareForBuild">
<PropertyGroup>
<ExpectedTargetFrameworkMonikerFileInIntermediateFolder>$(IntermediateOutputPath)ExpectedTargetFrameworkMoniker.txt</ExpectedTargetFrameworkMonikerFileInIntermediateFolder>
<VersionFileContent>$(TargetFrameworkMoniker)</VersionFileContent>
<ExistingVersionFileContent Condition=" Exists('$(ExpectedTargetFrameworkMonikerFileInIntermediateFolder)') ">
$([System.IO.File]::ReadAllText($(ExpectedTargetFrameworkMonikerFileInIntermediateFolder)))
</ExistingVersionFileContent>
<ShouldOverwriteVersionFile>false</ShouldOverwriteVersionFile>
<ShouldOverwriteVersionFile Condition=" '$(ExistingVersionFileContent.Trim())' != '$(VersionFileContent.Trim())' ">true</ShouldOverwriteVersionFile>
</PropertyGroup>
<WriteLinesToFile File="$(ExpectedTargetFrameworkMonikerFileInIntermediateFolder)" Lines="$(VersionFileContent)" Condition=" '$(ShouldOverwriteVersionFile)' == 'true' " Overwrite="true" />
<ItemGroup>
<Content Include="$(ExpectedTargetFrameworkMonikerFileInIntermediateFolder)" CopyToOutputDirectory="PreserveNewest" />
<FileWrites Include="$(ExpectedTargetFrameworkMonikerFileInIntermediateFolder)" />
</ItemGroup>
</Target>
<ItemGroup>
<ProjectReference Include="..\Microsoft.DotNet.Tools.Tests.Utilities\Microsoft.DotNet.Tools.Tests.Utilities.csproj" />
<ProjectReference Include="..\..\src\dotnet\dotnet.csproj" />