Merge pull request #8886 from peterhuene/first-run-tool-path

Fix failure to add tools path on restricted Windows environments.
This commit is contained in:
Peter Huene 2018-04-03 21:14:30 -07:00 committed by GitHub
commit cee27d3970
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
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());
}
}
}