Fix failure to add tools path on restricted Windows environments.

On environments where registry access is disabled, the first run experience
fails because it could not add the tools path to the user's environment.

This fix properly handles the security exception by printing a warning and
continuing.  Users will have to manually add the PATH environment variable to
their environments to prevent `dotnet tool install` from printing PATH
instructions.

A new file sentinel is added to track whether or not the PATH has been
modified.  The first run experience also now correctly skips modifying the PATH
if `DOTNET_SKIP_FIRST_TIME_EXPERIENCE` is set.

Fixes #8874.
This commit is contained in:
Peter Huene 2018-03-23 12:40:58 -07:00
parent d3f69e2e01
commit 19090744cb
No known key found for this signature in database
GPG key ID: E1D265D820213D6A
31 changed files with 746 additions and 328 deletions

View file

@ -142,5 +142,14 @@ namespace Microsoft.DotNet.Cli.Utils
}
}
public string GetEnvironmentVariable(string variable, EnvironmentVariableTarget target)
{
return Environment.GetEnvironmentVariable(variable, target);
}
public void SetEnvironmentVariable(string variable, string value, EnvironmentVariableTarget target)
{
Environment.SetEnvironmentVariable(variable, value, target);
}
}
}

View file

@ -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;
using System.Collections.Generic;
namespace Microsoft.DotNet.Cli.Utils
@ -18,5 +19,9 @@ namespace Microsoft.DotNet.Cli.Utils
bool GetEnvironmentVariableAsBool(string name, bool defaultValue);
string GetEnvironmentVariable(string name);
string GetEnvironmentVariable(string variable, EnvironmentVariableTarget target);
void SetEnvironmentVariable(string variable, string value, EnvironmentVariableTarget target);
}
}

View file

@ -18,6 +18,7 @@ namespace Microsoft.DotNet.Configurer
private IFirstTimeUseNoticeSentinel _firstTimeUseNoticeSentinel;
private IAspNetCertificateSentinel _aspNetCertificateSentinel;
private IAspNetCoreCertificateGenerator _aspNetCoreCertificateGenerator;
private IFileSentinel _toolPathSentinel;
private string _cliFallbackFolderPath;
private readonly IEnvironmentPath _pathAdder;
@ -27,6 +28,7 @@ namespace Microsoft.DotNet.Configurer
IFirstTimeUseNoticeSentinel firstTimeUseNoticeSentinel,
IAspNetCertificateSentinel aspNetCertificateSentinel,
IAspNetCoreCertificateGenerator aspNetCoreCertificateGenerator,
IFileSentinel toolPathSentinel,
IEnvironmentProvider environmentProvider,
IReporter reporter,
string cliFallbackFolderPath,
@ -37,6 +39,7 @@ namespace Microsoft.DotNet.Configurer
_firstTimeUseNoticeSentinel = firstTimeUseNoticeSentinel;
_aspNetCertificateSentinel = aspNetCertificateSentinel;
_aspNetCoreCertificateGenerator = aspNetCoreCertificateGenerator;
_toolPathSentinel = toolPathSentinel;
_environmentProvider = environmentProvider;
_reporter = reporter;
_cliFallbackFolderPath = cliFallbackFolderPath;
@ -45,7 +48,10 @@ namespace Microsoft.DotNet.Configurer
public void Configure()
{
AddPackageExecutablePath();
if (ShouldAddPackageExecutablePath())
{
AddPackageExecutablePath();
}
if (ShouldPrintFirstTimeUseNotice())
{
@ -66,7 +72,7 @@ namespace Microsoft.DotNet.Configurer
}
}
if(ShouldGenerateAspNetCertificate())
if (ShouldGenerateAspNetCertificate())
{
GenerateAspNetCertificate();
}
@ -92,21 +98,16 @@ namespace Microsoft.DotNet.Configurer
!_aspNetCertificateSentinel.Exists();
}
private bool ShouldAddPackageExecutablePath()
{
return ShouldRunFirstRunExperience() && !_toolPathSentinel.Exists();
}
private void AddPackageExecutablePath()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
if (!_firstTimeUseNoticeSentinel.Exists())
{
// Invoke when Windows first run
_pathAdder.AddPackageExecutablePathToUserPath();
}
}
else
{
// Invoke during installer, otherwise, _pathAdder will be no op object that this point
_pathAdder.AddPackageExecutablePathToUserPath();
}
_pathAdder.AddPackageExecutablePathToUserPath();
_toolPathSentinel.Create();
}
private bool ShouldPrintFirstTimeUseNotice()

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.IO;
using Microsoft.Extensions.EnvironmentAbstractions;
namespace Microsoft.DotNet.Configurer
{
public class FileSentinel : IFileSentinel
{
private readonly FilePath _file;
private readonly IFileSystem _fileSystem;
public FileSentinel(FilePath file) :
this(file, fileSystem: null)
{
}
internal FileSentinel(FilePath file, IFileSystem fileSystem)
{
_file = file;
_fileSystem = fileSystem ?? FileSystemWrapper.Default;
}
public bool Exists()
{
return _fileSystem.File.Exists(_file.Value);
}
public void Create()
{
if (Exists())
{
return;
}
var directory = _file.GetDirectoryPath();
if (!_fileSystem.Directory.Exists(directory.Value))
{
_fileSystem.Directory.CreateDirectory(directory.Value);
}
_fileSystem.File.CreateEmptyFile(_file.Value);
}
}
}

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.Configurer
{
public interface IFileSentinel
{
bool Exists();
void Create();
}
}

View file

@ -0,0 +1,19 @@
// 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.Configurer
{
public class NoOpFileSentinel : IFileSentinel
{
public bool Exists()
{
return true;
}
public void Create()
{
}
}
}

View file

@ -595,6 +595,9 @@ setx PATH "%PATH%;{0}"
<data name="EnvironmentPathWindowsNeedReopen" xml:space="preserve">
<value>Since you just installed the .NET Core SDK, you will need to reopen the Command Prompt window before running the tool you installed.</value>
</data>
<data name="FailedToSetToolsPathEnvironmentVariable" xml:space="preserve">
<value>Failed to add '{0}' to the PATH environment variable. Please add this directory to your PATH to use tools installed with 'dotnet tool install'.</value>
</data>
<data name="FailedToCreateShellShim" xml:space="preserve">
<value>Failed to create tool shim for command '{0}': {1}</value>
</data>

