diff --git a/src/ReleaseHistory.md b/src/ReleaseHistory.md
index eb01b6dcd..3c02fd709 100644
--- a/src/ReleaseHistory.md
+++ b/src/ReleaseHistory.md
@@ -5,6 +5,7 @@
* DEPENDENCY BREAKING: SARIF now requires Newtonsoft.JSON 11.0.2 (rather than 10.0.3)
* DEPENDENCY: SARIF TypeScript package now requires minimist 1.2.3 or later (rather than >=1.2.0)
* FEATURE: Add a setter to `GitHelper.GitExePath`. [#2110](https://github.com/microsoft/sarif-sdk/pull/2110)
+* FEATURE: `GitHelper` will search in %PATH% variable if `git.exe` isn't located in ProgramFiles folder
## **v2.3.6** [Sdk](https://www.nuget.org/packages/Sarif.Sdk/2.3.6) | [Driver](https://www.nuget.org/packages/Sarif.Driver/2.3.6) | [Converters](https://www.nuget.org/packages/Sarif.Converters/2.3.6) | [Multitool](https://www.nuget.org/packages/Sarif.Multitool/2.3.6) | [Multitool Library](https://www.nuget.org/packages/Sarif.Multitool.Library/2.3.6)
* BUGFIX: Restore multitool client app package build.
diff --git a/src/Sarif/FileSearcherHelper.cs b/src/Sarif/FileSearcherHelper.cs
new file mode 100644
index 000000000..71d6cac64
--- /dev/null
+++ b/src/Sarif/FileSearcherHelper.cs
@@ -0,0 +1,51 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.IO;
+
+namespace Microsoft.CodeAnalysis.Sarif
+{
+ internal static class FileSearcherHelper
+ {
+ ///
+ /// This method will search in the environment variable for a specific file name.
+ /// It will return the first file found.
+ ///
+ /// Environment variable that we will look for
+ /// Name of the file that we will look for in the environment variable
+ /// Path to the file name or empty string.
+ public static string SearchForFileInEnvironmentVariable(string environmentVariable, string fileName)
+ {
+ string variable = Environment.GetEnvironmentVariable(environmentVariable);
+ if (string.IsNullOrEmpty(variable))
+ {
+ return null;
+ }
+
+ string[] paths = variable.Split(';');
+ foreach (string path in paths)
+ {
+ string returnedPath = SearchForFileNameInPath(path, fileName);
+ if (!string.IsNullOrEmpty(returnedPath))
+ {
+ return returnedPath;
+ }
+ }
+
+ return null;
+ }
+
+ ///
+ /// This method will search for a file name in a specific path.
+ ///
+ /// Path where it will search.
+ /// Name of the file that it will search
+ /// Path to the file name or empty string.
+ public static string SearchForFileNameInPath(string path, string fileName)
+ {
+ string filePath = $@"{path}\{fileName}";
+ return File.Exists(filePath) ? filePath : null;
+ }
+ }
+}
diff --git a/src/Sarif/GitHelper.cs b/src/Sarif/GitHelper.cs
index 87b582cc6..648e5eb0d 100644
--- a/src/Sarif/GitHelper.cs
+++ b/src/Sarif/GitHelper.cs
@@ -73,8 +73,15 @@ public void Checkout(string repoPath, string commitSha)
args: $"checkout {commitSha}");
}
- private string GetGitExePath()
- => this.fileSystem.FileExists(s_expectedGitExePath) ? s_expectedGitExePath : null;
+ internal string GetGitExePath()
+ {
+ if (this.fileSystem.FileExists(s_expectedGitExePath))
+ {
+ return s_expectedGitExePath;
+ }
+
+ return FileSearcherHelper.SearchForFileInEnvironmentVariable("PATH", "git.exe");
+ }
public string GetCurrentBranch(string repoPath)
{
diff --git a/src/Test.UnitTests.Sarif/GitHelperTests.cs b/src/Test.UnitTests.Sarif/GitHelperTests.cs
index be9c544cf..d01181a8f 100644
--- a/src/Test.UnitTests.Sarif/GitHelperTests.cs
+++ b/src/Test.UnitTests.Sarif/GitHelperTests.cs
@@ -118,6 +118,49 @@ public void GetRepositoryRoot_WhenCalledOnTheDefaultInstanceWithCachingDisabled_
action.Should().NotThrow();
}
+ [Fact]
+ public void GetGitExePath_WhenPathExistsInProgramFiles()
+ {
+ var mockFileSystem = new Mock();
+
+ mockFileSystem.Setup(x => x.FileExists(It.IsAny())).Returns(true);
+
+ var gitHelper = new GitHelper(mockFileSystem.Object);
+
+ gitHelper.GetGitExePath().Should().NotBeNullOrEmpty();
+ }
+
+ [Fact]
+ public void GetGitExePath_WhenPathDoesNotExistInProgramFiles()
+ {
+ var mockFileSystem = new Mock();
+
+ mockFileSystem.Setup(x => x.FileExists(It.IsAny())).Returns(false);
+
+ var gitHelper = new GitHelper(mockFileSystem.Object);
+
+ gitHelper.GetGitExePath().Should().NotBeNull();
+ }
+
+ [Fact]
+ public void SearchForFileInEnvironmentVariable_WhenVariableDoesNotExist()
+ {
+ FileSearcherHelper.SearchForFileInEnvironmentVariable("PATH_THAT_DOES_NOT_EXIST", "filename.exe").Should().BeNull();
+ }
+
+ [Fact]
+ public void SearchForFileInEnvironmentVariable_WhenVariableExistsButFileDoesnt()
+ {
+ // The error in the ntdll name here is intentional.
+ FileSearcherHelper.SearchForFileInEnvironmentVariable("PATH", "ntdll.dlll").Should().BeNull();
+ }
+
+ [Fact]
+ public void SearchForFileInEnvironmentVariable_WhenVariableAndFileExists()
+ {
+ FileSearcherHelper.SearchForFileInEnvironmentVariable("PATH", "ntdll.dll").Should().NotBeNull();
+ }
+
[Fact]
public void GitExePath_WhenPathDoesntExist_SettingManuallyShouldWork()
{