Skip to content

Commit b425b27

Browse files
committed
Introduce IEnvironment to replace IEnvironmentVariables
Replace the simple IEnvironmentVariables envar wrapper with a component that includes the envars and other methods for manipulating the $PATH. This will be useful in future commits where we need to inspect and update the $PATH for postinstall and preuninstall. Also expose the new IEnvironment and existing IGit components on the command context in prep. Add a new path comparison method to IFileSystem and introduce platform-specific implementations.
1 parent d097b41 commit b425b27

File tree

16 files changed

+597
-285
lines changed

16 files changed

+597
-285
lines changed

src/shared/Microsoft.Git.CredentialManager.Tests/SettingsTests.cs

Lines changed: 110 additions & 135 deletions
Large diffs are not rendered by default.

src/shared/Microsoft.Git.CredentialManager.Tests/StringExtensionsTests.cs

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -203,12 +203,12 @@ public void StringExtensions_TrimUntilIndexOf_String(string input, string trim,
203203
}
204204

205205
[Theory]
206-
[InlineData("fooTRIMbar", "TRIM", StringComparison.Ordinal, "bar")]
207-
[InlineData("fooTRIMbar", "trim", StringComparison.Ordinal, "fooTRIMbar")]
208-
[InlineData("fooTRIMbar", "tRiM", StringComparison.Ordinal, "fooTRIMbar")]
209-
[InlineData("fooTRIMbar", "TRIM", StringComparison.OrdinalIgnoreCase, "bar")]
210-
[InlineData("fooTRIMbar", "trim", StringComparison.OrdinalIgnoreCase, "bar")]
211-
[InlineData("fooTRIMbar", "tRiM", StringComparison.OrdinalIgnoreCase, "bar")]
206+
[InlineData("fooTRIMbarTRIMsoup", "TRIM", StringComparison.Ordinal, "barTRIMsoup")]
207+
[InlineData("fooTRIMbarTRIMsoup", "trim", StringComparison.Ordinal, "fooTRIMbarTRIMsoup")]
208+
[InlineData("fooTRIMbarTRIMsoup", "tRiM", StringComparison.Ordinal, "fooTRIMbarTRIMsoup")]
209+
[InlineData("fooTRIMbarTRIMsoup", "TRIM", StringComparison.OrdinalIgnoreCase, "barTRIMsoup")]
210+
[InlineData("fooTRIMbarTRIMsoup", "trim", StringComparison.OrdinalIgnoreCase, "barTRIMsoup")]
211+
[InlineData("fooTRIMbarTRIMsoup", "tRiM", StringComparison.OrdinalIgnoreCase, "barTRIMsoup")]
212212
public void StringExtensions_TrimUntilIndexOf_String_ComparisonType(string input, string trim, StringComparison comparisonType, string expected)
213213
{
214214
string actual = StringExtensions.TrimUntilIndexOf(input, trim, comparisonType);
@@ -254,12 +254,12 @@ public void StringExtensions_TrimUntilLastIndexOf_String(string input, string tr
254254
}
255255

256256
[Theory]
257-
[InlineData("fooTRIMbar", "TRIM", StringComparison.Ordinal, "bar")]
258-
[InlineData("fooTRIMbar", "trim", StringComparison.Ordinal, "fooTRIMbar")]
259-
[InlineData("fooTRIMbar", "tRiM", StringComparison.Ordinal, "fooTRIMbar")]
260-
[InlineData("fooTRIMbar", "TRIM", StringComparison.OrdinalIgnoreCase, "bar")]
261-
[InlineData("fooTRIMbar", "trim", StringComparison.OrdinalIgnoreCase, "bar")]
262-
[InlineData("fooTRIMbar", "tRiM", StringComparison.OrdinalIgnoreCase, "bar")]
257+
[InlineData("fooTRIMbarTRIMsoup", "TRIM", StringComparison.Ordinal, "soup")]
258+
[InlineData("fooTRIMbarTRIMsoup", "trim", StringComparison.Ordinal, "fooTRIMbarTRIMsoup")]
259+
[InlineData("fooTRIMbarTRIMsoup", "tRiM", StringComparison.Ordinal, "fooTRIMbarTRIMsoup")]
260+
[InlineData("fooTRIMbarTRIMsoup", "TRIM", StringComparison.OrdinalIgnoreCase, "soup")]
261+
[InlineData("fooTRIMbarTRIMsoup", "trim", StringComparison.OrdinalIgnoreCase, "soup")]
262+
[InlineData("fooTRIMbarTRIMsoup", "tRiM", StringComparison.OrdinalIgnoreCase, "soup")]
263263
public void StringExtensions_TrimUntilLastIndexOf_String_ComparisonType(string input, string trim, StringComparison comparisonType, string expected)
264264
{
265265
string actual = StringExtensions.TrimUntilLastIndexOf(input, trim, comparisonType);
@@ -271,5 +271,28 @@ public void StringExtensions_TrimUntilLastIndexOf_String_Null_ThrowsArgumentNull
271271
{
272272
Assert.Throws<ArgumentNullException>(() => StringExtensions.TrimUntilLastIndexOf(null, "://"));
273273
}
274+
275+
[Theory]
276+
[InlineData("fooTRIMbar", "TRIM", "foobar")]
277+
[InlineData("fooTRIMbar", "trim", "fooTRIMbar")]
278+
[InlineData("fooTRIMbar", "tRiM", "fooTRIMbar")]
279+
public void StringExtensions_TrimMiddle_String(string input, string trim, string expected)
280+
{
281+
string actual = StringExtensions.TrimMiddle(input, trim);
282+
Assert.Equal(expected, actual);
283+
}
284+
285+
[Theory]
286+
[InlineData("fooTRIMbar", "TRIM", StringComparison.Ordinal, "foobar")]
287+
[InlineData("fooTRIMbar", "trim", StringComparison.Ordinal, "fooTRIMbar")]
288+
[InlineData("fooTRIMbar", "tRiM", StringComparison.Ordinal, "fooTRIMbar")]
289+
[InlineData("fooTRIMbar", "TRIM", StringComparison.OrdinalIgnoreCase, "foobar")]
290+
[InlineData("fooTRIMbar", "trim", StringComparison.OrdinalIgnoreCase, "foobar")]
291+
[InlineData("fooTRIMbar", "tRiM", StringComparison.OrdinalIgnoreCase, "foobar")]
292+
public void StringExtensions_TrimMiddle_String_ComparisonType(string input, string trim, StringComparison comparisonType, string expected)
293+
{
294+
string actual = StringExtensions.TrimMiddle(input, trim, comparisonType);
295+
Assert.Equal(expected, actual);
296+
}
274297
}
275298
}