View file

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
@ -23,6 +24,8 @@ namespace Microsoft.DotNet.Cli
{
public class Program
{
private static readonly string ToolPathSentinelFileName = $"{Product.Version}.toolpath.sentinel";
public static int Main(string[] args)
{
DebugHelper.HandleDebugSwitch(ref args);
@ -94,6 +97,12 @@ namespace Microsoft.DotNet.Cli
{
IFirstTimeUseNoticeSentinel firstTimeUseNoticeSentinel = disposableFirstTimeUseNoticeSentinel;
IAspNetCertificateSentinel aspNetCertificateSentinel = new AspNetCertificateSentinel(cliFallbackFolderPathCalculator);
IFileSentinel toolPathSentinel = new FileSentinel(
new FilePath(
Path.Combine(
CliFolderPathCalculator.DotnetUserProfileFolderPath,
ToolPathSentinelFileName)));
for (; lastArg < args.Length; lastArg++)
{
if (IsArg(args[lastArg], "d", "diagnostics"))
@ -138,6 +147,7 @@ namespace Microsoft.DotNet.Cli
{
aspNetCertificateSentinel = new NoOpAspNetCertificateSentinel();
firstTimeUseNoticeSentinel = new NoOpFirstTimeUseNoticeSentinel();
toolPathSentinel = new NoOpFileSentinel();
hasSuperUserAccess = true;
}
@ -145,6 +155,7 @@ namespace Microsoft.DotNet.Cli
nugetCacheSentinel,
firstTimeUseNoticeSentinel,
aspNetCertificateSentinel,
toolPathSentinel,
cliFallbackFolderPathCalculator,
hasSuperUserAccess);
@ -209,6 +220,7 @@ namespace Microsoft.DotNet.Cli
INuGetCacheSentinel nugetCacheSentinel,
IFirstTimeUseNoticeSentinel firstTimeUseNoticeSentinel,
IAspNetCertificateSentinel aspNetCertificateSentinel,
IFileSentinel toolPathSentinel,
CliFolderPathCalculator cliFolderPathCalculator,
bool hasSuperUserAccess)
{
@ -231,6 +243,7 @@ namespace Microsoft.DotNet.Cli
firstTimeUseNoticeSentinel,
aspNetCertificateSentinel,
aspnetCertificateGenerator,
toolPathSentinel,
environmentProvider,
Reporter.Output,
cliFolderPathCalculator.CliFallbackFolderPath,

View file

@ -33,14 +33,16 @@ namespace Microsoft.DotNet.ShellShim
{
environmentPath = new WindowsEnvironmentPath(
cliFolderPathCalculator.ToolsShimPath,
Reporter.Output);
Reporter.Output,
environmentProvider);
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && hasSuperUserAccess)
{
environmentPath = new LinuxEnvironmentPath(
cliFolderPathCalculator.ToolsShimPathInUnix,
Reporter.Output,
environmentProvider, new FileWrapper());
environmentProvider,
new FileWrapper());
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) && hasSuperUserAccess)
{

View file

@ -18,9 +18,9 @@ namespace Microsoft.DotNet.ShellShim
private const string PathName = "PATH";
private readonly BashPathUnderHomeDirectory _packageExecutablePath;
private readonly string _profiledDotnetCliToolsPath
= Environment.GetEnvironmentVariable("DOTNET_CLI_TEST_LINUX_PROFILED_PATH")
?? @"/etc/profile.d/dotnet-cli-tools-bin-path.sh";
internal static readonly string DotnetCliToolsProfilePath =
Environment.GetEnvironmentVariable("DOTNET_CLI_TEST_LINUX_PROFILED_PATH") ??
@"/etc/profile.d/dotnet-cli-tools-bin-path.sh";
internal LinuxEnvironmentPath(
BashPathUnderHomeDirectory packageExecutablePath,
@ -44,28 +44,27 @@ namespace Microsoft.DotNet.ShellShim
}
var script = $"export PATH=\"$PATH:{_packageExecutablePath.PathWithDollar}\"";
_fileSystem.WriteAllText(_profiledDotnetCliToolsPath, script);
_fileSystem.WriteAllText(DotnetCliToolsProfilePath, script);
}
private bool PackageExecutablePathExists()
{
var environmentVariable = _environmentProvider
.GetEnvironmentVariable(PathName);
if (environmentVariable == null)
var value = _environmentProvider.GetEnvironmentVariable(PathName);
if (value == null)
{
return false;
}
return environmentVariable
.Split(':').Contains(_packageExecutablePath.Path);
return value
.Split(':')
.Any(p => p == _packageExecutablePath.Path || p == _packageExecutablePath.PathWithTilde);
}
public void PrintAddPathInstructionIfPathDoesNotExist()
{
if (!PackageExecutablePathExists())
{
if (_fileSystem.Exists(_profiledDotnetCliToolsPath))
if (_fileSystem.Exists(DotnetCliToolsProfilePath))
{
_reporter.WriteLine(
CommonLocalizableStrings.EnvironmentPathLinuxNeedLogout);

View file

@ -18,7 +18,7 @@ namespace Microsoft.DotNet.ShellShim
private readonly IEnvironmentProvider _environmentProvider;
private readonly IReporter _reporter;
private static readonly string PathDDotnetCliToolsPath
internal static readonly string DotnetCliToolsPathsDPath
= Environment.GetEnvironmentVariable("DOTNET_CLI_TEST_OSX_PATHSD_PATH")
?? @"/etc/paths.d/dotnet-cli-tools";
@ -44,28 +44,27 @@ namespace Microsoft.DotNet.ShellShim
return;
}
var script = $"{_packageExecutablePath.PathWithTilde}";
_fileSystem.WriteAllText(PathDDotnetCliToolsPath, script);
_fileSystem.WriteAllText(DotnetCliToolsPathsDPath, _packageExecutablePath.PathWithTilde);
}
private bool PackageExecutablePathExists()
{
var environmentVariable = _environmentProvider.GetEnvironmentVariable(PathName);
if (environmentVariable == null)
var value = _environmentProvider.GetEnvironmentVariable(PathName);
if (value == null)
{
return false;
}
return environmentVariable.Split(':').Contains(_packageExecutablePath.PathWithTilde)
|| environmentVariable.Split(':').Contains(_packageExecutablePath.Path);
return value
.Split(':')
.Any(p => p == _packageExecutablePath.Path || p == _packageExecutablePath.PathWithTilde);
}
public void PrintAddPathInstructionIfPathDoesNotExist()
{
if (!PackageExecutablePathExists())
{
if (_fileSystem.Exists(PathDDotnetCliToolsPath))
if (_fileSystem.Exists(DotnetCliToolsPathsDPath))
{
_reporter.WriteLine(
CommonLocalizableStrings.EnvironmentPathOSXNeedReopen);

View file

@ -14,14 +14,13 @@ namespace Microsoft.DotNet.ShellShim
private readonly IReporter _reporter;
private const string PathName = "PATH";
private readonly string _packageExecutablePath;
private readonly IEnvironmentProvider _environmentProvider;
public WindowsEnvironmentPath(
string packageExecutablePath, IReporter reporter)
public WindowsEnvironmentPath(string packageExecutablePath, IReporter reporter, IEnvironmentProvider environmentProvider)
{
_packageExecutablePath
= packageExecutablePath ?? throw new ArgumentNullException(nameof(packageExecutablePath));
_reporter
= reporter ?? throw new ArgumentNullException(nameof(reporter));
_packageExecutablePath = packageExecutablePath ?? throw new ArgumentNullException(nameof(packageExecutablePath));
_reporter = reporter ?? throw new ArgumentNullException(nameof(reporter));
_environmentProvider = environmentProvider ?? throw new ArgumentNullException(nameof(environmentProvider));
}
public void AddPackageExecutablePathToUserPath()
@ -31,27 +30,36 @@ namespace Microsoft.DotNet.ShellShim
return;
}
var existingUserEnvPath = Environment.GetEnvironmentVariable(PathName, EnvironmentVariableTarget.User);
var existingUserEnvPath = _environmentProvider.GetEnvironmentVariable(PathName, EnvironmentVariableTarget.User);
if (existingUserEnvPath == null)
try
{
Environment.SetEnvironmentVariable(
PathName,
_packageExecutablePath,
EnvironmentVariableTarget.User);
}
else
{
if (existingUserEnvPath.EndsWith(';'))
if (existingUserEnvPath == null)
{
existingUserEnvPath = existingUserEnvPath.Substring(0, (existingUserEnvPath.Length - 1));
_environmentProvider.SetEnvironmentVariable(
PathName,
_packageExecutablePath,
EnvironmentVariableTarget.User);
}
else
{
if (existingUserEnvPath.EndsWith(';'))
{
existingUserEnvPath = existingUserEnvPath.Substring(0, (existingUserEnvPath.Length - 1));
}
Environment.SetEnvironmentVariable(
PathName,
$"{existingUserEnvPath};{_packageExecutablePath}",
EnvironmentVariableTarget.User);
_environmentProvider.SetEnvironmentVariable(
PathName,
$"{existingUserEnvPath};{_packageExecutablePath}",
EnvironmentVariableTarget.User);
}
}
catch (System.Security.SecurityException)
{
_reporter.WriteLine(
string.Format(
CommonLocalizableStrings.FailedToSetToolsPathEnvironmentVariable,
_packageExecutablePath).Yellow());
}
}
@ -62,13 +70,13 @@ namespace Microsoft.DotNet.ShellShim
private bool PackageExecutablePathWillExistForFutureNewProcess()
{
return EnvironmentVariableConatinsPackageExecutablePath(Environment.GetEnvironmentVariable(PathName, EnvironmentVariableTarget.User))
|| EnvironmentVariableConatinsPackageExecutablePath(Environment.GetEnvironmentVariable(PathName, EnvironmentVariableTarget.Machine));
return EnvironmentVariableConatinsPackageExecutablePath(_environmentProvider.GetEnvironmentVariable(PathName, EnvironmentVariableTarget.User))
|| EnvironmentVariableConatinsPackageExecutablePath(_environmentProvider.GetEnvironmentVariable(PathName, EnvironmentVariableTarget.Machine));
}
private bool PackageExecutablePathExistsForCurrentProcess()
{
return EnvironmentVariableConatinsPackageExecutablePath(Environment.GetEnvironmentVariable(PathName, EnvironmentVariableTarget.Process));
return EnvironmentVariableConatinsPackageExecutablePath(_environmentProvider.GetEnvironmentVariable(PathName, EnvironmentVariableTarget.Process));
}
private bool EnvironmentVariableConatinsPackageExecutablePath(string environmentVariable)
@ -78,15 +86,16 @@ namespace Microsoft.DotNet.ShellShim
return false;
}
return environmentVariable.Split(';').Contains(_packageExecutablePath);
return environmentVariable
.Split(';')
.Any(p => string.Equals(p, _packageExecutablePath, StringComparison.OrdinalIgnoreCase));
}
public void PrintAddPathInstructionIfPathDoesNotExist()
{
if (!PackageExecutablePathExistsForCurrentProcess() && PackageExecutablePathWillExistForFutureNewProcess())
{
_reporter.WriteLine(
CommonLocalizableStrings.EnvironmentPathWindowsNeedReopen);
_reporter.WriteLine(CommonLocalizableStrings.EnvironmentPathWindowsNeedReopen);
}
else if (!PackageExecutablePathWillExistForFutureNewProcess())
{

View file

@ -878,6 +878,11 @@ setx PATH "%PATH%;{0}"
<target state="new">Format version is missing. This tool may not be supported in this SDK version. Please contact the author of the tool.</target>
<note />
</trans-unit>
<trans-unit id="FailedToSetToolsPathEnvironmentVariable">
<source>Failed to add '{0}' to the PATH environment variable. Please add this directory to your PATH to use tools installed with 'dotnet tool install'.</source>
<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>
</body>
</file>
</xliff>

View file

@ -878,6 +878,11 @@ setx PATH "%PATH%;{0}"
<target state="new">Format version is missing. This tool may not be supported in this SDK version. Please contact the author of the tool.</target>
<note />
</trans-unit>
<trans-unit id="FailedToSetToolsPathEnvironmentVariable">
<source>Failed to add '{0}' to the PATH environment variable. Please add this directory to your PATH to use tools installed with 'dotnet tool install'.</source>
<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>
</body>
</file>
</xliff>

View file

@ -878,6 +878,11 @@ setx PATH "%PATH%;{0}"
<target state="new">Format version is missing. This tool may not be supported in this SDK version. Please contact the author of the tool.</target>
<note />
</trans-unit>
<trans-unit id="FailedToSetToolsPathEnvironmentVariable">
<source>Failed to add '{0}' to the PATH environment variable. Please add this directory to your PATH to use tools installed with 'dotnet tool install'.</source>
<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>
</body>
</file>
</xliff>

View file

@ -878,6 +878,11 @@ setx PATH "%PATH%;{0}"
<target state="new">Format version is missing. This tool may not be supported in this SDK version. Please contact the author of the tool.</target>
<note />
</trans-unit>
<trans-unit id="FailedToSetToolsPathEnvironmentVariable">
<source>Failed to add '{0}' to the PATH environment variable. Please add this directory to your PATH to use tools installed with 'dotnet tool install'.</source>
<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>
</body>
</file>
</xliff>

View file

@ -878,6 +878,11 @@ setx PATH "%PATH%;{0}"
<target state="new">Format version is missing. This tool may not be supported in this SDK version. Please contact the author of the tool.</target>
<note />
</trans-unit>
<trans-unit id="FailedToSetToolsPathEnvironmentVariable">
<source>Failed to add '{0}' to the PATH environment variable. Please add this directory to your PATH to use tools installed with 'dotnet tool install'.</source>
<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>
</body>
</file>
</xliff>

View file

@ -878,6 +878,11 @@ setx PATH "%PATH%;{0}"
<target state="new">Format version is missing. This tool may not be supported in this SDK version. Please contact the author of the tool.</target>
<note />
</trans-unit>
<trans-unit id="FailedToSetToolsPathEnvironmentVariable">
<source>Failed to add '{0}' to the PATH environment variable. Please add this directory to your PATH to use tools installed with 'dotnet tool install'.</source>
<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>
</body>
</file>
</xliff>

View file

@ -878,6 +878,11 @@ setx PATH "%PATH%;{0}"
<target state="new">Format version is missing. This tool may not be supported in this SDK version. Please contact the author of the tool.</target>
<note />
</trans-unit>
<trans-unit id="FailedToSetToolsPathEnvironmentVariable">
<source>Failed to add '{0}' to the PATH environment variable. Please add this directory to your PATH to use tools installed with 'dotnet tool install'.</source>
<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>
</body>
</file>
</xliff>

View file

@ -878,6 +878,11 @@ setx PATH "%PATH%;{0}"
<target state="new">Format version is missing. This tool may not be supported in this SDK version. Please contact the author of the tool.</target>
<note />
</trans-unit>
<trans-unit id="FailedToSetToolsPathEnvironmentVariable">
<source>Failed to add '{0}' to the PATH environment variable. Please add this directory to your PATH to use tools installed with 'dotnet tool install'.</source>
<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>
</body>
</file>
</xliff>

View file

@ -878,6 +878,11 @@ setx PATH "%PATH%;{0}"
<target state="new">Format version is missing. This tool may not be supported in this SDK version. Please contact the author of the tool.</target>
<note />
</trans-unit>
<trans-unit id="FailedToSetToolsPathEnvironmentVariable">
<source>Failed to add '{0}' to the PATH environment variable. Please add this directory to your PATH to use tools installed with 'dotnet tool install'.</source>
<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>
</body>
</file>
</xliff>

View file

@ -878,6 +878,11 @@ setx PATH "%PATH%;{0}"
<target state="new">Format version is missing. This tool may not be supported in this SDK version. Please contact the author of the tool.</target>
<note />
</trans-unit>
<trans-unit id="FailedToSetToolsPathEnvironmentVariable">
<source>Failed to add '{0}' to the PATH environment variable. Please add this directory to your PATH to use tools installed with 'dotnet tool install'.</source>
<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>
</body>
</file>
</xliff>

View file

@ -878,6 +878,11 @@ setx PATH "%PATH%;{0}"
<target state="new">Format version is missing. This tool may not be supported in this SDK version. Please contact the author of the tool.</target>
<note />
</trans-unit>
<trans-unit id="FailedToSetToolsPathEnvironmentVariable">
<source>Failed to add '{0}' to the PATH environment variable. Please add this directory to your PATH to use tools installed with 'dotnet tool install'.</source>
<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>
</body>
</file>
</xliff>

View file

@ -878,6 +878,11 @@ setx PATH "%PATH%;{0}"
<target state="new">Format version is missing. This tool may not be supported in this SDK version. Please contact the author of the tool.</target>
<note />
</trans-unit>
<trans-unit id="FailedToSetToolsPathEnvironmentVariable">
<source>Failed to add '{0}' to the PATH environment variable. Please add this directory to your PATH to use tools installed with 'dotnet tool install'.</source>
<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>
</body>
</file>
</xliff>

View file

@ -878,6 +878,11 @@ setx PATH "%PATH%;{0}"
<target state="new">Format version is missing. This tool may not be supported in this SDK version. Please contact the author of the tool.</target>
<note />
</trans-unit>
<trans-unit id="FailedToSetToolsPathEnvironmentVariable">
<source>Failed to add '{0}' to the PATH environment variable. Please add this directory to your PATH to use tools installed with 'dotnet tool install'.</source>
<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>
</body>
</file>
</xliff>

View file

@ -20,9 +20,10 @@ namespace Microsoft.DotNet.Configurer.UnitTests
private Mock<IFirstTimeUseNoticeSentinel> _firstTimeUseNoticeSentinelMock;
private Mock<IAspNetCertificateSentinel> _aspNetCertificateSentinelMock;
private Mock<IAspNetCoreCertificateGenerator> _aspNetCoreCertificateGeneratorMock;
private Mock<IFileSentinel> _toolPathSentinelMock;
private Mock<IEnvironmentProvider> _environmentProviderMock;
private Mock<IReporter> _reporterMock;
private Mock<IEnvironmentPath> _pathAdder;
private Mock<IEnvironmentPath> _pathAdderMock;
public GivenADotnetFirstTimeUseConfigurer()
{
@ -31,9 +32,10 @@ namespace Microsoft.DotNet.Configurer.UnitTests
_firstTimeUseNoticeSentinelMock = new Mock<IFirstTimeUseNoticeSentinel>();
_aspNetCertificateSentinelMock = new Mock<IAspNetCertificateSentinel>();
_aspNetCoreCertificateGeneratorMock = new Mock<IAspNetCoreCertificateGenerator>();
_toolPathSentinelMock = new Mock<IFileSentinel>();
_environmentProviderMock = new Mock<IEnvironmentProvider>();
_reporterMock = new Mock<IReporter>();
_pathAdder = new Mock<IEnvironmentPath>();
_pathAdderMock = new Mock<IEnvironmentPath>();
_environmentProviderMock
.Setup(e => e.GetEnvironmentVariableAsBool("DOTNET_SKIP_FIRST_TIME_EXPERIENCE", false))
@ -54,10 +56,11 @@ namespace Microsoft.DotNet.Configurer.UnitTests
_firstTimeUseNoticeSentinelMock.Object,
_aspNetCertificateSentinelMock.Object,
_aspNetCoreCertificateGeneratorMock.Object,
_toolPathSentinelMock.Object,
_environmentProviderMock.Object,
_reporterMock.Object,
CliFallbackFolderPath,
_pathAdder.Object);
_pathAdderMock.Object);
dotnetFirstTimeUseConfigurer.Configure();
@ -79,10 +82,11 @@ namespace Microsoft.DotNet.Configurer.UnitTests
_firstTimeUseNoticeSentinelMock.Object,
_aspNetCertificateSentinelMock.Object,
_aspNetCoreCertificateGeneratorMock.Object,
_toolPathSentinelMock.Object,
_environmentProviderMock.Object,
_reporterMock.Object,
CliFallbackFolderPath,
_pathAdder.Object);
_pathAdderMock.Object);
dotnetFirstTimeUseConfigurer.Configure();
@ -104,10 +108,11 @@ namespace Microsoft.DotNet.Configurer.UnitTests
_firstTimeUseNoticeSentinelMock.Object,
_aspNetCertificateSentinelMock.Object,
_aspNetCoreCertificateGeneratorMock.Object,
_toolPathSentinelMock.Object,
_environmentProviderMock.Object,
_reporterMock.Object,
CliFallbackFolderPath,
_pathAdder.Object);
_pathAdderMock.Object);
dotnetFirstTimeUseConfigurer.Configure();
@ -126,10 +131,11 @@ namespace Microsoft.DotNet.Configurer.UnitTests
_firstTimeUseNoticeSentinelMock.Object,
_aspNetCertificateSentinelMock.Object,
_aspNetCoreCertificateGeneratorMock.Object,
_toolPathSentinelMock.Object,
_environmentProviderMock.Object,
_reporterMock.Object,
CliFallbackFolderPath,
_pathAdder.Object);
_pathAdderMock.Object);
dotnetFirstTimeUseConfigurer.Configure();
@ -148,10 +154,11 @@ namespace Microsoft.DotNet.Configurer.UnitTests
_firstTimeUseNoticeSentinelMock.Object,
_aspNetCertificateSentinelMock.Object,
_aspNetCoreCertificateGeneratorMock.Object,
_toolPathSentinelMock.Object,
_environmentProviderMock.Object,
_reporterMock.Object,
CliFallbackFolderPath,
_pathAdder.Object);
_pathAdderMock.Object);
dotnetFirstTimeUseConfigurer.Configure();
@ -169,10 +176,11 @@ namespace Microsoft.DotNet.Configurer.UnitTests
_firstTimeUseNoticeSentinelMock.Object,
_aspNetCertificateSentinelMock.Object,
_aspNetCoreCertificateGeneratorMock.Object,
_toolPathSentinelMock.Object,
_environmentProviderMock.Object,
_reporterMock.Object,
CliFallbackFolderPath,
_pathAdder.Object);
_pathAdderMock.Object);
dotnetFirstTimeUseConfigurer.Configure();
@ -190,10 +198,11 @@ namespace Microsoft.DotNet.Configurer.UnitTests
_firstTimeUseNoticeSentinelMock.Object,
_aspNetCertificateSentinelMock.Object,
_aspNetCoreCertificateGeneratorMock.Object,
_toolPathSentinelMock.Object,
_environmentProviderMock.Object,
_reporterMock.Object,
CliFallbackFolderPath,
_pathAdder.Object);
_pathAdderMock.Object);
dotnetFirstTimeUseConfigurer.Configure();
@ -214,10 +223,11 @@ namespace Microsoft.DotNet.Configurer.UnitTests
_firstTimeUseNoticeSentinelMock.Object,
_aspNetCertificateSentinelMock.Object,
_aspNetCoreCertificateGeneratorMock.Object,
_toolPathSentinelMock.Object,
_environmentProviderMock.Object,
_reporterMock.Object,
CliFallbackFolderPath,
_pathAdder.Object);
_pathAdderMock.Object);
dotnetFirstTimeUseConfigurer.Configure();
@ -235,10 +245,11 @@ namespace Microsoft.DotNet.Configurer.UnitTests
_firstTimeUseNoticeSentinelMock.Object,
_aspNetCertificateSentinelMock.Object,
_aspNetCoreCertificateGeneratorMock.Object,
_toolPathSentinelMock.Object,
_environmentProviderMock.Object,
_reporterMock.Object,
CliFallbackFolderPath,
_pathAdder.Object);
_pathAdderMock.Object);
dotnetFirstTimeUseConfigurer.Configure();
@ -257,10 +268,11 @@ namespace Microsoft.DotNet.Configurer.UnitTests
_firstTimeUseNoticeSentinelMock.Object,
_aspNetCertificateSentinelMock.Object,
_aspNetCoreCertificateGeneratorMock.Object,
_toolPathSentinelMock.Object,
_environmentProviderMock.Object,
_reporterMock.Object,
CliFallbackFolderPath,
_pathAdder.Object);
_pathAdderMock.Object);
dotnetFirstTimeUseConfigurer.Configure();
@ -282,10 +294,11 @@ namespace Microsoft.DotNet.Configurer.UnitTests
_firstTimeUseNoticeSentinelMock.Object,
_aspNetCertificateSentinelMock.Object,
_aspNetCoreCertificateGeneratorMock.Object,
_toolPathSentinelMock.Object,
_environmentProviderMock.Object,
_reporterMock.Object,
CliFallbackFolderPath,
_pathAdder.Object);
_pathAdderMock.Object);
dotnetFirstTimeUseConfigurer.Configure();
@ -298,25 +311,49 @@ namespace Microsoft.DotNet.Configurer.UnitTests
}
[Fact]
public void It_adds_executable_package_path_to_environment_path_when_the_first_notice_sentinel_does_not_exist()
public void It_adds_the_tool_path_to_the_environment_if_the_tool_path_sentinel_does_not_exist()
{
_nugetCacheSentinelMock.Setup(n => n.Exists()).Returns(true);
_firstTimeUseNoticeSentinelMock.Setup(n => n.Exists()).Returns(false);
_toolPathSentinelMock.Setup(s => s.Exists()).Returns(false);
var dotnetFirstTimeUseConfigurer = new DotnetFirstTimeUseConfigurer(
_nugetCachePrimerMock.Object,
new FakeCreateWillExistNuGetCacheSentinel(false, true),
new FakeCreateWillExistFirstTimeUseNoticeSentinel(false),
_nugetCacheSentinelMock.Object,
_firstTimeUseNoticeSentinelMock.Object,
_aspNetCertificateSentinelMock.Object,
_aspNetCoreCertificateGeneratorMock.Object,
_toolPathSentinelMock.Object,
_environmentProviderMock.Object,
_reporterMock.Object,
CliFallbackFolderPath,
_pathAdder.Object);
_pathAdderMock.Object);
dotnetFirstTimeUseConfigurer.Configure();
_pathAdder.Verify(p => p.AddPackageExecutablePathToUserPath(), Times.AtLeastOnce);
_toolPathSentinelMock.Verify(s => s.Create(), Times.Once);
_pathAdderMock.Verify(p => p.AddPackageExecutablePathToUserPath(), Times.Once);
}
[Fact]
public void It_does_not_add_the_tool_path_to_the_environment_if_the_tool_path_sentinel_exists()
{
_toolPathSentinelMock.Setup(s => s.Exists()).Returns(true);
var dotnetFirstTimeUseConfigurer = new DotnetFirstTimeUseConfigurer(
_nugetCachePrimerMock.Object,
_nugetCacheSentinelMock.Object,
_firstTimeUseNoticeSentinelMock.Object,
_aspNetCertificateSentinelMock.Object,
_aspNetCoreCertificateGeneratorMock.Object,
_toolPathSentinelMock.Object,
_environmentProviderMock.Object,
_reporterMock.Object,
CliFallbackFolderPath,
_pathAdderMock.Object);
dotnetFirstTimeUseConfigurer.Configure();
_toolPathSentinelMock.Verify(s => s.Create(), Times.Never);
_pathAdderMock.Verify(p => p.AddPackageExecutablePathToUserPath(), Times.Never);
}
[Fact]
@ -330,10 +367,11 @@ namespace Microsoft.DotNet.Configurer.UnitTests
_firstTimeUseNoticeSentinelMock.Object,
_aspNetCertificateSentinelMock.Object,
_aspNetCoreCertificateGeneratorMock.Object,
_toolPathSentinelMock.Object,
_environmentProviderMock.Object,
_reporterMock.Object,
CliFallbackFolderPath,
_pathAdder.Object);
_pathAdderMock.Object);
dotnetFirstTimeUseConfigurer.Configure();
@ -359,10 +397,11 @@ namespace Microsoft.DotNet.Configurer.UnitTests
_firstTimeUseNoticeSentinelMock.Object,
_aspNetCertificateSentinelMock.Object,
_aspNetCoreCertificateGeneratorMock.Object,
_toolPathSentinelMock.Object,
_environmentProviderMock.Object,
_reporterMock.Object,
CliFallbackFolderPath,
_pathAdder.Object);
_pathAdderMock.Object);
dotnetFirstTimeUseConfigurer.Configure();
@ -380,10 +419,11 @@ namespace Microsoft.DotNet.Configurer.UnitTests
_firstTimeUseNoticeSentinelMock.Object,
_aspNetCertificateSentinelMock.Object,
_aspNetCoreCertificateGeneratorMock.Object,
_toolPathSentinelMock.Object,
_environmentProviderMock.Object,
_reporterMock.Object,
CliFallbackFolderPath,
_pathAdder.Object);
_pathAdderMock.Object);
dotnetFirstTimeUseConfigurer.Configure();
@ -405,10 +445,11 @@ namespace Microsoft.DotNet.Configurer.UnitTests
_firstTimeUseNoticeSentinelMock.Object,
_aspNetCertificateSentinelMock.Object,
_aspNetCoreCertificateGeneratorMock.Object,
_toolPathSentinelMock.Object,
_environmentProviderMock.Object,
_reporterMock.Object,
CliFallbackFolderPath,
_pathAdder.Object);
_pathAdderMock.Object);
dotnetFirstTimeUseConfigurer.Configure();
@ -430,10 +471,11 @@ namespace Microsoft.DotNet.Configurer.UnitTests
_firstTimeUseNoticeSentinelMock.Object,
_aspNetCertificateSentinelMock.Object,
_aspNetCoreCertificateGeneratorMock.Object,
_toolPathSentinelMock.Object,
_environmentProviderMock.Object,
_reporterMock.Object,
CliFallbackFolderPath,
_pathAdder.Object);
_pathAdderMock.Object);
dotnetFirstTimeUseConfigurer.Configure();
@ -454,10 +496,11 @@ namespace Microsoft.DotNet.Configurer.UnitTests
_firstTimeUseNoticeSentinelMock.Object,
_aspNetCertificateSentinelMock.Object,
_aspNetCoreCertificateGeneratorMock.Object,
_toolPathSentinelMock.Object,
_environmentProviderMock.Object,
_reporterMock.Object,
CliFallbackFolderPath,
_pathAdder.Object);
_pathAdderMock.Object);
dotnetFirstTimeUseConfigurer.Configure();
@ -467,61 +510,48 @@ namespace Microsoft.DotNet.Configurer.UnitTests
_aspNetCoreCertificateGeneratorMock.Verify(s => s.GenerateAspNetCoreDevelopmentCertificate(), Times.Once);
}
private class FakeCreateWillExistFirstTimeUseNoticeSentinel : IFirstTimeUseNoticeSentinel
[Fact]
public void It_adds_the_tool_path_to_the_environment_if_the_first_run_experience_is_not_skipped()
{
private bool _exists;
var dotnetFirstTimeUseConfigurer = new DotnetFirstTimeUseConfigurer(
_nugetCachePrimerMock.Object,
_nugetCacheSentinelMock.Object,
_firstTimeUseNoticeSentinelMock.Object,
_aspNetCertificateSentinelMock.Object,
_aspNetCoreCertificateGeneratorMock.Object,
_toolPathSentinelMock.Object,
_environmentProviderMock.Object,
_reporterMock.Object,
CliFallbackFolderPath,
_pathAdderMock.Object);
public FakeCreateWillExistFirstTimeUseNoticeSentinel(bool exists)
{
_exists = exists;
}
dotnetFirstTimeUseConfigurer.Configure();
public void Dispose()
{
}
public bool Exists()
{
return _exists;
}
public void CreateIfNotExists()
{
_exists = true;
}
_pathAdderMock.Verify(p => p.AddPackageExecutablePathToUserPath(), Times.Once);
}
private class FakeCreateWillExistNuGetCacheSentinel : INuGetCacheSentinel
[Fact]
public void It_does_not_add_the_tool_path_to_the_environment_if_the_first_run_experience_is_skipped()
{
private bool _inProgressSentinelAlreadyExists;
private bool _exists;
_environmentProviderMock
.Setup(e => e.GetEnvironmentVariableAsBool("DOTNET_SKIP_FIRST_TIME_EXPERIENCE", false))
.Returns(true);
public FakeCreateWillExistNuGetCacheSentinel(bool inProgressSentinelAlreadyExists, bool exists)
{
_inProgressSentinelAlreadyExists = inProgressSentinelAlreadyExists;
_exists = exists;
}
var dotnetFirstTimeUseConfigurer = new DotnetFirstTimeUseConfigurer(
_nugetCachePrimerMock.Object,
_nugetCacheSentinelMock.Object,
_firstTimeUseNoticeSentinelMock.Object,
_aspNetCertificateSentinelMock.Object,
_aspNetCoreCertificateGeneratorMock.Object,
_toolPathSentinelMock.Object,
_environmentProviderMock.Object,
_reporterMock.Object,
CliFallbackFolderPath,
_pathAdderMock.Object);
public void Dispose()
{
}
dotnetFirstTimeUseConfigurer.Configure();
public bool InProgressSentinelAlreadyExists()
{
return _inProgressSentinelAlreadyExists;
}
public bool Exists()
{
return _exists;
}
public void CreateIfNotExists()
{
_exists = true;
}
public bool UnauthorizedAccess { get; set; }
_pathAdderMock.Verify(p => p.AddPackageExecutablePathToUserPath(), Times.Never);
}
}
}

View file

@ -1,48 +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 Microsoft.DotNet.Cli.Utils;
namespace Microsoft.DotNet.ShellShim.Tests
{
internal class FakeEnvironmentProvider : IEnvironmentProvider
{
private readonly Dictionary<string, string> _environmentVariables;
public FakeEnvironmentProvider(Dictionary<string, string> environmentVariables)
{
_environmentVariables =
environmentVariables ?? throw new ArgumentNullException(nameof(environmentVariables));
}
public IEnumerable<string> ExecutableExtensions { get; }
public string GetCommandPath(string commandName, params string[] extensions)
{
throw new NotImplementedException();
}
public string GetCommandPathFromRootPath(string rootPath, string commandName, params string[] extensions)
{
throw new NotImplementedException();
}
public string GetCommandPathFromRootPath(string rootPath, string commandName,
IEnumerable<string> extensions)
{
throw new NotImplementedException();
}
public bool GetEnvironmentVariableAsBool(string name, bool defaultValue)
{
throw new NotImplementedException();
}
public string GetEnvironmentVariable(string name)
{
return _environmentVariables.ContainsKey(name) ? _environmentVariables[name] : "";
}
}
}

View file

@ -1,64 +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 Microsoft.Extensions.EnvironmentAbstractions;
namespace Microsoft.DotNet.ShellShim.Tests
{
internal class FakeFile : IFile
{
private Dictionary<string, string> _files;
public FakeFile(Dictionary<string, string> files)
{
_files = files;
}
public bool Exists(string path)
{
return _files.ContainsKey(path);
}
public string ReadAllText(string path)
{
throw new NotImplementedException();
}
public Stream OpenRead(string path)
{
throw new NotImplementedException();
}
public Stream OpenFile(string path, FileMode fileMode, FileAccess fileAccess, FileShare fileShare,
int bufferSize,
FileOptions fileOptions)
{
throw new NotImplementedException();
}
public void CreateEmptyFile(string path)
{
_files.Add(path, String.Empty);
}
public void WriteAllText(string path, string content)
{
_files[path] = content;
}
public void Move(string source, string destination)
{
throw new NotImplementedException();
}
public void Delete(string path)
{
throw new NotImplementedException();
}
public static FakeFile Empty => new FakeFile(new Dictionary<string, string>());
}
}

