Skip to content

Commit d5f8045

Browse files
committed
env: introduce TryLocateExecutable method
Introduce a `TryX` method version of the existing `LocateExecutable` method on `IEnvironment`, for locating executables with `which` and `where.exe`.
1 parent c0214d2 commit d5f8045

File tree

4 files changed

+57
-30
lines changed

4 files changed

+57
-30
lines changed

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

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,9 @@ public interface IEnvironment
4141
/// Locate an executable on the current PATH.
4242
/// </summary>
4343
/// <param name="program">Executable program name.</param>
44-
/// <returns>List of all instances of the found executable program, in order of most specific to least.</returns>
45-
string LocateExecutable(string program);
44+
/// <param name="path">First instance of the found executable program.</param>
45+
/// <returns>True if the executable was found, false otherwise.</returns>
46+
bool TryLocateExecutable(string program, out string path);
4647
}
4748

4849
public abstract class EnvironmentBase : IEnvironment
@@ -75,6 +76,25 @@ public bool IsDirectoryOnPath(string directoryPath)
7576

7677
protected abstract string[] SplitPathVariable(string value);
7778

78-
public abstract string LocateExecutable(string program);
79+
public abstract bool TryLocateExecutable(string program, out string path);
80+
}
81+
82+
public static class EnvironmentExtensions
83+
{
84+
/// <summary>
85+
/// Locate an executable on the current PATH.
86+
/// </summary>
87+
/// <param name="environment">The <see cref="IEnvironment"/>.</param>
88+
/// <param name="program">Executable program name.</param>
89+
/// <returns>List of all instances of the found executable program, in order of most specific to least.</returns>
90+
public static string LocateExecutable(this IEnvironment environment, string program)
91+
{
92+
if (environment.TryLocateExecutable(program, out string path))
93+
{
94+
return path;
95+
}
96+
97+
throw new Exception($"Failed to locate '{program}' executable on the path.");
98+
}
7999
}
80100
}

src/shared/Microsoft.Git.CredentialManager/Interop/Posix/PosixEnvironment.cs

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ protected override string[] SplitPathVariable(string value)
3131
return value.Split(':');
3232
}
3333

34-
public override string LocateExecutable(string program)
34+
public override bool TryLocateExecutable(string program, out string path)
3535
{
3636
const string whichPath = "/usr/bin/which";
3737
var psi = new ProcessStartInfo(whichPath, program)
@@ -45,19 +45,21 @@ public override string LocateExecutable(string program)
4545
where.Start();
4646
where.WaitForExit();
4747

48-
if (where.ExitCode != 0)
48+
switch (where.ExitCode)
4949
{
50-
throw new Exception($"Failed to locate '{program}' using {whichPath}. Exit code: {where.ExitCode}.");
51-
}
50+
case 0: // found
51+
string stdout = where.StandardOutput.ReadToEnd();
52+
string[] results = stdout.Split(new[] {'\n'}, StringSplitOptions.RemoveEmptyEntries);
53+
path = results.First();
54+
return true;
5255

53-
string stdout = where.StandardOutput.ReadToEnd();
54-
if (string.IsNullOrWhiteSpace(stdout))
55-
{
56-
return null;
57-
}
56+
case 1: // not found
57+
path = null;
58+
return false;
5859

59-
string[] results = stdout.Split(new[] {'\n'}, StringSplitOptions.RemoveEmptyEntries);
60-
return results.FirstOrDefault();
60+
default:
61+
throw new Exception($"Unknown error locating '{program}' using {whichPath}. Exit code: {where.ExitCode}.");
62+
}
6163
}
6264
}
6365

src/shared/Microsoft.Git.CredentialManager/Interop/Windows/WindowsEnvironment.cs

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ public override void RemoveDirectoryFromPath(string directoryPath, EnvironmentVa
6565
}
6666
}
6767

68-
public override string LocateExecutable(string program)
68+
public override bool TryLocateExecutable(string program, out string path)
6969
{
7070
string wherePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "where.exe");
7171
var psi = new ProcessStartInfo(wherePath, program)
@@ -79,19 +79,21 @@ public override string LocateExecutable(string program)
7979
where.Start();
8080
where.WaitForExit();
8181

82-
if (where.ExitCode != 0)
82+
switch (where.ExitCode)
8383
{
84-
throw new Exception($"Failed to locate '{program}' using where.exe. Exit code: {where.ExitCode}.");
84+
case 0: // found
85+
string stdout = where.StandardOutput.ReadToEnd();
86+
string[] results = stdout.Split(new[] {'\r', '\n'}, StringSplitOptions.RemoveEmptyEntries);
87+
path = results.First();
88+
return true;
89+
90+
case 1: // not found
91+
path = null;
92+
return false;
93+
94+
default:
95+
throw new Exception($"Unknown error locating '{program}' using where.exe. Exit code: {where.ExitCode}.");
8596
}
86-
87-
string stdout = where.StandardOutput.ReadToEnd();
88-
if (string.IsNullOrWhiteSpace(stdout))
89-
{
90-
return null;
91-
}
92-
93-
string[] results = stdout.Split(new[] {'\r', '\n'}, StringSplitOptions.RemoveEmptyEntries);
94-
return results.FirstOrDefault();
9597
}
9698
}
9799

src/shared/TestInfrastructure/Objects/TestEnvironment.cs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,21 +80,24 @@ public void RemoveDirectoryFromPath(string directoryPath, EnvironmentVariableTar
8080
Variables["PATH"] = string.Join(_envPathSeparator, Path);
8181
}
8282

83-
public string LocateExecutable(string program)
83+
public bool TryLocateExecutable(string program, out string path)
8484
{
8585
if (WhichFiles.TryGetValue(program, out ICollection<string> paths))
8686
{
87-
return paths.FirstOrDefault();
87+
path = paths.First();
88+
return true;
8889
}
8990

9091
if (!System.IO.Path.HasExtension(program) && PlatformUtils.IsWindows())
9192
{
9293
// If we're testing on a Windows platform, don't have a file extension, and were unable to locate
9394
// the executable file.. try appending .exe.
94-
return WhichFiles.TryGetValue($"{program}.exe", out paths) ? paths.FirstOrDefault() : null;
95+
path = WhichFiles.TryGetValue($"{program}.exe", out paths) ? paths.First() : null;
96+
return !(path is null);
9597
}
9698

97-
return null;
99+
path = null;
100+
return false;
98101
}
99102

100103
#endregion

0 commit comments

Comments
 (0)