src/shared/Microsoft.Git.CredentialManager/CommandContext.cs

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT license.
33
using System;
4-
using System.IO;
5-
using System.Text;
64
using Microsoft.Git.CredentialManager.Interop;
75
using Microsoft.Git.CredentialManager.Interop.MacOS;
86
using Microsoft.Git.CredentialManager.Interop.Posix;
@@ -49,46 +47,55 @@ public interface ICommandContext : IDisposable
4947
/// Factory for creating new <see cref="System.Net.Http.HttpClient"/> instances.
5048
/// </summary>
5149
IHttpClientFactory HttpClientFactory { get; }
50+
51+
/// <summary>
52+
/// Component for interacting with Git.
53+
/// </summary>
54+
IGit Git { get; }
55+
56+
/// <summary>
57+
/// The current process environment.
58+
/// </summary>
59+
IEnvironment Environment { get; }
5260
}
5361

5462
/// <summary>
5563
/// Real command execution environment using the actual <see cref="Console"/>, file system calls and environment.
5664
/// </summary>
5765
public class CommandContext : DisposableObject, ICommandContext
5866
{
59-
private readonly IGit _git;
60-
6167
public CommandContext()
6268
{
6369
Streams = new StandardStreams();
64-
Trace = new Trace();
65-
FileSystem = new FileSystem();
66-
67-
_git = new LibGit2(Trace);
68-
var envars = new EnvironmentVariables(Environment.GetEnvironmentVariables());
69-
string repoPath = _git.GetRepositoryPath(FileSystem.GetCurrentDirectory());
70-
Settings = new Settings(envars, _git, repoPath);
71-
72-
HttpClientFactory = new HttpClientFactory(Trace, Settings, Streams);
70+
Trace = new Trace();
71+
Git = new LibGit2(Trace);
7372

7473
if (PlatformUtils.IsWindows())
7574
{
76-
Terminal = new WindowsTerminal(Trace);
75+
FileSystem = new WindowsFileSystem();
76+
Environment = new WindowsEnvironment(FileSystem);
77+
Terminal = new WindowsTerminal(Trace);
7778
CredentialStore = WindowsCredentialManager.Open();
7879
}
7980
else if (PlatformUtils.IsPosix())
8081
{
81-
Terminal = new PosixTerminal(Trace);
82-
8382
if (PlatformUtils.IsMacOS())
8483
{
84+
FileSystem = new MacOSFileSystem();
8585
CredentialStore = MacOSKeychain.Open();
8686
}
8787
else if (PlatformUtils.IsLinux())
8888
{
8989
throw new NotImplementedException();
9090
}
91+
92+
Environment = new PosixEnvironment(FileSystem);
93+
Terminal = new PosixTerminal(Trace);
9194
}
95+
96+
string repoPath = Git.GetRepositoryPath(FileSystem.GetCurrentDirectory());
97+
Settings = new Settings(Environment, Git, repoPath);
98+
HttpClientFactory = new HttpClientFactory(Trace, Settings, Streams);
9299
}
93100

94101
#region ICommandContext
@@ -107,14 +114,18 @@ public CommandContext()
107114

108115
public IHttpClientFactory HttpClientFactory { get; }
109116

117+
public IGit Git { get; }
118+
119+
public IEnvironment Environment { get; }
120+
110121
#endregion
111122

112123
#region IDisposable
113124

114125
protected override void ReleaseManagedResources()
115126
{
116127
Settings?.Dispose();
117-
_git?.Dispose();
128+
Git?.Dispose();
118129
Trace?.Dispose();
119130

120131
base.ReleaseManagedResources();
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT license.
3+
using System.Collections.Generic;
4+
5+
namespace Microsoft.Git.CredentialManager
6+
{
7+
public static class DictionaryExtensions
8+
{
9+
/// <summary>
10+
/// Get the value of a dictionary entry as 'booleany' (either 'truthy' or 'falsey').
11+
/// </summary>
12+
/// <param name="dict">Dictionary.</param>
13+
/// <param name="key">Dictionary entry key.</param>
14+
/// <param name="defaultValue">Default value if the key is not present, or was neither 'truthy' or 'falsey'.</param>
15+
/// <returns>Dictionary entry value.</returns>
16+
/// <remarks>
17+
/// 'Truthy' and 'fasley' is defined by the implementation of <see cref="StringExtensions.IsTruthy"/> and <see cref="StringExtensions.IsFalsey"/>.
18+
/// </remarks>
19+
public static bool GetBooleanyOrDefault(this IReadOnlyDictionary<string, string> dict, string key, bool defaultValue)
20+
{
21+
if (dict.TryGetValue(key, out string value))
22+
{
23+
return value.ToBooleanyOrDefault(defaultValue);
24+
}
25+
26+
return defaultValue;
27+
}
28+
}
29+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT license.
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
6+
namespace Microsoft.Git.CredentialManager
7+
{
8+
/// <summary>
9+
/// Component that encapsulates the process environment, including environment variables.
10+
/// </summary>
11+
public interface IEnvironment
12+
{
13+
/// <summary>
14+
/// Current process environment variables.
15+
/// </summary>
16+
IReadOnlyDictionary<string, string> Variables { get; }
17+
18+
/// <summary>
19+
/// Check if the given directory exists on the path.
20+
/// </summary>
21+
/// <param name="directoryPath">Path to directory to check for existence on the path.</param>
22+
/// <returns>True if the directory is on the path, false otherwise.</returns>
23+
bool IsDirectoryOnPath(string directoryPath);
24+
25+
/// <summary>
26+
/// Add the directory to the path.
27+
/// </summary>
28+
/// <param name="directoryPath">Path to directory to add to the path.</param>
29+
/// <param name="system">True to update the path for all users on the system, false to update only the user's path.</param>
30+
void AddDirectoryToPath(string directoryPath, bool system);
31+
32+
/// <summary>
33+
/// Remove the directory from the path.
34+
/// </summary>
35+
/// <param name="directoryPath">Path to directory to remove from the path.</param>
36+
/// <param name="system">True to update the path for all users on the system, false to update only the user's path.</param>
37+
void RemoveDirectoryFromPath(string directoryPath, bool system);
38+
}
39+
40+
public abstract class EnvironmentBase : IEnvironment
41+
{
42+
protected EnvironmentBase(IFileSystem fileSystem)
43+
{
44+
EnsureArgument.NotNull(fileSystem, nameof(fileSystem));
45+
46+
FileSystem = fileSystem;
47+
}
48+
49+
public IReadOnlyDictionary<string, string> Variables { get; protected set; }
50+
51+
protected IFileSystem FileSystem { get; }
52+
53+
public bool IsDirectoryOnPath(string directoryPath)
54+
{
55+
if (Variables.TryGetValue("PATH", out string pathValue))
56+
{
57+
string[] paths = SplitPathVariable(pathValue);
58+
return paths.Any(x => FileSystem.IsSamePath(x, directoryPath));
59+
}
60+
61+
return false;
62+
}
63+
64+
public abstract void AddDirectoryToPath(string directoryPath, bool system);
65+
66+
public abstract void RemoveDirectoryFromPath(string directoryPath, bool system);
67+
68+
protected abstract string[] SplitPathVariable(string value);
69+
}
70+
}

src/shared/Microsoft.Git.CredentialManager/EnvironmentVariables.cs

Lines changed: 0 additions & 109 deletions
This file was deleted.

0 commit comments

Comments
 (0)