View file

@ -2,14 +2,13 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using FluentAssertions;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Configurer;
using Microsoft.DotNet.Tools;
using Microsoft.DotNet.Tools.Test.Utilities;
using Microsoft.Extensions.DependencyModel.Tests;
using Moq;
using Xunit;
namespace Microsoft.DotNet.ShellShim.Tests
@ -17,66 +16,138 @@ namespace Microsoft.DotNet.ShellShim.Tests
public class LinuxEnvironmentPathTests
{
[Fact]
public void GivenEnvironmentAndReporterItCanPrintOutInstructionToAddPath()
public void GivenPathNotSetItPrintsManualInstructions()
{
var reporter = new BufferedReporter();
var linuxEnvironmentPath = new LinuxEnvironmentPath(
new BashPathUnderHomeDirectory("/myhome", "executable/path"),
var toolsPath = new BashPathUnderHomeDirectory("/home/user", ".dotnet/tools");
var pathValue = @"/usr/bin";
var provider = new Mock<IEnvironmentProvider>(MockBehavior.Strict);
provider
.Setup(p => p.GetEnvironmentVariable("PATH"))
.Returns(pathValue);
var environmentPath = new LinuxEnvironmentPath(
toolsPath,
reporter,
new FakeEnvironmentProvider(
new Dictionary<string, string>
{
{"PATH", ""}
}),
FakeFile.Empty);
provider.Object,
FileSystemMockBuilder.Empty.File);
linuxEnvironmentPath.PrintAddPathInstructionIfPathDoesNotExist();
environmentPath.PrintAddPathInstructionIfPathDoesNotExist();
// similar to https://code.visualstudio.com/docs/setup/mac
reporter.Lines.Should().Equal(
string.Format(
CommonLocalizableStrings.EnvironmentPathLinuxManualInstructions,
"/myhome/executable/path"));
toolsPath.Path));
}
[Fact]
public void GivenEnvironmentAndReporterItPrintsNothingWhenenvironmentExists()
public void GivenPathNotSetAndProfileExistsItPrintsLogoutMessage()
{
var reporter = new BufferedReporter();
var linuxEnvironmentPath = new LinuxEnvironmentPath(
new BashPathUnderHomeDirectory("/myhome", "executable/path"),
reporter,
new FakeEnvironmentProvider(
new Dictionary<string, string>
{
{"PATH", @"/myhome/executable/path"}
}),
FakeFile.Empty);
var toolsPath = new BashPathUnderHomeDirectory("/home/user", ".dotnet/tools");
var pathValue = @"/usr/bin";
var provider = new Mock<IEnvironmentProvider>(MockBehavior.Strict);
linuxEnvironmentPath.PrintAddPathInstructionIfPathDoesNotExist();
provider
.Setup(p => p.GetEnvironmentVariable("PATH"))
.Returns(pathValue);
var environmentPath = new LinuxEnvironmentPath(
toolsPath,
reporter,
provider.Object,
new FileSystemMockBuilder()
.AddFile(LinuxEnvironmentPath.DotnetCliToolsProfilePath, "")
.Build()
.File);
environmentPath.PrintAddPathInstructionIfPathDoesNotExist();
reporter.Lines.Should().Equal(CommonLocalizableStrings.EnvironmentPathLinuxNeedLogout);
}
[Theory]
[InlineData("/home/user/.dotnet/tools")]
[InlineData("~/.dotnet/tools")]
public void GivenPathSetItPrintsNothing(string toolsDiretoryOnPath)
{
var reporter = new BufferedReporter();
var toolsPath = new BashPathUnderHomeDirectory("/home/user", ".dotnet/tools");
var pathValue = @"/usr/bin";
var provider = new Mock<IEnvironmentProvider>(MockBehavior.Strict);
provider
.Setup(p => p.GetEnvironmentVariable("PATH"))
.Returns(pathValue + ":" + toolsDiretoryOnPath);
var environmentPath = new LinuxEnvironmentPath(
toolsPath,
reporter,
provider.Object,
FileSystemMockBuilder.Empty.File);
environmentPath.PrintAddPathInstructionIfPathDoesNotExist();
reporter.Lines.Should().BeEmpty();
}
[Fact]
public void GivenAddPackageExecutablePathToUserPathJustRunItPrintsInstructionToLogout()
public void GivenPathSetItDoesNotAddPathToEnvironment()
{
var reporter = new BufferedReporter();
var linuxEnvironmentPath = new LinuxEnvironmentPath(
new BashPathUnderHomeDirectory("/myhome", "executable/path"),
var toolsPath = new BashPathUnderHomeDirectory("/home/user", ".dotnet/tools");
var pathValue = @"/usr/bin";
var provider = new Mock<IEnvironmentProvider>(MockBehavior.Strict);
var fileSystem = new FileSystemMockBuilder().Build().File;
provider
.Setup(p => p.GetEnvironmentVariable("PATH"))
.Returns(pathValue + ":" + toolsPath.Path);
var environmentPath = new LinuxEnvironmentPath(
toolsPath,
reporter,
new FakeEnvironmentProvider(
new Dictionary<string, string>
{
{"PATH", @""}
}),
FakeFile.Empty);
linuxEnvironmentPath.AddPackageExecutablePathToUserPath();
provider.Object,
fileSystem);
linuxEnvironmentPath.PrintAddPathInstructionIfPathDoesNotExist();
environmentPath.AddPackageExecutablePathToUserPath();
reporter.Lines.Should()
.Equal(CommonLocalizableStrings.EnvironmentPathLinuxNeedLogout);
reporter.Lines.Should().BeEmpty();
fileSystem
.Exists(LinuxEnvironmentPath.DotnetCliToolsProfilePath)
.Should()
.Be(false);
}
[Fact]
public void GivenPathNotSetItAddsToEnvironment()
{
var reporter = new BufferedReporter();
var toolsPath = new BashPathUnderHomeDirectory("/home/user", ".dotnet/tools");
var pathValue = @"/usr/bin";
var provider = new Mock<IEnvironmentProvider>(MockBehavior.Strict);
var fileSystem = new FileSystemMockBuilder().Build().File;
provider
.Setup(p => p.GetEnvironmentVariable("PATH"))
.Returns(pathValue);
var environmentPath = new LinuxEnvironmentPath(
toolsPath,
reporter,
provider.Object,
fileSystem);
environmentPath.AddPackageExecutablePathToUserPath();
reporter.Lines.Should().BeEmpty();
fileSystem
.ReadAllText(LinuxEnvironmentPath.DotnetCliToolsProfilePath)
.Should()
.Be($"export PATH=\"$PATH:{toolsPath.PathWithDollar}\"");
}
}
}

View file

@ -2,14 +2,13 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using FluentAssertions;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Configurer;
using Microsoft.DotNet.Tools;
using Microsoft.DotNet.Tools.Test.Utilities;
using Microsoft.Extensions.DependencyModel.Tests;
using Moq;
using Xunit;
namespace Microsoft.DotNet.ShellShim.Tests
@ -17,67 +16,138 @@ namespace Microsoft.DotNet.ShellShim.Tests
public class OsxEnvironmentPathTests
{
[Fact]
public void GivenEnvironmentAndReporterItCanPrintOutInstructionToAddPath()
public void GivenPathNotSetItPrintsManualInstructions()
{
var reporter = new BufferedReporter();
var osxEnvironmentPath = new OSXEnvironmentPath(
new BashPathUnderHomeDirectory("/myhome", "executable/path"),
var toolsPath = new BashPathUnderHomeDirectory("/home/user", ".dotnet/tools");
var pathValue = @"/usr/bin";
var provider = new Mock<IEnvironmentProvider>(MockBehavior.Strict);
provider
.Setup(p => p.GetEnvironmentVariable("PATH"))
.Returns(pathValue);
var environmentPath = new OSXEnvironmentPath(
toolsPath,
reporter,
new FakeEnvironmentProvider(
new Dictionary<string, string>
{
{"PATH", ""}
}),
FakeFile.Empty);
provider.Object,
FileSystemMockBuilder.Empty.File);
osxEnvironmentPath.PrintAddPathInstructionIfPathDoesNotExist();
environmentPath.PrintAddPathInstructionIfPathDoesNotExist();
// similar to https://code.visualstudio.com/docs/setup/mac
reporter.Lines.Should().Equal(
string.Format(
CommonLocalizableStrings.EnvironmentPathOSXManualInstructions,
"/myhome/executable/path"));
toolsPath.Path));
}
[Fact]
public void GivenPathNotSetAndProfileExistsItPrintsReopenMessage()
{
var reporter = new BufferedReporter();
var toolsPath = new BashPathUnderHomeDirectory("/home/user", ".dotnet/tools");
var pathValue = @"/usr/bin";
var provider = new Mock<IEnvironmentProvider>(MockBehavior.Strict);
provider
.Setup(p => p.GetEnvironmentVariable("PATH"))
.Returns(pathValue);
var environmentPath = new OSXEnvironmentPath(
toolsPath,
reporter,
provider.Object,
new FileSystemMockBuilder()
.AddFile(OSXEnvironmentPath.DotnetCliToolsPathsDPath, "")
.Build()
.File);
environmentPath.PrintAddPathInstructionIfPathDoesNotExist();
reporter.Lines.Should().Equal(CommonLocalizableStrings.EnvironmentPathOSXNeedReopen);
}
[Theory]
[InlineData("/myhome/executable/path")]
[InlineData("~/executable/path")]
public void GivenEnvironmentAndReporterItPrintsNothingWhenenvironmentExists(string existingPath)
[InlineData("/home/user/.dotnet/tools")]
[InlineData("~/.dotnet/tools")]
public void GivenPathSetItPrintsNothing(string toolsDiretoryOnPath)
{
var reporter = new BufferedReporter();
var osxEnvironmentPath = new OSXEnvironmentPath(
new BashPathUnderHomeDirectory("/myhome", "executable/path"),
reporter,
new FakeEnvironmentProvider(
new Dictionary<string, string>
{
{"PATH", existingPath}
}),
FakeFile.Empty);
var toolsPath = new BashPathUnderHomeDirectory("/home/user", ".dotnet/tools");
var pathValue = @"/usr/bin";
var provider = new Mock<IEnvironmentProvider>(MockBehavior.Strict);
osxEnvironmentPath.PrintAddPathInstructionIfPathDoesNotExist();
provider
.Setup(p => p.GetEnvironmentVariable("PATH"))
.Returns(pathValue + ":" + toolsDiretoryOnPath);
var environmentPath = new OSXEnvironmentPath(
toolsPath,
reporter,
provider.Object,
FileSystemMockBuilder.Empty.File);
environmentPath.PrintAddPathInstructionIfPathDoesNotExist();
reporter.Lines.Should().BeEmpty();
}
[Fact]
public void GivenAddPackageExecutablePathToUserPathJustRunItPrintsInstructionToLogout()
public void GivenPathSetItDoesNotAddPathToEnvironment()
{
var reporter = new BufferedReporter();
var osxEnvironmentPath = new OSXEnvironmentPath(
new BashPathUnderHomeDirectory("/myhome", "executable/path"),
var toolsPath = new BashPathUnderHomeDirectory("/home/user", ".dotnet/tools");
var pathValue = @"/usr/bin";
var provider = new Mock<IEnvironmentProvider>(MockBehavior.Strict);
var fileSystem = new FileSystemMockBuilder().Build().File;
provider
.Setup(p => p.GetEnvironmentVariable("PATH"))
.Returns(pathValue + ":" + toolsPath.Path);
var environmentPath = new OSXEnvironmentPath(
toolsPath,
reporter,
new FakeEnvironmentProvider(
new Dictionary<string, string>
{
{"PATH", @""}
}),
FakeFile.Empty);
osxEnvironmentPath.AddPackageExecutablePathToUserPath();
provider.Object,
fileSystem);
osxEnvironmentPath.PrintAddPathInstructionIfPathDoesNotExist();
environmentPath.AddPackageExecutablePathToUserPath();
reporter.Lines.Should().Equal(CommonLocalizableStrings.EnvironmentPathOSXNeedReopen);
reporter.Lines.Should().BeEmpty();
fileSystem
.Exists(OSXEnvironmentPath.DotnetCliToolsPathsDPath)
.Should()
.Be(false);
}
[Fact]
public void GivenPathNotSetItAddsToEnvironment()
{
var reporter = new BufferedReporter();
var toolsPath = new BashPathUnderHomeDirectory("/home/user", ".dotnet/tools");
var pathValue = @"/usr/bin";
var provider = new Mock<IEnvironmentProvider>(MockBehavior.Strict);
var fileSystem = new FileSystemMockBuilder().Build().File;
provider
.Setup(p => p.GetEnvironmentVariable("PATH"))
.Returns(pathValue);
var environmentPath = new OSXEnvironmentPath(
toolsPath,
reporter,
provider.Object,
fileSystem);
environmentPath.AddPackageExecutablePathToUserPath();
reporter.Lines.Should().BeEmpty();
fileSystem
.ReadAllText(OSXEnvironmentPath.DotnetCliToolsPathsDPath)
.Should()
.Be(toolsPath.PathWithTilde);
}
}
}

