consume bring your own shim(byos) (#9018)
If there are shims packaged by convention in nupkg. Shim Repository will simply copy it to the right location. The query interface ToolPackageInstance will be in charge of finding the shim folder and filter the right RID. Shim Repository will pick the right file after the folder is located since Shim Repository knows the shim name and it also book keep the files at uninstallation. During development, due to the wrong adapter level. The mock duplicated too much logic. So, I corrected the abstraction level to lower (only create shim). And replaced the existing mock with a much smaller one without any atomic control and file move, copy logic. At the same time. The chmod, which is a IO action, causes problem during tests. So I added adapter layer to it and put it in Util.
This commit is contained in:
parent
98a1ee6a67
commit
b0ee5db411
47 changed files with 898 additions and 208 deletions
29
src/Microsoft.DotNet.Cli.Utils/CommandPermission.cs
Normal file
29
src/Microsoft.DotNet.Cli.Utils/CommandPermission.cs
Normal file
|
@ -0,0 +1,29 @@
|
|||
// 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.Runtime.InteropServices;
|
||||
|
||||
namespace Microsoft.DotNet.Cli.Utils
|
||||
{
|
||||
internal class FilePermissionSetter : IFilePermissionSetter
|
||||
{
|
||||
public void SetUserExecutionPermission(string path)
|
||||
{
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CommandResult result = new CommandFactory()
|
||||
.Create("chmod", new[] { "u+x", path })
|
||||
.CaptureStdOut()
|
||||
.CaptureStdErr()
|
||||
.Execute();
|
||||
|
||||
if (result.ExitCode != 0)
|
||||
{
|
||||
throw new FilePermissionSettingException(result.StdErr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Microsoft.DotNet.Cli.Utils
|
||||
{
|
||||
[Serializable]
|
||||
internal class FilePermissionSettingException : Exception
|
||||
{
|
||||
public FilePermissionSettingException()
|
||||
{
|
||||
}
|
||||
|
||||
public FilePermissionSettingException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public FilePermissionSettingException(string message, Exception innerException) : base(message, innerException)
|
||||
{
|
||||
}
|
||||
|
||||
protected FilePermissionSettingException(SerializationInfo info, StreamingContext context) : base(info, context)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
7
src/Microsoft.DotNet.Cli.Utils/IFilePermissionSetter.cs
Normal file
7
src/Microsoft.DotNet.Cli.Utils/IFilePermissionSetter.cs
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace Microsoft.DotNet.Cli.Utils
|
||||
{
|
||||
internal interface IFilePermissionSetter
|
||||
{
|
||||
void SetUserExecutionPermission(string path);
|
||||
}
|
||||
}
|
|
@ -51,6 +51,11 @@ namespace Microsoft.Extensions.EnvironmentAbstractions
|
|||
File.Move(source, destination);
|
||||
}
|
||||
|
||||
public void Copy(string sourceFileName, string destFileName)
|
||||
{
|
||||
File.Copy(sourceFileName, destFileName);
|
||||
}
|
||||
|
||||
public void Delete(string path)
|
||||
{
|
||||
File.Delete(path);
|
||||
|
|
|
@ -27,6 +27,8 @@ namespace Microsoft.Extensions.EnvironmentAbstractions
|
|||
|
||||
void Move(string source, string destination);
|
||||
|
||||
void Copy(string source, string destination);
|
||||
|
||||
void Delete(string path);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -640,4 +640,10 @@ setx PATH "%PATH%;{0}"
|
|||
<data name="FormatVersionIsMissing" xml:space="preserve">
|
||||
<value>Format version is missing. This tool may not be supported in this SDK version. Please contact the author of the tool.</value>
|
||||
</data>
|
||||
<data name="MoreThanOnePackagedShimAvailable" xml:space="preserve">
|
||||
<value>More than one packaged shim is available: {0}.</value>
|
||||
</data>
|
||||
<data name="FailedToReadNuGetLockFile" xml:space="preserve">
|
||||
<value>Failed to read NuGet LockFile for tool package '{0}': {1}</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
53
src/dotnet/ShellShim/AppHostShimMaker.cs
Normal file
53
src/dotnet/ShellShim/AppHostShimMaker.cs
Normal file
|
@ -0,0 +1,53 @@
|
|||
// 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;
|
||||
using System.Runtime.InteropServices;
|
||||
using Microsoft.DotNet.Cli.Utils;
|
||||
using Microsoft.DotNet.PlatformAbstractions;
|
||||
using Microsoft.DotNet.Tools.Common;
|
||||
using Microsoft.Extensions.EnvironmentAbstractions;
|
||||
|
||||
namespace Microsoft.DotNet.ShellShim
|
||||
{
|
||||
internal class AppHostShellShimMaker : IAppHostShellShimMaker
|
||||
{
|
||||
private const string ApphostNameWithoutExtension = "apphost";
|
||||
private readonly string _appHostSourceDirectory;
|
||||
private readonly IFilePermissionSetter _filePermissionSetter;
|
||||
|
||||
public AppHostShellShimMaker(string appHostSourceDirectory = null, IFilePermissionSetter filePermissionSetter = null)
|
||||
{
|
||||
_appHostSourceDirectory =
|
||||
appHostSourceDirectory
|
||||
?? Path.Combine(ApplicationEnvironment.ApplicationBasePath, "AppHostTemplate");
|
||||
|
||||
_filePermissionSetter =
|
||||
filePermissionSetter
|
||||
?? new FilePermissionSetter();
|
||||
}
|
||||
|
||||
public void CreateApphostShellShim(FilePath entryPoint, FilePath shimPath)
|
||||
{
|
||||
string appHostSourcePath;
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
appHostSourcePath = Path.Combine(_appHostSourceDirectory, ApphostNameWithoutExtension + ".exe");
|
||||
}
|
||||
else
|
||||
{
|
||||
appHostSourcePath = Path.Combine(_appHostSourceDirectory, ApphostNameWithoutExtension);
|
||||
}
|
||||
|
||||
var appHostDestinationFilePath = shimPath.Value;
|
||||
var appBinaryFilePath = PathUtility.GetRelativePath(appHostDestinationFilePath, entryPoint.Value);
|
||||
|
||||
EmbedAppNameInHost.EmbedAndReturnModifiedAppHostPath(
|
||||
appHostSourceFilePath: appHostSourcePath,
|
||||
appHostDestinationFilePath: appHostDestinationFilePath,
|
||||
appBinaryFilePath: appBinaryFilePath);
|
||||
|
||||
_filePermissionSetter.SetUserExecutionPermission(appHostDestinationFilePath);
|
||||
}
|
||||
}
|
||||
}
|
9
src/dotnet/ShellShim/IApphostShellShimMaker.cs
Normal file
9
src/dotnet/ShellShim/IApphostShellShimMaker.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
using Microsoft.Extensions.EnvironmentAbstractions;
|
||||
|
||||
namespace Microsoft.DotNet.ShellShim
|
||||
{
|
||||
internal interface IAppHostShellShimMaker
|
||||
{
|
||||
void CreateApphostShellShim(FilePath entryPoint, FilePath shimPath);
|
||||
}
|
||||
}
|
|
@ -1,12 +1,13 @@
|
|||
// 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.Collections.Generic;
|
||||
using Microsoft.Extensions.EnvironmentAbstractions;
|
||||
|
||||
namespace Microsoft.DotNet.ShellShim
|
||||
{
|
||||
internal interface IShellShimRepository
|
||||
{
|
||||
void CreateShim(FilePath targetExecutablePath, string commandName);
|
||||
void CreateShim(FilePath targetExecutablePath, string commandName, IReadOnlyList<FilePath> packagedShims = null);
|
||||
|
||||
void RemoveShim(string commandName);
|
||||
}
|
||||
|
|
|
@ -21,16 +21,24 @@ namespace Microsoft.DotNet.ShellShim
|
|||
private const string ApphostNameWithoutExtension = "apphost";
|
||||
|
||||
private readonly DirectoryPath _shimsDirectory;
|
||||
private readonly string _appHostSourceDirectory;
|
||||
private readonly IFileSystem _fileSystem;
|
||||
private readonly IAppHostShellShimMaker _appHostShellShimMaker;
|
||||
private readonly IFilePermissionSetter _filePermissionSetter;
|
||||
|
||||
public ShellShimRepository(DirectoryPath shimsDirectory, string appHostSourcePath = null)
|
||||
public ShellShimRepository(
|
||||
DirectoryPath shimsDirectory,
|
||||
string appHostSourceDirectory = null,
|
||||
IFileSystem fileSystem = null,
|
||||
IAppHostShellShimMaker appHostShellShimMaker = null,
|
||||
IFilePermissionSetter filePermissionSetter = null)
|
||||
{
|
||||
_shimsDirectory = shimsDirectory;
|
||||
_appHostSourceDirectory = appHostSourcePath ?? Path.Combine(ApplicationEnvironment.ApplicationBasePath,
|
||||
"AppHostTemplate");
|
||||
_fileSystem = fileSystem ?? new FileSystemWrapper();
|
||||
_appHostShellShimMaker = appHostShellShimMaker ?? new AppHostShellShimMaker(appHostSourceDirectory: appHostSourceDirectory);
|
||||
_filePermissionSetter = filePermissionSetter ?? new FilePermissionSetter();
|
||||
}
|
||||
|
||||
public void CreateShim(FilePath targetExecutablePath, string commandName)
|
||||
public void CreateShim(FilePath targetExecutablePath, string commandName, IReadOnlyList<FilePath> packagedShims = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(targetExecutablePath.Value))
|
||||
{
|
||||
|
@ -54,19 +62,27 @@ namespace Microsoft.DotNet.ShellShim
|
|||
{
|
||||
try
|
||||
{
|
||||
if (!Directory.Exists(_shimsDirectory.Value))
|
||||
if (!_fileSystem.Directory.Exists(_shimsDirectory.Value))
|
||||
{
|
||||
Directory.CreateDirectory(_shimsDirectory.Value);
|
||||
_fileSystem.Directory.CreateDirectory(_shimsDirectory.Value);
|
||||
}
|
||||
|
||||
CreateApphostShim(
|
||||
commandName,
|
||||
entryPoint: targetExecutablePath);
|
||||
|
||||
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
if (TryGetPackagedShim(packagedShims, commandName, out FilePath? packagedShim))
|
||||
{
|
||||
SetUserExecutionPermission(GetShimPath(commandName));
|
||||
_fileSystem.File.Copy(packagedShim.Value.Value, GetShimPath(commandName).Value);
|
||||
_filePermissionSetter.SetUserExecutionPermission(GetShimPath(commandName).Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
_appHostShellShimMaker.CreateApphostShellShim(
|
||||
targetExecutablePath,
|
||||
GetShimPath(commandName));
|
||||
}
|
||||
}
|
||||
catch (FilePermissionSettingException ex)
|
||||
{
|
||||
throw new ShellShimException(
|
||||
string.Format(CommonLocalizableStrings.FailedSettingShimPermissions, ex.Message));
|
||||
}
|
||||
catch (Exception ex) when (ex is UnauthorizedAccessException || ex is IOException)
|
||||
{
|
||||
|
@ -80,7 +96,7 @@ namespace Microsoft.DotNet.ShellShim
|
|||
}
|
||||
},
|
||||
rollback: () => {
|
||||
foreach (var file in GetShimFiles(commandName).Where(f => File.Exists(f.Value)))
|
||||
foreach (var file in GetShimFiles(commandName).Where(f => _fileSystem.File.Exists(f.Value)))
|
||||
{
|
||||
File.Delete(file.Value);
|
||||
}
|
||||
|
@ -94,10 +110,10 @@ namespace Microsoft.DotNet.ShellShim
|
|||
action: () => {
|
||||
try
|
||||
{
|
||||
foreach (var file in GetShimFiles(commandName).Where(f => File.Exists(f.Value)))
|
||||
foreach (var file in GetShimFiles(commandName).Where(f => _fileSystem.File.Exists(f.Value)))
|
||||
{
|
||||
var tempPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
|
||||
File.Move(file.Value, tempPath);
|
||||
_fileSystem.File.Move(file.Value, tempPath);
|
||||
files[file.Value] = tempPath;
|
||||
}
|
||||
}
|
||||
|
@ -115,38 +131,17 @@ namespace Microsoft.DotNet.ShellShim
|
|||
commit: () => {
|
||||
foreach (var value in files.Values)
|
||||
{
|
||||
File.Delete(value);
|
||||
_fileSystem.File.Delete(value);
|
||||
}
|
||||
},
|
||||
rollback: () => {
|
||||
foreach (var kvp in files)
|
||||
{
|
||||
File.Move(kvp.Value, kvp.Key);
|
||||
_fileSystem.File.Move(kvp.Value, kvp.Key);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void CreateApphostShim(string commandName, FilePath entryPoint)
|
||||
{
|
||||
string appHostSourcePath;
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
appHostSourcePath = Path.Combine(_appHostSourceDirectory, ApphostNameWithoutExtension + ".exe");
|
||||
}
|
||||
else
|
||||
{
|
||||
appHostSourcePath = Path.Combine(_appHostSourceDirectory, ApphostNameWithoutExtension);
|
||||
}
|
||||
|
||||
var appHostDestinationFilePath = GetShimPath(commandName).Value;
|
||||
var appBinaryFilePath = PathUtility.GetRelativePath(appHostDestinationFilePath, entryPoint.Value);
|
||||
|
||||
EmbedAppNameInHost.EmbedAndReturnModifiedAppHostPath(
|
||||
appHostSourceFilePath: appHostSourcePath,
|
||||
appHostDestinationFilePath: appHostDestinationFilePath,
|
||||
appBinaryFilePath: appBinaryFilePath);
|
||||
}
|
||||
|
||||
private class StartupOptions
|
||||
{
|
||||
public string appRoot { get; set; }
|
||||
|
@ -159,7 +154,7 @@ namespace Microsoft.DotNet.ShellShim
|
|||
|
||||
private bool ShimExists(string commandName)
|
||||
{
|
||||
return GetShimFiles(commandName).Any(p => File.Exists(p.Value));
|
||||
return GetShimFiles(commandName).Any(p => _fileSystem.File.Exists(p.Value));
|
||||
}
|
||||
|
||||
private IEnumerable<FilePath> GetShimFiles(string commandName)
|
||||
|
@ -184,24 +179,37 @@ namespace Microsoft.DotNet.ShellShim
|
|||
}
|
||||
}
|
||||
|
||||
private static void SetUserExecutionPermission(FilePath path)
|
||||
private bool TryGetPackagedShim(
|
||||
IReadOnlyList<FilePath> packagedShims,
|
||||
string commandName,
|
||||
out FilePath? packagedShim)
|
||||
{
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
packagedShim = null;
|
||||
|
||||
if (packagedShims != null && packagedShims.Count > 0)
|
||||
{
|
||||
return;
|
||||
FilePath[] candidatepackagedShim =
|
||||
packagedShims
|
||||
.Where(s => string.Equals(
|
||||
Path.GetFileName(s.Value),
|
||||
Path.GetFileName(GetShimPath(commandName).Value))).ToArray();
|
||||
|
||||
if (candidatepackagedShim.Length > 1)
|
||||
{
|
||||
throw new ShellShimException(
|
||||
string.Format(
|
||||
CommonLocalizableStrings.MoreThanOnePackagedShimAvailable,
|
||||
string.Join(';', candidatepackagedShim)));
|
||||
}
|
||||
|
||||
if (candidatepackagedShim.Length == 1)
|
||||
{
|
||||
packagedShim = candidatepackagedShim.Single();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
CommandResult result = new CommandFactory()
|
||||
.Create("chmod", new[] { "u+x", path.Value })
|
||||
.CaptureStdOut()
|
||||
.CaptureStdErr()
|
||||
.Execute();
|
||||
|
||||
if (result.ExitCode != 0)
|
||||
{
|
||||
throw new ShellShimException(
|
||||
string.Format(CommonLocalizableStrings.FailedSettingShimPermissions, result.StdErr));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,8 @@ namespace Microsoft.DotNet.ToolPackage
|
|||
|
||||
IEnumerable<string> Warnings { get; }
|
||||
|
||||
IReadOnlyList<FilePath> PackagedShims { get; }
|
||||
|
||||
void Uninstall();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NuGet.ProjectModel;
|
||||
|
||||
|
@ -22,36 +23,60 @@ namespace Microsoft.DotNet.ToolPackage
|
|||
string[] entryPointPathInArray = SplitPathByDirectorySeparator(targetRelativeFilePath);
|
||||
|
||||
return entryPointPathInArray.Length >= 1
|
||||
&& PathInLockFileDirectoriesStartWithToolsAndFollowsTwoSubFolder()
|
||||
&& SubPathMatchesTargetFilePath();
|
||||
&& PathInLockFileDirectoriesStartWithToolsAndFollowsTwoSubFolder(
|
||||
pathInLockFilePathInArray,
|
||||
entryPointPathInArray)
|
||||
&& SubPathMatchesTargetFilePath(pathInLockFilePathInArray, entryPointPathInArray);
|
||||
}
|
||||
|
||||
bool SubPathMatchesTargetFilePath()
|
||||
/// <summary>
|
||||
/// Check if LockFileItem is under targetRelativePath directory.
|
||||
/// The path in LockFileItem is in pattern tools/TFM/RID/my/tool.dll. Tools/TFM/RID is selected by NuGet.
|
||||
/// And there will be only one TFM/RID combination.
|
||||
/// When "my/folder/of/tool/tools.dll" part under targetRelativePath "my/folder/of" or "my/folder", return true.
|
||||
/// </summary>
|
||||
internal static bool MatchesDirectoryPath(LockFileItem lockFileItem, string targetRelativePath)
|
||||
{
|
||||
string[] pathInLockFilePathInArray = SplitPathByDirectorySeparator(lockFileItem.Path);
|
||||
string[] targetDirectoryPathInArray = SplitPathByDirectorySeparator(targetRelativePath);
|
||||
|
||||
return pathInLockFilePathInArray[0] == "tools"
|
||||
&& SubPathMatchesTargetFilePath(pathInLockFilePathInArray, targetDirectoryPathInArray);
|
||||
}
|
||||
|
||||
private static bool SubPathMatchesTargetFilePath(string[] pathInLockFilePathInArray, string[] targetInArray)
|
||||
{
|
||||
string[] pathAfterToolsTfmRid = pathInLockFilePathInArray.Skip(3).ToArray();
|
||||
return !targetInArray
|
||||
.Where((directoryOnEveryLevel, i) => directoryOnEveryLevel != pathAfterToolsTfmRid[i])
|
||||
.Any();
|
||||
}
|
||||
|
||||
private static bool PathInLockFileDirectoriesStartWithToolsAndFollowsTwoSubFolder(
|
||||
string[] pathInLockFilePathInArray,
|
||||
string[] targetInArray)
|
||||
{
|
||||
if (pathInLockFilePathInArray.Length - targetInArray.Length != 3)
|
||||
{
|
||||
string[] pathAfterToolsTfmRid = pathInLockFilePathInArray.Skip(3).ToArray();
|
||||
return !pathAfterToolsTfmRid
|
||||
.Where((directoryOnEveryLevel, i) => directoryOnEveryLevel != entryPointPathInArray[i])
|
||||
.Any();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PathInLockFileDirectoriesStartWithToolsAndFollowsTwoSubFolder()
|
||||
if (pathInLockFilePathInArray[0] != "tools")
|
||||
{
|
||||
if (pathInLockFilePathInArray.Length - entryPointPathInArray.Length != 3)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pathInLockFilePathInArray[0] != "tools")
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
string[] SplitPathByDirectorySeparator(string path)
|
||||
return true;
|
||||
}
|
||||
|
||||
private static string[] SplitPathByDirectorySeparator(string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
return path.Split('\\', '/');
|
||||
return new string[0];
|
||||
}
|
||||
|
||||
return path.Split('\\', '/');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,13 +7,17 @@ using Microsoft.DotNet.Tools;
|
|||
using Microsoft.Extensions.EnvironmentAbstractions;
|
||||
using NuGet.ProjectModel;
|
||||
using NuGet.Versioning;
|
||||
using Microsoft.DotNet.Cli.Utils;
|
||||
|
||||
namespace Microsoft.DotNet.ToolPackage
|
||||
{
|
||||
// This is named "ToolPackageInstance" because "ToolPackage" would conflict with the namespace
|
||||
internal class ToolPackageInstance : IToolPackage
|
||||
{
|
||||
private const string PackagedShimsDirectoryConvention = "shims";
|
||||
|
||||
public IEnumerable<string> Warnings => _toolConfiguration.Value.Warnings;
|
||||
|
||||
public PackageId Id { get; private set; }
|
||||
|
||||
public NuGetVersion Version { get; private set; }
|
||||
|
@ -28,12 +32,21 @@ namespace Microsoft.DotNet.ToolPackage
|
|||
}
|
||||
}
|
||||
|
||||
public IReadOnlyList<FilePath> PackagedShims
|
||||
{
|
||||
get
|
||||
{
|
||||
return _packagedShims.Value;
|
||||
}
|
||||
}
|
||||
|
||||
private const string AssetsFileName = "project.assets.json";
|
||||
private const string ToolSettingsFileName = "DotnetToolSettings.xml";
|
||||
|
||||
private IToolPackageStore _store;
|
||||
private Lazy<IReadOnlyList<CommandSettings>> _commands;
|
||||
private Lazy<ToolConfiguration> _toolConfiguration;
|
||||
private Lazy<IReadOnlyList<FilePath>> _packagedShims;
|
||||
|
||||
public ToolPackageInstance(
|
||||
IToolPackageStore store,
|
||||
|
@ -43,6 +56,7 @@ namespace Microsoft.DotNet.ToolPackage
|
|||
{
|
||||
_store = store ?? throw new ArgumentNullException(nameof(store));
|
||||
_commands = new Lazy<IReadOnlyList<CommandSettings>>(GetCommands);
|
||||
_packagedShims = new Lazy<IReadOnlyList<FilePath>>(GetPackagedShims);
|
||||
|
||||
Id = id;
|
||||
Version = version ?? throw new ArgumentNullException(nameof(version));
|
||||
|
@ -107,11 +121,11 @@ namespace Microsoft.DotNet.ToolPackage
|
|||
try
|
||||
{
|
||||
var commands = new List<CommandSettings>();
|
||||
var lockFile = new LockFileFormat().Read(PackageDirectory.WithFile(AssetsFileName).Value);
|
||||
var library = FindLibraryInLockFile(lockFile);
|
||||
LockFile lockFile = new LockFileFormat().Read(PackageDirectory.WithFile(AssetsFileName).Value);
|
||||
LockFileTargetLibrary library = FindLibraryInLockFile(lockFile);
|
||||
|
||||
ToolConfiguration configuration = _toolConfiguration.Value;
|
||||
var entryPointFromLockFile = FindItemInTargetLibrary(library, configuration.ToolAssemblyEntryPoint);
|
||||
LockFileItem entryPointFromLockFile = FindItemInTargetLibrary(library, configuration.ToolAssemblyEntryPoint);
|
||||
if (entryPointFromLockFile == null)
|
||||
{
|
||||
throw new ToolConfigurationException(
|
||||
|
@ -125,11 +139,7 @@ namespace Microsoft.DotNet.ToolPackage
|
|||
commands.Add(new CommandSettings(
|
||||
configuration.CommandName,
|
||||
"dotnet",
|
||||
PackageDirectory
|
||||
.WithSubDirectories(
|
||||
Id.ToString(),
|
||||
library.Version.ToNormalizedString())
|
||||
.WithFile(entryPointFromLockFile.Path)));
|
||||
LockFileRelativePathToFullFilePath(entryPointFromLockFile.Path, library)));
|
||||
|
||||
return commands;
|
||||
}
|
||||
|
@ -143,6 +153,15 @@ namespace Microsoft.DotNet.ToolPackage
|
|||
}
|
||||
}
|
||||
|
||||
private FilePath LockFileRelativePathToFullFilePath(string lockFileRelativePath, LockFileTargetLibrary library)
|
||||
{
|
||||
return PackageDirectory
|
||||
.WithSubDirectories(
|
||||
Id.ToString(),
|
||||
library.Version.ToNormalizedString())
|
||||
.WithFile(lockFileRelativePath);
|
||||
}
|
||||
|
||||
private ToolConfiguration GetToolConfiguration()
|
||||
{
|
||||
try
|
||||
|
@ -161,6 +180,55 @@ namespace Microsoft.DotNet.ToolPackage
|
|||
}
|
||||
}
|
||||
|
||||
private IReadOnlyList<FilePath> GetPackagedShims()
|
||||
{
|
||||
LockFileTargetLibrary library;
|
||||
try
|
||||
{
|
||||
LockFile lockFile = new LockFileFormat().Read(PackageDirectory.WithFile(AssetsFileName).Value);
|
||||
library = FindLibraryInLockFile(lockFile);
|
||||
}
|
||||
catch (Exception ex) when (ex is UnauthorizedAccessException || ex is IOException)
|
||||
{
|
||||
throw new ToolPackageException(
|
||||
string.Format(
|
||||
CommonLocalizableStrings.FailedToReadNuGetLockFile,
|
||||
Id,
|
||||
ex.Message),
|
||||
ex);
|
||||
}
|
||||
|
||||
IEnumerable<LockFileItem> filesUnderShimsDirectory = library
|
||||
?.ToolsAssemblies
|
||||
?.Where(t => LockFileMatcher.MatchesDirectoryPath(t, PackagedShimsDirectoryConvention));
|
||||
|
||||
if (filesUnderShimsDirectory == null)
|
||||
{
|
||||
return Array.Empty<FilePath>();
|
||||
}
|
||||
|
||||
IEnumerable<string> allAvailableShimRuntimeIdentifiers = filesUnderShimsDirectory
|
||||
.Select(f => f.Path.Split('\\', '/')?[4]) // ex: "tools/netcoreapp2.1/any/shims/osx-x64/demo" osx-x64 is at [4]
|
||||
.Where(f => !string.IsNullOrEmpty(f));
|
||||
|
||||
if (new FrameworkDependencyFile().TryGetMostFitRuntimeIdentifier(
|
||||
DotnetFiles.VersionFileObject.BuildRid,
|
||||
allAvailableShimRuntimeIdentifiers.ToArray(),
|
||||
out var mostFitRuntimeIdentifier))
|
||||
{
|
||||
return library
|
||||
?.ToolsAssemblies
|
||||
?.Where(l =>
|
||||
LockFileMatcher.MatchesDirectoryPath(l, $"{PackagedShimsDirectoryConvention}/{mostFitRuntimeIdentifier}"))
|
||||
.Select(l => LockFileRelativePathToFullFilePath(l.Path, library)).ToArray()
|
||||
?? Array.Empty<FilePath>();
|
||||
}
|
||||
else
|
||||
{
|
||||
return Array.Empty<FilePath>();
|
||||
}
|
||||
}
|
||||
|
||||
private ToolConfiguration DeserializeToolConfiguration(string ToolSettingsFileName, LockFileTargetLibrary library)
|
||||
{
|
||||
var dotnetToolSettings = FindItemInTargetLibrary(library, ToolSettingsFileName);
|
||||
|
|
|
@ -142,7 +142,7 @@ namespace Microsoft.DotNet.Tools.Tool.Install
|
|||
|
||||
foreach (var command in package.Commands)
|
||||
{
|
||||
shellShimRepository.CreateShim(command.Executable, command.Name);
|
||||
shellShimRepository.CreateShim(command.Executable, command.Name, package.PackagedShims);
|
||||
}
|
||||
|
||||
scope.Complete();
|
||||
|
|
|
@ -883,6 +883,16 @@ setx PATH "%PATH%;{0}"
|
|||
<target state="new">Failed to add '{0}' to the PATH environment variable. Please add this directory to your PATH to use tools installed with 'dotnet tool install'.</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="MoreThanOnePackagedShimAvailable">
|
||||
<source>More than one packaged shim is available: {0}.</source>
|
||||
<target state="new">More than one packaged shim is available: {0}.</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="FailedToReadNuGetLockFile">
|
||||
<source>Failed to read NuGet LockFile for tool package '{0}': {1}</source>
|
||||
<target state="new">Failed to read NuGet LockFile for tool package '{0}': {1}</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
|
@ -883,6 +883,16 @@ setx PATH "%PATH%;{0}"
|
|||
<target state="new">Failed to add '{0}' to the PATH environment variable. Please add this directory to your PATH to use tools installed with 'dotnet tool install'.</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="MoreThanOnePackagedShimAvailable">
|
||||
<source>More than one packaged shim is available: {0}.</source>
|
||||
<target state="new">More than one packaged shim is available: {0}.</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="FailedToReadNuGetLockFile">
|
||||
<source>Failed to read NuGet LockFile for tool package '{0}': {1}</source>
|
||||
<target state="new">Failed to read NuGet LockFile for tool package '{0}': {1}</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
|
@ -883,6 +883,16 @@ setx PATH "%PATH%;{0}"
|
|||
<target state="new">Failed to add '{0}' to the PATH environment variable. Please add this directory to your PATH to use tools installed with 'dotnet tool install'.</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="MoreThanOnePackagedShimAvailable">
|
||||
<source>More than one packaged shim is available: {0}.</source>
|
||||
<target state="new">More than one packaged shim is available: {0}.</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="FailedToReadNuGetLockFile">
|
||||
<source>Failed to read NuGet LockFile for tool package '{0}': {1}</source>
|
||||
<target state="new">Failed to read NuGet LockFile for tool package '{0}': {1}</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
|
@ -883,6 +883,16 @@ setx PATH "%PATH%;{0}"
|
|||
<target state="new">Failed to add '{0}' to the PATH environment variable. Please add this directory to your PATH to use tools installed with 'dotnet tool install'.</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="MoreThanOnePackagedShimAvailable">
|
||||
<source>More than one packaged shim is available: {0}.</source>
|
||||
<target state="new">More than one packaged shim is available: {0}.</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="FailedToReadNuGetLockFile">
|
||||
<source>Failed to read NuGet LockFile for tool package '{0}': {1}</source>
|
||||
<target state="new">Failed to read NuGet LockFile for tool package '{0}': {1}</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
|
@ -883,6 +883,16 @@ setx PATH "%PATH%;{0}"
|
|||
<target state="new">Failed to add '{0}' to the PATH environment variable. Please add this directory to your PATH to use tools installed with 'dotnet tool install'.</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="MoreThanOnePackagedShimAvailable">
|
||||
<source>More than one packaged shim is available: {0}.</source>
|
||||
<target state="new">More than one packaged shim is available: {0}.</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="FailedToReadNuGetLockFile">
|
||||
<source>Failed to read NuGet LockFile for tool package '{0}': {1}</source>
|
||||
<target state="new">Failed to read NuGet LockFile for tool package '{0}': {1}</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
|
@ -883,6 +883,16 @@ setx PATH "%PATH%;{0}"
|
|||
<target state="new">Failed to add '{0}' to the PATH environment variable. Please add this directory to your PATH to use tools installed with 'dotnet tool install'.</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="MoreThanOnePackagedShimAvailable">
|
||||
<source>More than one packaged shim is available: {0}.</source>
|
||||
<target state="new">More than one packaged shim is available: {0}.</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="FailedToReadNuGetLockFile">
|
||||
<source>Failed to read NuGet LockFile for tool package '{0}': {1}</source>
|
||||
<target state="new">Failed to read NuGet LockFile for tool package '{0}': {1}</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
|
@ -883,6 +883,16 @@ setx PATH "%PATH%;{0}"
|
|||
<target state="new">Failed to add '{0}' to the PATH environment variable. Please add this directory to your PATH to use tools installed with 'dotnet tool install'.</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="MoreThanOnePackagedShimAvailable">
|
||||
<source>More than one packaged shim is available: {0}.</source>
|
||||
<target state="new">More than one packaged shim is available: {0}.</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="FailedToReadNuGetLockFile">
|
||||
<source>Failed to read NuGet LockFile for tool package '{0}': {1}</source>
|
||||
<target state="new">Failed to read NuGet LockFile for tool package '{0}': {1}</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
|
@ -883,6 +883,16 @@ setx PATH "%PATH%;{0}"
|
|||
<target state="new">Failed to add '{0}' to the PATH environment variable. Please add this directory to your PATH to use tools installed with 'dotnet tool install'.</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="MoreThanOnePackagedShimAvailable">
|
||||
<source>More than one packaged shim is available: {0}.</source>
|
||||
<target state="new">More than one packaged shim is available: {0}.</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="FailedToReadNuGetLockFile">
|
||||
<source>Failed to read NuGet LockFile for tool package '{0}': {1}</source>
|
||||
<target state="new">Failed to read NuGet LockFile for tool package '{0}': {1}</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
|
@ -883,6 +883,16 @@ setx PATH "%PATH%;{0}"
|
|||
<target state="new">Failed to add '{0}' to the PATH environment variable. Please add this directory to your PATH to use tools installed with 'dotnet tool install'.</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="MoreThanOnePackagedShimAvailable">
|
||||
<source>More than one packaged shim is available: {0}.</source>
|
||||
<target state="new">More than one packaged shim is available: {0}.</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="FailedToReadNuGetLockFile">
|
||||
<source>Failed to read NuGet LockFile for tool package '{0}': {1}</source>
|
||||
<target state="new">Failed to read NuGet LockFile for tool package '{0}': {1}</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
|
@ -883,6 +883,16 @@ setx PATH "%PATH%;{0}"
|
|||
<target state="new">Failed to add '{0}' to the PATH environment variable. Please add this directory to your PATH to use tools installed with 'dotnet tool install'.</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="MoreThanOnePackagedShimAvailable">
|
||||
<source>More than one packaged shim is available: {0}.</source>
|
||||
<target state="new">More than one packaged shim is available: {0}.</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="FailedToReadNuGetLockFile">
|
||||
<source>Failed to read NuGet LockFile for tool package '{0}': {1}</source>
|
||||
<target state="new">Failed to read NuGet LockFile for tool package '{0}': {1}</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
|
@ -883,6 +883,16 @@ setx PATH "%PATH%;{0}"
|
|||
<target state="new">Failed to add '{0}' to the PATH environment variable. Please add this directory to your PATH to use tools installed with 'dotnet tool install'.</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="MoreThanOnePackagedShimAvailable">
|
||||
<source>More than one packaged shim is available: {0}.</source>
|
||||
<target state="new">More than one packaged shim is available: {0}.</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="FailedToReadNuGetLockFile">
|
||||
<source>Failed to read NuGet LockFile for tool package '{0}': {1}</source>
|
||||
<target state="new">Failed to read NuGet LockFile for tool package '{0}': {1}</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
|
@ -883,6 +883,16 @@ setx PATH "%PATH%;{0}"
|
|||
<target state="new">Failed to add '{0}' to the PATH environment variable. Please add this directory to your PATH to use tools installed with 'dotnet tool install'.</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="MoreThanOnePackagedShimAvailable">
|
||||
<source>More than one packaged shim is available: {0}.</source>
|
||||
<target state="new">More than one packaged shim is available: {0}.</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="FailedToReadNuGetLockFile">
|
||||
<source>Failed to read NuGet LockFile for tool package '{0}': {1}</source>
|
||||
<target state="new">Failed to read NuGet LockFile for tool package '{0}': {1}</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
|
@ -883,6 +883,16 @@ setx PATH "%PATH%;{0}"
|
|||
<target state="new">Failed to add '{0}' to the PATH environment variable. Please add this directory to your PATH to use tools installed with 'dotnet tool install'.</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="MoreThanOnePackagedShimAvailable">
|
||||
<source>More than one packaged shim is available: {0}.</source>
|
||||
<target state="new">More than one packaged shim is available: {0}.</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="FailedToReadNuGetLockFile">
|
||||
<source>Failed to read NuGet LockFile for tool package '{0}': {1}</source>
|
||||
<target state="new">Failed to read NuGet LockFile for tool package '{0}': {1}</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
|
@ -134,6 +134,11 @@ namespace Microsoft.DotNet.Configurer.UnitTests
|
|||
{
|
||||
throw new UnauthorizedAccessException();
|
||||
}
|
||||
|
||||
public void Copy(string source, string destination)
|
||||
{
|
||||
throw new UnauthorizedAccessException();
|
||||
}
|
||||
}
|
||||
|
||||
private class NoPermissionDirectoryFake : IDirectory
|
||||
|
|
|
@ -286,6 +286,11 @@ namespace Microsoft.DotNet.Configurer.UnitTests
|
|||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Copy(string source, string destination)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
private class MockStream : MemoryStream
|
||||
|
|
|
@ -119,7 +119,7 @@ namespace Microsoft.DotNet.ShellShim.Tests
|
|||
IShellShimRepository shellShimRepository;
|
||||
if (testMockBehaviorIsInSync)
|
||||
{
|
||||
shellShimRepository = new ShellShimRepositoryMock(new DirectoryPath(pathToShim));
|
||||
shellShimRepository = GetShellShimRepositoryWithMockMaker(pathToShim);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -161,7 +161,7 @@ namespace Microsoft.DotNet.ShellShim.Tests
|
|||
IShellShimRepository shellShimRepository;
|
||||
if (testMockBehaviorIsInSync)
|
||||
{
|
||||
shellShimRepository = new ShellShimRepositoryMock(new DirectoryPath(pathToShim));
|
||||
shellShimRepository = GetShellShimRepositoryWithMockMaker(pathToShim);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -198,7 +198,7 @@ namespace Microsoft.DotNet.ShellShim.Tests
|
|||
IShellShimRepository shellShimRepository;
|
||||
if (testMockBehaviorIsInSync)
|
||||
{
|
||||
shellShimRepository = new ShellShimRepositoryMock(new DirectoryPath(pathToShim));
|
||||
shellShimRepository = GetShellShimRepositoryWithMockMaker(pathToShim);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -223,7 +223,7 @@ namespace Microsoft.DotNet.ShellShim.Tests
|
|||
IShellShimRepository shellShimRepository;
|
||||
if (testMockBehaviorIsInSync)
|
||||
{
|
||||
shellShimRepository = new ShellShimRepositoryMock(new DirectoryPath(pathToShim));
|
||||
shellShimRepository = GetShellShimRepositoryWithMockMaker(pathToShim);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -252,7 +252,7 @@ namespace Microsoft.DotNet.ShellShim.Tests
|
|||
IShellShimRepository shellShimRepository;
|
||||
if (testMockBehaviorIsInSync)
|
||||
{
|
||||
shellShimRepository = new ShellShimRepositoryMock(new DirectoryPath(pathToShim));
|
||||
shellShimRepository = GetShellShimRepositoryWithMockMaker(pathToShim);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -288,7 +288,7 @@ namespace Microsoft.DotNet.ShellShim.Tests
|
|||
IShellShimRepository shellShimRepository;
|
||||
if (testMockBehaviorIsInSync)
|
||||
{
|
||||
shellShimRepository = new ShellShimRepositoryMock(new DirectoryPath(pathToShim));
|
||||
shellShimRepository = GetShellShimRepositoryWithMockMaker(pathToShim);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -315,6 +315,67 @@ namespace Microsoft.DotNet.ShellShim.Tests
|
|||
Directory.EnumerateFileSystemEntries(pathToShim).Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WhenPackagedShimProvidedItCopies()
|
||||
{
|
||||
const string tokenToIdentifyCopiedShim = "packagedShim";
|
||||
|
||||
var shellCommandName = nameof(ShellShimRepositoryTests) + Path.GetRandomFileName();
|
||||
var pathToShim = GetNewCleanFolderUnderTempRoot();
|
||||
var packagedShimFolder = GetNewCleanFolderUnderTempRoot();
|
||||
var dummyShimPath = Path.Combine(packagedShimFolder, shellCommandName);
|
||||
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
dummyShimPath = dummyShimPath + ".exe";
|
||||
}
|
||||
|
||||
File.WriteAllText(dummyShimPath, tokenToIdentifyCopiedShim);
|
||||
|
||||
ShellShimRepository shellShimRepository = GetShellShimRepositoryWithMockMaker(pathToShim);
|
||||
|
||||
shellShimRepository.CreateShim(
|
||||
new FilePath("dummy.dll"),
|
||||
shellCommandName,
|
||||
new[] {new FilePath(dummyShimPath)});
|
||||
|
||||
var createdShim = Directory.EnumerateFileSystemEntries(pathToShim).Single();
|
||||
File.ReadAllText(createdShim).Should().Contain(tokenToIdentifyCopiedShim);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WhenMutipleSameNamePackagedShimProvidedItThrows()
|
||||
{
|
||||
const string tokenToIdentifyCopiedShim = "packagedShim";
|
||||
|
||||
var shellCommandName = nameof(ShellShimRepositoryTests) + Path.GetRandomFileName();
|
||||
var pathToShim = GetNewCleanFolderUnderTempRoot();
|
||||
var packagedShimFolder = GetNewCleanFolderUnderTempRoot();
|
||||
var dummyShimPath = Path.Combine(packagedShimFolder, shellCommandName);
|
||||
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
dummyShimPath = dummyShimPath + ".exe";
|
||||
}
|
||||
|
||||
File.WriteAllText(dummyShimPath, tokenToIdentifyCopiedShim);
|
||||
ShellShimRepository shellShimRepository = GetShellShimRepositoryWithMockMaker(pathToShim);
|
||||
|
||||
FilePath[] filePaths = new[] { new FilePath(dummyShimPath), new FilePath("path" + dummyShimPath) };
|
||||
|
||||
Action a = () => shellShimRepository.CreateShim(
|
||||
new FilePath("dummy.dll"),
|
||||
shellCommandName,
|
||||
new[] { new FilePath(dummyShimPath), new FilePath("path" + dummyShimPath) });
|
||||
|
||||
a.ShouldThrow<ShellShimException>()
|
||||
.And.Message
|
||||
.Should().Contain(
|
||||
string.Format(
|
||||
CommonLocalizableStrings.MoreThanOnePackagedShimAvailable,
|
||||
string.Join(';', filePaths)));
|
||||
}
|
||||
|
||||
private static void MakeNameConflictingCommand(string pathToPlaceShim, string shellCommandName)
|
||||
{
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
|
@ -440,5 +501,12 @@ namespace Microsoft.DotNet.ShellShim.Tests
|
|||
|
||||
return CleanFolderUnderTempRoot.FullName;
|
||||
}
|
||||
|
||||
private ShellShimRepository GetShellShimRepositoryWithMockMaker(string pathToShim)
|
||||
{
|
||||
return new ShellShimRepository(
|
||||
new DirectoryPath(pathToShim),
|
||||
appHostShellShimMaker: new AppHostShellShimMakerMock());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ namespace Microsoft.DotNet.ToolPackage.Tests
|
|||
{
|
||||
public class LockFileMatcherTests : TestBase
|
||||
{
|
||||
|
||||
[Theory]
|
||||
[InlineData("tools/netcoreapp1.1/any/tool.dll", "tool.dll", true)]
|
||||
[InlineData(@"tools\netcoreapp1.1\any\subDirectory\tool.dll", "subDirectory/tool.dll", true)]
|
||||
|
@ -24,5 +23,20 @@ namespace Microsoft.DotNet.ToolPackage.Tests
|
|||
LockFileMatcher.MatchesFile(new LockFileItem(pathInLockFileItem), targetRelativeFilePath)
|
||||
.Should().Be(shouldMatch);
|
||||
}
|
||||
|
||||
|
||||
[Theory]
|
||||
[InlineData("tools/netcoreapp1.1/any/tool.dll", "", true)]
|
||||
[InlineData(@"tools\netcoreapp1.1\any\subDirectory\tool.dll", "subDirectory", true)]
|
||||
[InlineData(@"tools\netcoreapp1.1\any\subDirectory\tool.dll", "sub", false)]
|
||||
[InlineData("tools/netcoreapp1.1/any/subDirectory/tool.dll", "any/subDirectory", false)]
|
||||
public void MatchesDirectoryPathTests(
|
||||
string pathInLockFileItem,
|
||||
string targetRelativeFilePath,
|
||||
bool shouldMatch)
|
||||
{
|
||||
LockFileMatcher.MatchesDirectoryPath(new LockFileItem(pathInLockFileItem), targetRelativeFilePath)
|
||||
.Should().Be(shouldMatch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,6 +45,11 @@
|
|||
<Content Remove="SampleGlobalTool/**" />
|
||||
<EmbeddedResource Remove="SampleGlobalTool/**" />
|
||||
<None Remove="SampleGlobalTool/**" />
|
||||
|
||||
<Compile Remove="SampleGlobalToolWithShim/**" />
|
||||
<Content Remove="SampleGlobalToolWithShim/**" />
|
||||
<EmbeddedResource Remove="SampleGlobalToolWithShim/**" />
|
||||
<None Remove="SampleGlobalToolWithShim/**" />
|
||||
</ItemGroup>
|
||||
|
||||
<UsingTask TaskName="DownloadFile" AssemblyFile="$(CLIBuildDll)" />
|
||||
|
@ -59,4 +64,17 @@
|
|||
<MSBuild BuildInParallel="False" Projects="SampleGlobalTool/consoledemo.csproj" Targets="pack" Properties="Configuration=Release;NuspecFile=includepublish.nuspec;NuspecBasePath=$(testAssetSourceRoot);PackageOutputPath=$(OutputPath)/TestAssetLocalNugetFeed">
|
||||
</MSBuild>
|
||||
</Target>
|
||||
|
||||
<Target Name="CreateNupkgWithShimFromSource" BeforeTargets="Build">
|
||||
<PropertyGroup>
|
||||
<testAssetSourceRoot>$(BaseOutputPath)/TestAsset/SampleGlobalToolWithShim</testAssetSourceRoot>
|
||||
</PropertyGroup>
|
||||
<Copy SourceFiles="SampleGlobalToolWithShim/DotnetToolSettings.xml" DestinationFolder="$(testAssetSourceRoot)" />
|
||||
<Copy SourceFiles="SampleGlobalToolWithShim/dummyshim" DestinationFolder="$(testAssetSourceRoot)" />
|
||||
<Copy SourceFiles="SampleGlobalToolWithShim/dummyshim.exe" DestinationFolder="$(testAssetSourceRoot)" />
|
||||
<MSBuild BuildInParallel="False" Projects="SampleGlobalToolWithShim/consoledemo.csproj" Targets="Restore;Build;Publish" Properties="Configuration=Release;BaseOutputPath=$(testAssetSourceRoot)/bin/">
|
||||
</MSBuild>
|
||||
<MSBuild BuildInParallel="False" Projects="SampleGlobalToolWithShim/consoledemo.csproj" Targets="pack" Properties="Configuration=Release;NuspecFile=includepublish.nuspec;NuspecBasePath=$(testAssetSourceRoot);PackageOutputPath=$(OutputPath)/TestAssetLocalNugetFeed">
|
||||
</MSBuild>
|
||||
</Target>
|
||||
</Project>
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<DotNetCliTool Version="1" >
|
||||
<Commands>
|
||||
<Command Name="demo" EntryPoint="consoledemo.dll" Runner="dotnet" />
|
||||
</Commands>
|
||||
</DotNetCliTool>
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<AssemblyName>consoledemo</AssemblyName>
|
||||
</PropertyGroup>
|
||||
</Project>
|
|
@ -0,0 +1 @@
|
|||
packagedshim
|
|
@ -0,0 +1 @@
|
|||
packagedshim
|
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>global.tool.console.demo.with.shim</id>
|
||||
<version>1.0.4</version>
|
||||
<description>test app</description>
|
||||
<authors>testauthor</authors>
|
||||
<packageTypes>
|
||||
<packageType name="DotnetTool" />
|
||||
</packageTypes>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="bin\Release\netcoreapp2.1\publish\*.*" target="tools\netcoreapp2.1\any\" />
|
||||
<file src="DotnetToolSettings.xml" target="tools\netcoreapp2.1\any\DotnetToolSettings.xml" />
|
||||
<file src="dummyshim.exe" target="tools\netcoreapp2.1\any\shims\win-x64\demo.exe" />
|
||||
<file src="dummyshim.exe" target="tools\netcoreapp2.1\any\shims\win-x86\demo.exe" />
|
||||
<file src="dummyshim" target="tools\netcoreapp2.1\any\shims\osx-x64\demo" />
|
||||
<file src="dummyshim" target="tools\netcoreapp2.1\any\shims\linux\demo" />
|
||||
</files>
|
||||
</package>
|
|
@ -0,0 +1,130 @@
|
|||
// 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 System.Reflection;
|
||||
using System.Transactions;
|
||||
using FluentAssertions;
|
||||
using Microsoft.DotNet.Tools.Test.Utilities;
|
||||
using Microsoft.DotNet.Cli;
|
||||
using Microsoft.DotNet.Cli.Utils;
|
||||
using Microsoft.DotNet.Tools;
|
||||
using Microsoft.DotNet.Tools.Tool.Install;
|
||||
using Microsoft.DotNet.Tools.Tests.ComponentMocks;
|
||||
using Microsoft.Extensions.DependencyModel.Tests;
|
||||
using Microsoft.Extensions.EnvironmentAbstractions;
|
||||
using NuGet.Versioning;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.DotNet.ToolPackage.Tests
|
||||
{
|
||||
public class ToolPackageInstanceTests : TestBase
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(false)]
|
||||
[InlineData(true)]
|
||||
public void GivenAnInstalledPackageUninstallRemovesThePackage(bool testMockBehaviorIsInSync)
|
||||
{
|
||||
var source = GetTestLocalFeedPath();
|
||||
|
||||
var (store, installer, reporter, fileSystem) = Setup(
|
||||
useMock: testMockBehaviorIsInSync,
|
||||
feeds: GetMockFeedsForSource(source));
|
||||
|
||||
var package = installer.InstallPackage(
|
||||
packageId: TestPackageId,
|
||||
versionRange: VersionRange.Parse(TestPackageVersion),
|
||||
targetFramework: _testTargetframework,
|
||||
additionalFeeds: new[] {source});
|
||||
|
||||
package.PackagedShims.Should().ContainSingle(f => f.Value.Contains("demo.exe") || f.Value.Contains("demo"));
|
||||
|
||||
package.Uninstall();
|
||||
}
|
||||
|
||||
private static FilePath GetUniqueTempProjectPathEachTest()
|
||||
{
|
||||
var tempProjectDirectory =
|
||||
new DirectoryPath(Path.GetTempPath()).WithSubDirectories(Path.GetRandomFileName());
|
||||
var tempProjectPath =
|
||||
tempProjectDirectory.WithFile(Path.GetRandomFileName() + ".csproj");
|
||||
return tempProjectPath;
|
||||
}
|
||||
|
||||
private static IEnumerable<MockFeed> GetMockFeedsForSource(string source)
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
new MockFeed
|
||||
{
|
||||
Type = MockFeedType.ImplicitAdditionalFeed,
|
||||
Uri = source,
|
||||
Packages = new List<MockFeedPackage>
|
||||
{
|
||||
new MockFeedPackage
|
||||
{
|
||||
PackageId = TestPackageId.ToString(),
|
||||
Version = TestPackageVersion
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static (IToolPackageStore, IToolPackageInstaller, BufferedReporter, IFileSystem) Setup(
|
||||
bool useMock,
|
||||
IEnumerable<MockFeed> feeds = null,
|
||||
FilePath? tempProject = null,
|
||||
DirectoryPath? offlineFeed = null)
|
||||
{
|
||||
var root = new DirectoryPath(Path.Combine(TempRoot.Root, Path.GetRandomFileName()));
|
||||
var reporter = new BufferedReporter();
|
||||
|
||||
IFileSystem fileSystem;
|
||||
IToolPackageStore store;
|
||||
IToolPackageInstaller installer;
|
||||
if (useMock)
|
||||
{
|
||||
var packagedShimsMap = new Dictionary<PackageId, IReadOnlyList<FilePath>>
|
||||
{
|
||||
[TestPackageId] = new FilePath[] {new FilePath("path/demo.exe")}
|
||||
};
|
||||
|
||||
fileSystem = new FileSystemMockBuilder().Build();
|
||||
store = new ToolPackageStoreMock(root, fileSystem);
|
||||
installer = new ToolPackageInstallerMock(
|
||||
fileSystem: fileSystem,
|
||||
store: store,
|
||||
projectRestorer: new ProjectRestorerMock(
|
||||
fileSystem: fileSystem,
|
||||
reporter: reporter,
|
||||
feeds: feeds),
|
||||
packagedShimsMap: packagedShimsMap);
|
||||
}
|
||||
else
|
||||
{
|
||||
fileSystem = new FileSystemWrapper();
|
||||
store = new ToolPackageStore(root);
|
||||
installer = new ToolPackageInstaller(
|
||||
store: store,
|
||||
projectRestorer: new ProjectRestorer(reporter),
|
||||
tempProject: tempProject ?? GetUniqueTempProjectPathEachTest(),
|
||||
offlineFeed: offlineFeed ?? new DirectoryPath("does not exist"));
|
||||
}
|
||||
|
||||
store.Root.Value.Should().Be(Path.GetFullPath(root.Value));
|
||||
|
||||
return (store, installer, reporter, fileSystem);
|
||||
}
|
||||
|
||||
private static string GetTestLocalFeedPath() =>
|
||||
Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "TestAssetLocalNugetFeed");
|
||||
|
||||
private readonly string _testTargetframework = BundledTargetFramework.GetTargetFrameworkMoniker();
|
||||
private const string TestPackageVersion = "1.0.4";
|
||||
private static readonly PackageId TestPackageId = new PackageId("global.tool.console.demo.with.shim");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
// 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.DotNet.ShellShim;
|
||||
using Microsoft.Extensions.EnvironmentAbstractions;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.DotNet.Tools.Tests.ComponentMocks
|
||||
{
|
||||
internal class AppHostShellShimMakerMock : IAppHostShellShimMaker
|
||||
{
|
||||
private static IFileSystem _fileSystem;
|
||||
|
||||
public AppHostShellShimMakerMock(IFileSystem fileSystem = null)
|
||||
{
|
||||
_fileSystem = fileSystem ?? new FileSystemWrapper();
|
||||
}
|
||||
|
||||
public void CreateApphostShellShim(FilePath entryPoint, FilePath shimPath)
|
||||
{
|
||||
var shim = new FakeShim
|
||||
{
|
||||
Runner = "dotnet",
|
||||
ExecutablePath = entryPoint.Value
|
||||
};
|
||||
|
||||
_fileSystem.File.WriteAllText(
|
||||
shimPath.Value,
|
||||
JsonConvert.SerializeObject(shim));
|
||||
}
|
||||
|
||||
public class FakeShim
|
||||
{
|
||||
public string Runner { get; set; }
|
||||
public string ExecutablePath { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,104 +0,0 @@
|
|||
// 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.Runtime.InteropServices;
|
||||
using Microsoft.DotNet.Cli;
|
||||
using Microsoft.DotNet.Cli.Utils;
|
||||
using Microsoft.DotNet.ShellShim;
|
||||
using Microsoft.Extensions.EnvironmentAbstractions;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.DotNet.Tools.Tests.ComponentMocks
|
||||
{
|
||||
internal class ShellShimRepositoryMock : IShellShimRepository
|
||||
{
|
||||
private static IFileSystem _fileSystem;
|
||||
private readonly DirectoryPath _pathToPlaceShim;
|
||||
|
||||
public ShellShimRepositoryMock(DirectoryPath pathToPlaceShim, IFileSystem fileSystem = null)
|
||||
{
|
||||
_pathToPlaceShim = pathToPlaceShim;
|
||||
_fileSystem = fileSystem ?? new FileSystemWrapper();
|
||||
}
|
||||
|
||||
public void CreateShim(FilePath targetExecutablePath, string commandName)
|
||||
{
|
||||
if (ShimExists(commandName))
|
||||
{
|
||||
throw new ShellShimException(
|
||||
string.Format(CommonLocalizableStrings.ShellShimConflict,
|
||||
commandName));
|
||||
}
|
||||
|
||||
TransactionalAction.Run(
|
||||
action: () => {
|
||||
var shim = new FakeShim
|
||||
{
|
||||
Runner = "dotnet",
|
||||
ExecutablePath = targetExecutablePath.Value
|
||||
};
|
||||
|
||||
_fileSystem.File.WriteAllText(
|
||||
GetShimPath(commandName).Value,
|
||||
JsonConvert.SerializeObject(shim));
|
||||
},
|
||||
rollback: () => {
|
||||
_fileSystem.File.Delete(GetShimPath(commandName).Value);
|
||||
});
|
||||
}
|
||||
|
||||
public void RemoveShim(string commandName)
|
||||
{
|
||||
var originalShimPath = GetShimPath(commandName);
|
||||
if (!_fileSystem.File.Exists(originalShimPath.Value))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string tempShimPath = null;
|
||||
TransactionalAction.Run(
|
||||
action: () => {
|
||||
var tempFile = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
|
||||
_fileSystem.File.Move(originalShimPath.Value, tempFile);
|
||||
tempShimPath = tempFile;
|
||||
},
|
||||
commit: () => {
|
||||
if (tempShimPath != null)
|
||||
{
|
||||
_fileSystem.File.Delete(tempShimPath);
|
||||
}
|
||||
},
|
||||
rollback: () => {
|
||||
if (tempShimPath != null)
|
||||
{
|
||||
_fileSystem.File.Move(tempShimPath, originalShimPath.Value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private bool ShimExists(string commandName)
|
||||
{
|
||||
return _fileSystem.File.Exists(GetShimPath(commandName).Value);
|
||||
}
|
||||
|
||||
private FilePath GetShimPath(string shellCommandName)
|
||||
{
|
||||
var shimPath = Path.Combine(_pathToPlaceShim.Value, shellCommandName);
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
shimPath += ".exe";
|
||||
}
|
||||
|
||||
return new FilePath(shimPath);
|
||||
}
|
||||
|
||||
public class FakeShim
|
||||
{
|
||||
public string Runner { get; set; }
|
||||
public string ExecutablePath { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -23,19 +23,22 @@ namespace Microsoft.DotNet.Tools.Tests.ComponentMocks
|
|||
private readonly IFileSystem _fileSystem;
|
||||
private readonly Action _installCallback;
|
||||
private readonly Dictionary<PackageId, IEnumerable<string>> _warningsMap;
|
||||
private readonly Dictionary<PackageId, IReadOnlyList<FilePath>> _packagedShimsMap;
|
||||
|
||||
public ToolPackageInstallerMock(
|
||||
IFileSystem fileSystem,
|
||||
IToolPackageStore store,
|
||||
IProjectRestorer projectRestorer,
|
||||
Action installCallback = null,
|
||||
Dictionary<PackageId, IEnumerable<string>> warningsMap = null)
|
||||
Dictionary<PackageId, IEnumerable<string>> warningsMap = null,
|
||||
Dictionary<PackageId, IReadOnlyList<FilePath>> packagedShimsMap = null)
|
||||
{
|
||||
_fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem));
|
||||
_store = store ?? throw new ArgumentNullException(nameof(store));
|
||||
_projectRestorer = projectRestorer ?? throw new ArgumentNullException(nameof(projectRestorer));
|
||||
_installCallback = installCallback;
|
||||
_warningsMap = warningsMap ?? new Dictionary<PackageId, IEnumerable<string>>();
|
||||
_packagedShimsMap = packagedShimsMap ?? new Dictionary<PackageId, IReadOnlyList<FilePath>>();
|
||||
}
|
||||
|
||||
public IToolPackage InstallPackage(PackageId packageId,
|
||||
|
@ -92,7 +95,10 @@ namespace Microsoft.DotNet.Tools.Tests.ComponentMocks
|
|||
IEnumerable<string> warnings = null;
|
||||
_warningsMap.TryGetValue(packageId, out warnings);
|
||||
|
||||
return new ToolPackageMock(_fileSystem, packageId, version, packageDirectory, warnings: warnings);
|
||||
IReadOnlyList<FilePath> packedShims = null;
|
||||
_packagedShimsMap.TryGetValue(packageId, out packedShims);
|
||||
|
||||
return new ToolPackageMock(_fileSystem, packageId, version, packageDirectory, warnings: warnings, packagedShims: packedShims);
|
||||
},
|
||||
rollback: () => {
|
||||
if (rollbackDirectory != null && _fileSystem.Directory.Exists(rollbackDirectory))
|
||||
|
|
|
@ -18,6 +18,7 @@ namespace Microsoft.DotNet.Tools.Tests.ComponentMocks
|
|||
private Lazy<IReadOnlyList<CommandSettings>> _commands;
|
||||
private Action _uninstallCallback;
|
||||
private IEnumerable<string> _warnings;
|
||||
private readonly IReadOnlyList<FilePath> _packagedShims;
|
||||
|
||||
public ToolPackageMock(
|
||||
IFileSystem fileSystem,
|
||||
|
@ -25,7 +26,8 @@ namespace Microsoft.DotNet.Tools.Tests.ComponentMocks
|
|||
NuGetVersion version,
|
||||
DirectoryPath packageDirectory,
|
||||
Action uninstallCallback = null,
|
||||
IEnumerable<string> warnings = null)
|
||||
IEnumerable<string> warnings = null,
|
||||
IReadOnlyList<FilePath> packagedShims = null)
|
||||
{
|
||||
_fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem));
|
||||
Id = id;
|
||||
|
@ -34,6 +36,7 @@ namespace Microsoft.DotNet.Tools.Tests.ComponentMocks
|
|||
_commands = new Lazy<IReadOnlyList<CommandSettings>>(GetCommands);
|
||||
_uninstallCallback = uninstallCallback;
|
||||
_warnings = warnings ?? new List<string>();
|
||||
_packagedShims = packagedShims ?? new List<FilePath>();
|
||||
}
|
||||
|
||||
public PackageId Id { get; private set; }
|
||||
|
@ -52,6 +55,14 @@ namespace Microsoft.DotNet.Tools.Tests.ComponentMocks
|
|||
|
||||
public IEnumerable<string> Warnings => _warnings;
|
||||
|
||||
public IReadOnlyList<FilePath> PackagedShims
|
||||
{
|
||||
get
|
||||
{
|
||||
return _packagedShims;
|
||||
}
|
||||
}
|
||||
|
||||
public void Uninstall()
|
||||
{
|
||||
var rootDirectory = PackageDirectory.GetParentPath();
|
||||
|
|
|
@ -132,6 +132,20 @@ namespace Microsoft.Extensions.DependencyModel.Tests
|
|||
|
||||
_files.Remove(path);
|
||||
}
|
||||
|
||||
public void Copy(string source, string destination)
|
||||
{
|
||||
if (!Exists(source))
|
||||
{
|
||||
throw new FileNotFoundException("source does not exist.");
|
||||
}
|
||||
if (Exists(destination))
|
||||
{
|
||||
throw new IOException("destination exists.");
|
||||
}
|
||||
|
||||
_files[destination] = _files[source];
|
||||
}
|
||||
}
|
||||
|
||||
private class DirectoryMock : IDirectory
|
||||
|
|
|
@ -46,7 +46,11 @@ namespace Microsoft.DotNet.Tests.Commands
|
|||
_fileSystem = new FileSystemMockBuilder().Build();
|
||||
_toolPackageStore = new ToolPackageStoreMock(new DirectoryPath(PathToPlacePackages), _fileSystem);
|
||||
_createShellShimRepository =
|
||||
(nonGlobalLocation) => new ShellShimRepositoryMock(new DirectoryPath(PathToPlaceShim), _fileSystem);
|
||||
(nonGlobalLocation) => new ShellShimRepository(
|
||||
new DirectoryPath(PathToPlaceShim),
|
||||
fileSystem: _fileSystem,
|
||||
appHostShellShimMaker: new AppHostShellShimMakerMock(_fileSystem),
|
||||
filePermissionSetter: new NoOpFilePermissionSetter());
|
||||
_environmentPathInstructionMock =
|
||||
new EnvironmentPathInstructionMock(_reporter, PathToPlaceShim);
|
||||
_createToolPackageStoreAndInstaller = (_) => (_toolPackageStore, CreateToolPackageInstaller());
|
||||
|
@ -71,7 +75,7 @@ namespace Microsoft.DotNet.Tests.Commands
|
|||
|
||||
// It is hard to simulate shell behavior. Only Assert shim can point to executable dll
|
||||
_fileSystem.File.Exists(ExpectedCommandPath()).Should().BeTrue();
|
||||
var deserializedFakeShim = JsonConvert.DeserializeObject<ShellShimRepositoryMock.FakeShim>(
|
||||
var deserializedFakeShim = JsonConvert.DeserializeObject<AppHostShellShimMakerMock.FakeShim>(
|
||||
_fileSystem.File.ReadAllText(ExpectedCommandPath()));
|
||||
|
||||
_fileSystem.File.Exists(deserializedFakeShim.ExecutablePath).Should().BeTrue();
|
||||
|
@ -117,7 +121,7 @@ namespace Microsoft.DotNet.Tests.Commands
|
|||
_fileSystem.File.Exists(ExpectedCommandPath())
|
||||
.Should().BeTrue();
|
||||
var deserializedFakeShim =
|
||||
JsonConvert.DeserializeObject<ShellShimRepositoryMock.FakeShim>(
|
||||
JsonConvert.DeserializeObject<AppHostShellShimMakerMock.FakeShim>(
|
||||
_fileSystem.File.ReadAllText(ExpectedCommandPath()));
|
||||
_fileSystem.File.Exists(deserializedFakeShim.ExecutablePath).Should().BeTrue();
|
||||
}
|
||||
|
@ -455,6 +459,42 @@ namespace Microsoft.DotNet.Tests.Commands
|
|||
_reporter.Lines.Should().NotContain(l => l.Contains(EnvironmentPathInstructionMock.MockInstructionText));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AndPackagedShimIsProvidedWhenRunWithPackageIdItCreateShimUsingPackagedShim()
|
||||
{
|
||||
var extension = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ".exe" : string.Empty;
|
||||
var prepackagedShimPath = "packagedShimDirectory/" + ProjectRestorerMock.FakeCommandName + extension;
|
||||
var tokenToIdentifyPackagedShim = "packagedShim";
|
||||
_fileSystem.File.WriteAllText(prepackagedShimPath, tokenToIdentifyPackagedShim);
|
||||
|
||||
var result = Parser.Instance.Parse($"dotnet tool install --tool-path /tmp/folder {PackageId}");
|
||||
var appliedCommand = result["dotnet"]["tool"]["install"];
|
||||
var parser = Parser.Instance;
|
||||
var parseResult = parser.ParseFrom("dotnet tool", new[] {"install", "-g", PackageId});
|
||||
|
||||
var packagedShimsMap = new Dictionary<PackageId, IReadOnlyList<FilePath>>
|
||||
{
|
||||
[new PackageId(PackageId)] = new[] {new FilePath(prepackagedShimPath)}
|
||||
};
|
||||
|
||||
var installCommand = new ToolInstallCommand(appliedCommand,
|
||||
parseResult,
|
||||
(_) => (_toolPackageStore, new ToolPackageInstallerMock(
|
||||
fileSystem: _fileSystem,
|
||||
store: _toolPackageStore,
|
||||
packagedShimsMap: packagedShimsMap,
|
||||
projectRestorer: new ProjectRestorerMock(
|
||||
fileSystem: _fileSystem,
|
||||
reporter: _reporter))),
|
||||
_createShellShimRepository,
|
||||
new EnvironmentPathInstructionMock(_reporter, PathToPlaceShim),
|
||||
_reporter);
|
||||
|
||||
installCommand.Execute().Should().Be(0);
|
||||
|
||||
_fileSystem.File.ReadAllText(ExpectedCommandPath()).Should().Be(tokenToIdentifyPackagedShim);
|
||||
}
|
||||
|
||||
private IToolPackageInstaller CreateToolPackageInstaller(
|
||||
IEnumerable<MockFeed> feeds = null,
|
||||
Action installCallback = null)
|
||||
|
@ -476,5 +516,12 @@ namespace Microsoft.DotNet.Tests.Commands
|
|||
"pathToPlace",
|
||||
ProjectRestorerMock.FakeCommandName + extension);
|
||||
}
|
||||
|
||||
private class NoOpFilePermissionSetter : IFilePermissionSetter
|
||||
{
|
||||
public void SetUserExecutionPermission(string path)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ using Xunit;
|
|||
using Parser = Microsoft.DotNet.Cli.Parser;
|
||||
using LocalizableStrings = Microsoft.DotNet.Tools.Tool.Uninstall.LocalizableStrings;
|
||||
using InstallLocalizableStrings = Microsoft.DotNet.Tools.Tool.Install.LocalizableStrings;
|
||||
using Microsoft.DotNet.ShellShim;
|
||||
|
||||
namespace Microsoft.DotNet.Tests.Commands
|
||||
{
|
||||
|
@ -203,7 +204,10 @@ namespace Microsoft.DotNet.Tests.Commands
|
|||
result["dotnet"]["tool"]["install"],
|
||||
result,
|
||||
(_) => (store, packageInstallerMock),
|
||||
(_) => new ShellShimRepositoryMock(new DirectoryPath(ShimsDirectory), _fileSystem),
|
||||
(_) => new ShellShimRepository(
|
||||
new DirectoryPath(ShimsDirectory),
|
||||
fileSystem: _fileSystem,
|
||||
appHostShellShimMaker: new AppHostShellShimMakerMock(_fileSystem)),
|
||||
_environmentPathInstructionMock,
|
||||
_reporter);
|
||||
}
|
||||
|
@ -219,7 +223,10 @@ namespace Microsoft.DotNet.Tests.Commands
|
|||
new DirectoryPath(ToolsDirectory),
|
||||
_fileSystem,
|
||||
uninstallCallback),
|
||||
(_) => new ShellShimRepositoryMock(new DirectoryPath(ShimsDirectory), _fileSystem),
|
||||
(_) => new ShellShimRepository(
|
||||
new DirectoryPath(ShimsDirectory),
|
||||
fileSystem: _fileSystem,
|
||||
appHostShellShimMaker: new AppHostShellShimMakerMock(_fileSystem)),
|
||||
_reporter);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ using Microsoft.Extensions.EnvironmentAbstractions;
|
|||
using Xunit;
|
||||
using Parser = Microsoft.DotNet.Cli.Parser;
|
||||
using LocalizableStrings = Microsoft.DotNet.Tools.Tool.Update.LocalizableStrings;
|
||||
using Microsoft.DotNet.ShellShim;
|
||||
|
||||
namespace Microsoft.DotNet.Tests.Commands
|
||||
{
|
||||
|
@ -139,7 +140,7 @@ namespace Microsoft.DotNet.Tests.Commands
|
|||
_mockFeeds
|
||||
),
|
||||
installCallback: () => throw new ToolConfigurationException("Simulated error"))),
|
||||
_ => new ShellShimRepositoryMock(new DirectoryPath(ShimsDirectory), _fileSystem),
|
||||
_ => GetMockedShellShimRepository(),
|
||||
_reporter);
|
||||
|
||||
Action a = () => command.Execute();
|
||||
|
@ -168,7 +169,7 @@ namespace Microsoft.DotNet.Tests.Commands
|
|||
_mockFeeds
|
||||
),
|
||||
installCallback: () => throw new ToolConfigurationException("Simulated error"))),
|
||||
_ => new ShellShimRepositoryMock(new DirectoryPath(ShimsDirectory), _fileSystem),
|
||||
_ => GetMockedShellShimRepository(),
|
||||
_reporter);
|
||||
|
||||
Action a = () => command.Execute();
|
||||
|
@ -216,7 +217,7 @@ namespace Microsoft.DotNet.Tests.Commands
|
|||
_reporter,
|
||||
_mockFeeds
|
||||
))),
|
||||
(_) => new ShellShimRepositoryMock(new DirectoryPath(ShimsDirectory), _fileSystem),
|
||||
(_) => GetMockedShellShimRepository(),
|
||||
_environmentPathInstructionMock,
|
||||
_reporter);
|
||||
}
|
||||
|
@ -236,8 +237,16 @@ namespace Microsoft.DotNet.Tests.Commands
|
|||
_reporter,
|
||||
_mockFeeds
|
||||
))),
|
||||
(_) => new ShellShimRepositoryMock(new DirectoryPath(ShimsDirectory), _fileSystem),
|
||||
(_) => GetMockedShellShimRepository(),
|
||||
_reporter);
|
||||
}
|
||||
|
||||
private ShellShimRepository GetMockedShellShimRepository()
|
||||
{
|
||||
return new ShellShimRepository(
|
||||
new DirectoryPath(ShimsDirectory),
|
||||
fileSystem: _fileSystem,
|
||||
appHostShellShimMaker: new AppHostShellShimMakerMock(_fileSystem));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue