mirror of
https://github.com/davidkaya/aryx.git
synced 2026-08-26 12:53:59 +02:00
fix: make copilot CLI path resolution cross-platform
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -8,6 +8,10 @@ internal static class CopilotCliPathResolver
|
|||||||
private const string CopilotCommandName = "copilot";
|
private const string CopilotCommandName = "copilot";
|
||||||
private const string DefaultWindowsCommandProcessor = "cmd.exe";
|
private const string DefaultWindowsCommandProcessor = "cmd.exe";
|
||||||
private const string DefaultWindowsPathExtensions = ".COM;.EXE;.BAT;.CMD";
|
private const string DefaultWindowsPathExtensions = ".COM;.EXE;.BAT;.CMD";
|
||||||
|
private const char WindowsSearchPathSeparator = ';';
|
||||||
|
private const char UnixSearchPathSeparator = ':';
|
||||||
|
private const char WindowsDirectorySeparator = '\\';
|
||||||
|
private const char UnixDirectorySeparator = '/';
|
||||||
|
|
||||||
private static readonly string[] BlockedCliEnvironmentPrefixes = ["BUN_", "COPILOT_", "ELECTRON_", "NODE_", "NPM_"];
|
private static readonly string[] BlockedCliEnvironmentPrefixes = ["BUN_", "COPILOT_", "ELECTRON_", "NODE_", "NPM_"];
|
||||||
|
|
||||||
@@ -125,7 +129,7 @@ internal static class CopilotCliPathResolver
|
|||||||
{
|
{
|
||||||
foreach (string candidateName in GetCandidateFileNames(pathExtValue, isWindows))
|
foreach (string candidateName in GetCandidateFileNames(pathExtValue, isWindows))
|
||||||
{
|
{
|
||||||
string candidatePath = Path.Combine(directory, candidateName);
|
string candidatePath = CombineSearchPath(directory, candidateName, isWindows);
|
||||||
if (fileExists(candidatePath))
|
if (fileExists(candidatePath))
|
||||||
{
|
{
|
||||||
return candidatePath;
|
return candidatePath;
|
||||||
@@ -145,7 +149,7 @@ internal static class CopilotCliPathResolver
|
|||||||
|
|
||||||
StringComparer comparer = isWindows ? StringComparer.OrdinalIgnoreCase : StringComparer.Ordinal;
|
StringComparer comparer = isWindows ? StringComparer.OrdinalIgnoreCase : StringComparer.Ordinal;
|
||||||
foreach (string directory in pathValue
|
foreach (string directory in pathValue
|
||||||
.Split(Path.PathSeparator, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
|
.Split(GetSearchPathSeparator(isWindows), StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
|
||||||
.Select(segment => segment.Trim('"'))
|
.Select(segment => segment.Trim('"'))
|
||||||
.Where(segment => !string.IsNullOrWhiteSpace(segment))
|
.Where(segment => !string.IsNullOrWhiteSpace(segment))
|
||||||
.Distinct(comparer))
|
.Distinct(comparer))
|
||||||
@@ -184,6 +188,38 @@ internal static class CopilotCliPathResolver
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static char GetSearchPathSeparator(bool isWindows)
|
||||||
|
{
|
||||||
|
return isWindows ? WindowsSearchPathSeparator : UnixSearchPathSeparator;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string CombineSearchPath(string directory, string fileName, bool isWindows)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(directory))
|
||||||
|
{
|
||||||
|
return fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (EndsWithDirectorySeparator(directory, isWindows))
|
||||||
|
{
|
||||||
|
return directory + fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
return directory + GetDirectorySeparator(isWindows) + fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool EndsWithDirectorySeparator(string path, bool isWindows)
|
||||||
|
{
|
||||||
|
char lastCharacter = path[^1];
|
||||||
|
return lastCharacter == GetDirectorySeparator(isWindows)
|
||||||
|
|| (isWindows && lastCharacter == UnixDirectorySeparator);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static char GetDirectorySeparator(bool isWindows)
|
||||||
|
{
|
||||||
|
return isWindows ? WindowsDirectorySeparator : UnixDirectorySeparator;
|
||||||
|
}
|
||||||
|
|
||||||
private static IEnumerable<KeyValuePair<string, string?>> GetCurrentEnvironmentVariables()
|
private static IEnumerable<KeyValuePair<string, string?>> GetCurrentEnvironmentVariables()
|
||||||
{
|
{
|
||||||
return Environment.GetEnvironmentVariables()
|
return Environment.GetEnvironmentVariables()
|
||||||
|
|||||||
@@ -8,9 +8,10 @@ public sealed class CopilotCliPathResolverTests
|
|||||||
public void Resolve_UsesCopilotFromPath()
|
public void Resolve_UsesCopilotFromPath()
|
||||||
{
|
{
|
||||||
string copilotDirectory = @"C:\tools\copilot";
|
string copilotDirectory = @"C:\tools\copilot";
|
||||||
|
string expectedCliPath = @"C:\tools\copilot\copilot.exe";
|
||||||
HashSet<string> existingFiles = new(StringComparer.OrdinalIgnoreCase)
|
HashSet<string> existingFiles = new(StringComparer.OrdinalIgnoreCase)
|
||||||
{
|
{
|
||||||
Path.Combine(copilotDirectory, "copilot.exe"),
|
expectedCliPath,
|
||||||
};
|
};
|
||||||
|
|
||||||
string? cliPath = CopilotCliPathResolver.Resolve(
|
string? cliPath = CopilotCliPathResolver.Resolve(
|
||||||
@@ -19,16 +20,17 @@ public sealed class CopilotCliPathResolverTests
|
|||||||
isWindows: true,
|
isWindows: true,
|
||||||
fileExists: existingFiles.Contains);
|
fileExists: existingFiles.Contains);
|
||||||
|
|
||||||
Assert.Equal(Path.Combine(copilotDirectory, "copilot.exe"), cliPath, ignoreCase: true);
|
Assert.Equal(expectedCliPath, cliPath, ignoreCase: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Resolve_UsesDefaultWindowsExtensionsWhenPathExtMissing()
|
public void Resolve_UsesDefaultWindowsExtensionsWhenPathExtMissing()
|
||||||
{
|
{
|
||||||
string copilotDirectory = @"C:\tools\copilot";
|
string copilotDirectory = @"C:\tools\copilot";
|
||||||
|
string expectedCliPath = @"C:\tools\copilot\copilot.cmd";
|
||||||
HashSet<string> existingFiles = new(StringComparer.OrdinalIgnoreCase)
|
HashSet<string> existingFiles = new(StringComparer.OrdinalIgnoreCase)
|
||||||
{
|
{
|
||||||
Path.Combine(copilotDirectory, "copilot.cmd"),
|
expectedCliPath,
|
||||||
};
|
};
|
||||||
|
|
||||||
string? cliPath = CopilotCliPathResolver.Resolve(
|
string? cliPath = CopilotCliPathResolver.Resolve(
|
||||||
@@ -37,7 +39,25 @@ public sealed class CopilotCliPathResolverTests
|
|||||||
isWindows: true,
|
isWindows: true,
|
||||||
fileExists: existingFiles.Contains);
|
fileExists: existingFiles.Contains);
|
||||||
|
|
||||||
Assert.Equal(Path.Combine(copilotDirectory, "copilot.cmd"), cliPath, ignoreCase: true);
|
Assert.Equal(expectedCliPath, cliPath, ignoreCase: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Resolve_UsesCopilotFromPathOutsideWindows()
|
||||||
|
{
|
||||||
|
const string expectedCliPath = "/usr/local/bin/copilot";
|
||||||
|
HashSet<string> existingFiles = new(StringComparer.Ordinal)
|
||||||
|
{
|
||||||
|
expectedCliPath,
|
||||||
|
};
|
||||||
|
|
||||||
|
string? cliPath = CopilotCliPathResolver.Resolve(
|
||||||
|
pathValue: "/usr/bin:/usr/local/bin",
|
||||||
|
pathExtValue: null,
|
||||||
|
isWindows: false,
|
||||||
|
fileExists: existingFiles.Contains);
|
||||||
|
|
||||||
|
Assert.Equal(expectedCliPath, cliPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|||||||
Reference in New Issue
Block a user