View file

@ -0,0 +1,175 @@
// 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 FluentAssertions;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Configurer;
using Microsoft.DotNet.Tools;
using Microsoft.DotNet.Tools.Test.Utilities;
using Moq;
using Xunit;
namespace Microsoft.DotNet.ShellShim.Tests
{
public class WindowsEnvironmentPathTests
{
[Fact]
public void GivenPathNotSetItPrintsManualInstructions()
{
var reporter = new BufferedReporter();
var toolsPath = @"C:\Tools";
var pathValue = @"C:\Other";
var provider = new Mock<IEnvironmentProvider>(MockBehavior.Strict);
provider
.Setup(p => p.GetEnvironmentVariable(
"PATH",
It.Is<EnvironmentVariableTarget>(t =>
t == EnvironmentVariableTarget.Process ||
t == EnvironmentVariableTarget.User ||
t == EnvironmentVariableTarget.Machine)))
.Returns(pathValue);
var environmentPath = new WindowsEnvironmentPath(toolsPath, reporter, provider.Object);
environmentPath.PrintAddPathInstructionIfPathDoesNotExist();
reporter.Lines.Should().Equal(
string.Format(
CommonLocalizableStrings.EnvironmentPathWindowsManualInstructions,
toolsPath));
}
[Fact]
public void GivenPathNotSetInProcessItPrintsReopenNotice()
{
var reporter = new BufferedReporter();
var toolsPath = @"C:\Tools";
var pathValue = @"C:\Other";
var provider = new Mock<IEnvironmentProvider>(MockBehavior.Strict);
provider
.Setup(p => p.GetEnvironmentVariable("PATH", EnvironmentVariableTarget.Process))
.Returns(pathValue);
provider
.Setup(p => p.GetEnvironmentVariable(
"PATH",
It.Is<EnvironmentVariableTarget>(t =>
t == EnvironmentVariableTarget.User ||
t == EnvironmentVariableTarget.Machine)))
.Returns(pathValue + ";" + toolsPath);
var environmentPath = new WindowsEnvironmentPath(toolsPath, reporter, provider.Object);
environmentPath.PrintAddPathInstructionIfPathDoesNotExist();
reporter.Lines.Should().Equal(CommonLocalizableStrings.EnvironmentPathWindowsNeedReopen);
}
[Fact]
public void GivenPathSetInProcessAndEnvironmentItPrintsNothing()
{
var reporter = new BufferedReporter();
var toolsPath = @"C:\Tools";
var pathValue = @"C:\Other";
var provider = new Mock<IEnvironmentProvider>(MockBehavior.Strict);
provider
.Setup(p => p.GetEnvironmentVariable(
"PATH",
It.Is<EnvironmentVariableTarget>(t =>
t == EnvironmentVariableTarget.Process ||
t == EnvironmentVariableTarget.User ||
t == EnvironmentVariableTarget.Machine)))
.Returns(pathValue + ";" + toolsPath);
var environmentPath = new WindowsEnvironmentPath(toolsPath, reporter, provider.Object);
environmentPath.PrintAddPathInstructionIfPathDoesNotExist();
reporter.Lines.Should().BeEmpty();
}
[Fact]
public void GivenPathSetItDoesNotAddPathToEnvironment()
{
var reporter = new BufferedReporter();
var toolsPath = @"C:\Tools";
var pathValue = @"C:\Other";
var provider = new Mock<IEnvironmentProvider>(MockBehavior.Strict);
provider
.Setup(p => p.GetEnvironmentVariable(
"PATH",
It.Is<EnvironmentVariableTarget>(t =>
t == EnvironmentVariableTarget.Process ||
t == EnvironmentVariableTarget.User ||
t == EnvironmentVariableTarget.Machine)))
.Returns(pathValue + ";" + toolsPath);
var environmentPath = new WindowsEnvironmentPath(toolsPath, reporter, provider.Object);
environmentPath.AddPackageExecutablePathToUserPath();
reporter.Lines.Should().BeEmpty();
}
[Fact]
public void GivenPathNotSetItAddsToEnvironment()
{
var reporter = new BufferedReporter();
var toolsPath = @"C:\Tools";
var pathValue = @"C:\Other";
var provider = new Mock<IEnvironmentProvider>(MockBehavior.Strict);
provider
.Setup(p => p.GetEnvironmentVariable(
"PATH",
It.Is<EnvironmentVariableTarget>(t =>
t == EnvironmentVariableTarget.Process ||
t == EnvironmentVariableTarget.User ||
t == EnvironmentVariableTarget.Machine)))
.Returns(pathValue);
provider
.Setup(p => p.SetEnvironmentVariable("PATH", pathValue + ";" + toolsPath, EnvironmentVariableTarget.User));
var environmentPath = new WindowsEnvironmentPath(toolsPath, reporter, provider.Object);
environmentPath.AddPackageExecutablePathToUserPath();
reporter.Lines.Should().BeEmpty();
}
[Fact]
public void GivenSecurityExceptionItPrintsWarning()
{
var reporter = new BufferedReporter();
var toolsPath = @"C:\Tools";
var pathValue = @"C:\Other";
var provider = new Mock<IEnvironmentProvider>(MockBehavior.Strict);
provider
.Setup(p => p.GetEnvironmentVariable(
"PATH",
It.Is<EnvironmentVariableTarget>(t =>
t == EnvironmentVariableTarget.Process ||
t == EnvironmentVariableTarget.User ||
t == EnvironmentVariableTarget.Machine)))
.Returns(pathValue);
provider
.Setup(p => p.SetEnvironmentVariable("PATH", pathValue + ";" + toolsPath, EnvironmentVariableTarget.User))
.Throws(new System.Security.SecurityException());
var environmentPath = new WindowsEnvironmentPath(toolsPath, reporter, provider.Object);
environmentPath.AddPackageExecutablePathToUserPath();
reporter.Lines.Should().Equal(
string.Format(
CommonLocalizableStrings.FailedToSetToolsPathEnvironmentVariable,
toolsPath).Yellow());
}
}
}