diff --git a/tools/azsdk-cli/Azure.Sdk.Tools.Cli.Tests/Helpers/PackageInfoContractTests.cs b/tools/azsdk-cli/Azure.Sdk.Tools.Cli.Tests/Helpers/PackageInfoContractTests.cs index 2ceb2718ed6..08de9eac736 100644 --- a/tools/azsdk-cli/Azure.Sdk.Tools.Cli.Tests/Helpers/PackageInfoContractTests.cs +++ b/tools/azsdk-cli/Azure.Sdk.Tools.Cli.Tests/Helpers/PackageInfoContractTests.cs @@ -27,7 +27,7 @@ public class PackageInfoContractTests [TearDown] public void TearDown() => _tempRoot.Dispose(); - private (string packagePath, GitHelper gitHelper, IOutputHelper, IProcessHelper, IPowershellHelper, IMicroagentHostService, INpxHelper, ICommonValidationHelpers) CreateSdkPackage(string service, string package) + private (string packagePath, GitHelper gitHelper, IOutputHelper, IProcessHelper, IPowershellHelper, IMicroagentHostService, INpxHelper, IPythonHelper, ICommonValidationHelpers) CreateSdkPackage(string service, string package) { var repoRoot = Path.Combine(_tempRoot.DirectoryPath, "azure-sdk-repo-root"); Directory.CreateDirectory(repoRoot); @@ -41,9 +41,10 @@ public class PackageInfoContractTests var gitHelper = new GitHelper(ghMock.Object, new TestLogger()); var microAgentMock = new Mock(); var npxHelperMock = new Mock(); + var pythonHelperMock = new Mock(); var commonValidationHelper = new Mock(); - return (sdkPath, gitHelper, outputMock.Object, processHelperMock.Object, powershellMock.Object, microAgentMock.Object, npxHelperMock.Object, commonValidationHelper.Object); + return (sdkPath, gitHelper, outputMock.Object, processHelperMock.Object, powershellMock.Object, microAgentMock.Object, npxHelperMock.Object, pythonHelperMock.Object, commonValidationHelper.Object); } private void CreateTestFile(string packagePath, string relativePath, string content) @@ -119,8 +120,8 @@ public async Task CommonProperties_AreDerivedCorrectly(SdkLanguage language) var service = language == SdkLanguage.Go ? "keyvault" : "storage"; var package = language switch { SdkLanguage.DotNet => "Azure.Storage.Blobs", SdkLanguage.Java => "azure-storage-blob", SdkLanguage.Go => "azkeys", _ => "storage-blob" }; var servicePath = language == SdkLanguage.Go ? Path.Combine(group, service) : service; - var (pkgPath, gitHelper, outputHelper, processHelper, powershellHelper, microAgentMock, npxHelper, commonValidationHelper) = CreateSdkPackage(servicePath, package); - var helper = CreateHelperForLanguage(language, gitHelper, outputHelper, processHelper, powershellHelper, microAgentMock, npxHelper, commonValidationHelper); + var (pkgPath, gitHelper, outputHelper, processHelper, powershellHelper, microAgentMock, npxHelper, pythonHelper, commonValidationHelper) = CreateSdkPackage(servicePath, package); + var helper = CreateHelperForLanguage(language, gitHelper, outputHelper, processHelper, powershellHelper, microAgentMock, npxHelper, pythonHelper, commonValidationHelper); var info = await helper.GetPackageInfo(pkgPath); Assert.Multiple(() => @@ -144,11 +145,11 @@ public async Task CommonProperties_AreDerivedCorrectly(SdkLanguage language) public async Task VersionParsing_Works(SdkLanguage language, string package, string expectedVersion) { var servicePath = language == SdkLanguage.Go ? "security/keyvault" : "ai"; - var (pkgPath, gitHelper, outputHelper, processHelper, powershellHelper, microAgentMock, npxHelper, commonValidationHelper) = CreateSdkPackage(servicePath, package); + var (pkgPath, gitHelper, outputHelper, processHelper, powershellHelper, microAgentMock, npxHelper, pythonHelper, commonValidationHelper) = CreateSdkPackage(servicePath, package); SetupPackageForLanguage(language, pkgPath, package, expectedVersion); - var helper = CreateHelperForLanguage(language, gitHelper, outputHelper, processHelper, powershellHelper, microAgentMock, npxHelper, commonValidationHelper); + var helper = CreateHelperForLanguage(language, gitHelper, outputHelper, processHelper, powershellHelper, microAgentMock, npxHelper, pythonHelper, commonValidationHelper); var info = await helper.GetPackageInfo(pkgPath); Assert.That(info.PackageVersion, Is.EqualTo(expectedVersion)); } @@ -162,19 +163,19 @@ public async Task VersionParsing_Works(SdkLanguage language, string package, str public async Task VersionParsing_MissingFile_ReturnsNull(SdkLanguage language, string package) { var servicePath = language == SdkLanguage.Go ? "security/keyvault" : "missing"; - var (pkgPath, gitHelper, outputHelper, processHelper, powershellHelper, microAgentMock, npxHelper, commonValidationHelper) = CreateSdkPackage(servicePath, package); - var helper = CreateHelperForLanguage(language, gitHelper, outputHelper, processHelper, powershellHelper, microAgentMock, npxHelper, commonValidationHelper); + var (pkgPath, gitHelper, outputHelper, processHelper, powershellHelper, microAgentMock, npxHelper, pythonHelper, commonValidationHelper) = CreateSdkPackage(servicePath, package); + var helper = CreateHelperForLanguage(language, gitHelper, outputHelper, processHelper, powershellHelper, microAgentMock, npxHelper, pythonHelper, commonValidationHelper); var info = await helper.GetPackageInfo(pkgPath); Assert.That(info.PackageVersion, Is.Null); } - private static LanguageService CreateHelperForLanguage(SdkLanguage language, IGitHelper gitHelper, IOutputHelper outputHelper, IProcessHelper processHelper, IPowershellHelper powershellHelper, IMicroagentHostService microAgentMock, INpxHelper npxHelper, ICommonValidationHelpers commonValidationHelper) => language switch + private static LanguageService CreateHelperForLanguage(SdkLanguage language, IGitHelper gitHelper, IOutputHelper outputHelper, IProcessHelper processHelper, IPowershellHelper powershellHelper, IMicroagentHostService microAgentMock, INpxHelper npxHelper, IPythonHelper pythonHelper, ICommonValidationHelpers commonValidationHelper) => language switch { ///var powershellHelper = new Mock(); SdkLanguage.DotNet => new DotnetLanguageService(processHelper, powershellHelper, gitHelper, new TestLogger(), commonValidationHelper), SdkLanguage.Java => new JavaLanguageService(processHelper, gitHelper, new Mock().Object, microAgentMock, new TestLogger(), commonValidationHelper), - SdkLanguage.Python => new PythonLanguageService(processHelper, npxHelper, gitHelper, new TestLogger(), commonValidationHelper), + SdkLanguage.Python => new PythonLanguageService(processHelper, pythonHelper, npxHelper, gitHelper, new TestLogger(), commonValidationHelper), SdkLanguage.JavaScript => new JavaScriptLanguageService(processHelper, npxHelper, gitHelper, new TestLogger(), commonValidationHelper), SdkLanguage.Go => new GoLanguageService(processHelper, npxHelper, gitHelper, new TestLogger(), commonValidationHelper), _ => throw new ArgumentException($"Unsupported language '{language}'", nameof(language)) diff --git a/tools/azsdk-cli/Azure.Sdk.Tools.Cli.Tests/Helpers/PythonOptionsTests.cs b/tools/azsdk-cli/Azure.Sdk.Tools.Cli.Tests/Helpers/PythonOptionsTests.cs new file mode 100644 index 00000000000..6a196721e80 --- /dev/null +++ b/tools/azsdk-cli/Azure.Sdk.Tools.Cli.Tests/Helpers/PythonOptionsTests.cs @@ -0,0 +1,333 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +using System.Runtime.InteropServices; +using Azure.Sdk.Tools.Cli.Helpers; +using Azure.Sdk.Tools.Cli.Tests.TestHelpers; + +namespace Azure.Sdk.Tools.Cli.Tests.Helpers +{ + internal class PythonOptionsTests + { + private const string VenvEnvironmentVariable = "AZSDKTOOLS_PYTHON_VENV_PATH"; + private TestLogger logger; + private string? originalVenvPath; + + [SetUp] + public void Setup() + { + logger = new TestLogger(); + // Save original environment variable value + originalVenvPath = Environment.GetEnvironmentVariable(VenvEnvironmentVariable); + } + + [TearDown] + public void TearDown() + { + // Restore original environment variable value + Environment.SetEnvironmentVariable(VenvEnvironmentVariable, originalVenvPath); + } + + [Test] + public void Constructor_CreatesOptionsWithResolvedExecutable() + { + // Arrange & Act + var options = new PythonOptions("python", ["--version"]); + + // Assert + Assert.That(options, Is.Not.Null); + Assert.That(options.Args, Is.Not.Null); + + // On Windows, args are wrapped with /C and command, on Unix they're direct + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + Assert.That(options.Args, Does.Contain("--version")); + } + else + { + Assert.That(options.Args, Has.Count.EqualTo(1)); + Assert.That(options.Args[0], Is.EqualTo("--version")); + } + } + + [Test] + public void Constructor_WithWorkingDirectory_SetsWorkingDirectory() + { + // Arrange + var workingDir = Path.GetTempPath(); + + // Act + var options = new PythonOptions( + "python", + ["--version"], + workingDirectory: workingDir + ); + + // Assert + Assert.That(options.WorkingDirectory, Is.EqualTo(workingDir)); + } + + [Test] + public void Constructor_WithTimeout_SetsTimeout() + { + // Arrange + var timeout = TimeSpan.FromMinutes(5); + + // Act + var options = new PythonOptions( + "python", + ["--version"], + timeout: timeout + ); + + // Assert + Assert.That(options.Timeout, Is.EqualTo(timeout)); + } + + [Test] + public void Constructor_WithLogOutputStream_SetsLogOutputStream() + { + // Act + var options = new PythonOptions( + "python", + ["--version"], + logOutputStream: false + ); + + // Assert + Assert.That(options.LogOutputStream, Is.False); + } + + [Test] + public void ResolvePythonExecutable_WithoutVenvPath_ReturnsExecutableName() + { + // Arrange + Environment.SetEnvironmentVariable(VenvEnvironmentVariable, null); + + // Act + var result = PythonOptions.ResolvePythonExecutable("python"); + + // Assert + Assert.That(result, Is.EqualTo("python")); + } + + [Test] + public void ResolvePythonExecutable_WithEmptyVenvPath_ReturnsExecutableName() + { + // Arrange + Environment.SetEnvironmentVariable(VenvEnvironmentVariable, ""); + + // Act + var result = PythonOptions.ResolvePythonExecutable("pytest"); + + // Assert + Assert.That(result, Is.EqualTo("pytest")); + } + + [Test] + public void ResolvePythonExecutable_WithNonExistentVenvPath_ThrowsDirectoryNotFoundException() + { + // Arrange + var nonExistentPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + Environment.SetEnvironmentVariable(VenvEnvironmentVariable, nonExistentPath); + + // Act & Assert + var ex = Assert.Throws(() => + PythonOptions.ResolvePythonExecutable("python")); + + Assert.That(ex.Message, Does.Contain(VenvEnvironmentVariable)); + Assert.That(ex.Message, Does.Contain(nonExistentPath)); + } + + [Test] + public void ResolvePythonExecutable_WithValidVenvPath_ResolvesCorrectly() + { + // Arrange + var tempVenvPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + try + { + Directory.CreateDirectory(tempVenvPath); + var binDir = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "Scripts" : "bin"; + Directory.CreateDirectory(Path.Combine(tempVenvPath, binDir)); + + Environment.SetEnvironmentVariable(VenvEnvironmentVariable, tempVenvPath); + + // Act + var result = PythonOptions.ResolvePythonExecutable("python"); + + // Assert + var expectedPath = Path.Combine(tempVenvPath, binDir, "python"); + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + expectedPath += ".exe"; + } + Assert.That(result, Is.EqualTo(expectedPath)); + } + finally + { + // Cleanup + if (Directory.Exists(tempVenvPath)) + { + Directory.Delete(tempVenvPath, true); + } + } + } + + [Test] + [Platform("Win")] + public void ResolvePythonExecutable_OnWindows_AddsExeExtension() + { + // Arrange + var tempVenvPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + try + { + Directory.CreateDirectory(tempVenvPath); + Directory.CreateDirectory(Path.Combine(tempVenvPath, "Scripts")); + + Environment.SetEnvironmentVariable(VenvEnvironmentVariable, tempVenvPath); + + // Act + var result = PythonOptions.ResolvePythonExecutable("pytest"); + + // Assert + Assert.That(result, Does.EndWith(".exe")); + Assert.That(result, Does.Contain("Scripts")); + } + finally + { + // Cleanup + if (Directory.Exists(tempVenvPath)) + { + Directory.Delete(tempVenvPath, true); + } + } + } + + [Test] + [Platform("Win")] + public void ResolvePythonExecutable_OnWindows_WithExeExtension_DoesNotAddAnotherExtension() + { + // Arrange + var tempVenvPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + try + { + Directory.CreateDirectory(tempVenvPath); + Directory.CreateDirectory(Path.Combine(tempVenvPath, "Scripts")); + + Environment.SetEnvironmentVariable(VenvEnvironmentVariable, tempVenvPath); + + // Act + var result = PythonOptions.ResolvePythonExecutable("python.exe"); + + // Assert + Assert.That(result, Does.EndWith(".exe")); + Assert.That(result, Does.Not.Contain(".exe.exe")); + Assert.That(result, Does.Contain("Scripts")); + } + finally + { + // Cleanup + if (Directory.Exists(tempVenvPath)) + { + Directory.Delete(tempVenvPath, true); + } + } + } + + [Test] + [Platform("Linux,MacOsX")] + public void ResolvePythonExecutable_OnUnix_UsesBinDirectory() + { + // Arrange + var tempVenvPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + try + { + Directory.CreateDirectory(tempVenvPath); + Directory.CreateDirectory(Path.Combine(tempVenvPath, "bin")); + + Environment.SetEnvironmentVariable(VenvEnvironmentVariable, tempVenvPath); + + // Act + var result = PythonOptions.ResolvePythonExecutable("python"); + + // Assert + Assert.That(result, Does.Contain("bin")); + Assert.That(result, Does.Not.EndWith(".exe")); + } + finally + { + // Cleanup + if (Directory.Exists(tempVenvPath)) + { + Directory.Delete(tempVenvPath, true); + } + } + } + + [Test] + public void ResolvePythonExecutable_WithDifferentExecutableNames_ResolvesCorrectly() + { + // Arrange + var tempVenvPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + try + { + Directory.CreateDirectory(tempVenvPath); + var binDir = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "Scripts" : "bin"; + Directory.CreateDirectory(Path.Combine(tempVenvPath, binDir)); + + Environment.SetEnvironmentVariable(VenvEnvironmentVariable, tempVenvPath); + + var executables = new[] { "python", "pytest", "azpysdk", "pip" }; + + // Act & Assert + foreach (var executable in executables) + { + var result = PythonOptions.ResolvePythonExecutable(executable); + Assert.That(result, Does.Contain(executable)); + Assert.That(result, Does.Contain(tempVenvPath)); + } + } + finally + { + // Cleanup + if (Directory.Exists(tempVenvPath)) + { + Directory.Delete(tempVenvPath, true); + } + } + } + + [Test] + public void Constructor_WithAllParameters_CreatesOptionsCorrectly() + { + // Arrange + var args = new[] { "-m", "pytest", "--verbose" }; + var workingDir = Path.GetTempPath(); + var timeout = TimeSpan.FromMinutes(10); + + // Act + var options = new PythonOptions( + "python", + args, + workingDir, + timeout, + logOutputStream: false + ); + + // Assert + // On Windows, args are wrapped with /C and command, on Unix they're direct + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + Assert.That(options.Args, Does.Contain("-m")); + Assert.That(options.Args, Does.Contain("pytest")); + Assert.That(options.Args, Does.Contain("--verbose")); + } + else + { + Assert.That(options.Args, Is.EqualTo(args)); + } + Assert.That(options.WorkingDirectory, Is.EqualTo(workingDir)); + Assert.That(options.Timeout, Is.EqualTo(timeout)); + Assert.That(options.LogOutputStream, Is.False); + } + } +} diff --git a/tools/azsdk-cli/Azure.Sdk.Tools.Cli.Tests/Tools/CheckAllToolTests.cs b/tools/azsdk-cli/Azure.Sdk.Tools.Cli.Tests/Tools/CheckAllToolTests.cs index 0672bf04946..63899dd3255 100644 --- a/tools/azsdk-cli/Azure.Sdk.Tools.Cli.Tests/Tools/CheckAllToolTests.cs +++ b/tools/azsdk-cli/Azure.Sdk.Tools.Cli.Tests/Tools/CheckAllToolTests.cs @@ -16,6 +16,7 @@ public class PackageCheckToolTests { private Mock> _mockLogger; private Mock _mockProcessHelper; + private Mock _mockPythonHelper; private Mock _mockNpxHelper; private Mock _mockGitHelper; private Mock> _mockPythonLogger; @@ -28,6 +29,7 @@ public void Setup() { _mockLogger = new Mock>(); _mockProcessHelper = new Mock(); + _mockPythonHelper = new Mock(); _mockNpxHelper = new Mock(); _mockGitHelper = new Mock(); _mockGitHelper.Setup(g => g.GetRepoName(It.IsAny())).Returns("azure-sdk-for-python"); @@ -35,7 +37,7 @@ public void Setup() _mockCommonValidationHelpers = new Mock(); // Create language-specific check implementations with mocked dependencies - var pythonCheck = new PythonLanguageService(_mockProcessHelper.Object, _mockNpxHelper.Object, _mockGitHelper.Object, _mockPythonLogger.Object, _mockCommonValidationHelpers.Object); + var pythonCheck = new PythonLanguageService(_mockProcessHelper.Object, _mockPythonHelper.Object, _mockNpxHelper.Object, _mockGitHelper.Object, _mockPythonLogger.Object, _mockCommonValidationHelpers.Object); var languageChecks = new List { pythonCheck }; var mockPowershellHelper = new Mock(); @@ -68,7 +70,7 @@ public void TearDown() public async Task RunPackageCheck_WithAllChecks_ReturnsFailureResult() { // Act - Using empty temp directory will cause dependency check to fail - var result = await _packageCheckTool.RunPackageCheck(_testProjectPath.DirectoryPath, PackageCheckType.All, false, CancellationToken.None); + var result = await _packageCheckTool.RunPackageCheck(_testProjectPath.DirectoryPath, PackageCheckType.All, false); // Assert Assert.IsNotNull(result); @@ -80,7 +82,7 @@ public async Task RunPackageCheck_WithAllChecks_ReturnsFailureResult() public async Task RunPackageCheck_WithChangelogCheck_ReturnsResult() { // Act - var result = await _packageCheckTool.RunPackageCheck(_testProjectPath.DirectoryPath, PackageCheckType.Changelog, false, CancellationToken.None); + var result = await _packageCheckTool.RunPackageCheck(_testProjectPath.DirectoryPath, PackageCheckType.Changelog, false); // Assert Assert.IsNotNull(result); @@ -92,7 +94,7 @@ public async Task RunPackageCheck_WithChangelogCheck_ReturnsResult() public async Task RunPackageCheck_WithDependencyCheck_ReturnsResult() { // Act - var result = await _packageCheckTool.RunPackageCheck(_testProjectPath.DirectoryPath, PackageCheckType.Dependency, false, CancellationToken.None); + var result = await _packageCheckTool.RunPackageCheck(_testProjectPath.DirectoryPath, PackageCheckType.Dependency, false); // Assert Assert.IsNotNull(result); @@ -106,7 +108,7 @@ public async Task RunPackageCheck_WithReadmeCheck_WhenNoReadmeExists_ReturnsFail // Arrange - Empty directory with no README // Act - var result = await _packageCheckTool.RunPackageCheck(_testProjectPath.DirectoryPath, PackageCheckType.Readme, false, CancellationToken.None); + var result = await _packageCheckTool.RunPackageCheck(_testProjectPath.DirectoryPath, PackageCheckType.Readme, false); // Assert Assert.IsNotNull(result); @@ -122,7 +124,7 @@ public async Task RunPackageCheck_WithSpellingCheck_WhenFileWithTypos_ReturnsFai await File.WriteAllTextAsync(testFile, "This file contians obvioius speling erors."); // Act - var result = await _packageCheckTool.RunPackageCheck(_testProjectPath.DirectoryPath, PackageCheckType.Cspell, false, CancellationToken.None); + var result = await _packageCheckTool.RunPackageCheck(_testProjectPath.DirectoryPath, PackageCheckType.Cspell, false); // Assert Assert.IsNotNull(result); @@ -138,7 +140,7 @@ public async Task RunPackageCheck_WithProjectFile_ReturnsPartialSuccess() await File.WriteAllTextAsync(projectFilePath, ""); // Act - This will still fail because dotnet commands won't work properly, but test structure is better - var result = await _packageCheckTool.RunPackageCheck(_testProjectPath.DirectoryPath, PackageCheckType.All, false, CancellationToken.None); + var result = await _packageCheckTool.RunPackageCheck(_testProjectPath.DirectoryPath, PackageCheckType.All, false); // Assert Assert.IsNotNull(result); @@ -154,7 +156,7 @@ public async Task RunPackageCheck_WithInvalidPath_ReturnsErrorResult() string invalidPath = "/tmp/nonexistent-path-12345"; // Act - var result = await _packageCheckTool.RunPackageCheck(invalidPath, PackageCheckType.All, false, CancellationToken.None); + var result = await _packageCheckTool.RunPackageCheck(invalidPath, PackageCheckType.All, false); // Assert Assert.IsNotNull(result); @@ -167,7 +169,7 @@ public async Task RunPackageCheck_WithInvalidPath_ReturnsErrorResult() public async Task RunPackageCheck_WithValidPath_RunsAllChecks() { // Act - var result = await _packageCheckTool.RunPackageCheck(_testProjectPath.DirectoryPath, PackageCheckType.All, false, CancellationToken.None); + var result = await _packageCheckTool.RunPackageCheck(_testProjectPath.DirectoryPath, PackageCheckType.All, false); // Assert Assert.IsNotNull(result); @@ -184,12 +186,12 @@ public async Task RunPackageCheck_EnumValues_WorksCorrectly() // Test that all enum values work correctly // Act - Test all enum values - var allResult = await _packageCheckTool.RunPackageCheck(_testProjectPath.DirectoryPath, PackageCheckType.All, false, CancellationToken.None); - var changelogResult = await _packageCheckTool.RunPackageCheck(_testProjectPath.DirectoryPath, PackageCheckType.Changelog, false, CancellationToken.None); - var dependencyResult = await _packageCheckTool.RunPackageCheck(_testProjectPath.DirectoryPath, PackageCheckType.Dependency, false, CancellationToken.None); - var readmeResult = await _packageCheckTool.RunPackageCheck(_testProjectPath.DirectoryPath, PackageCheckType.Readme, false, CancellationToken.None); - var spellingResult = await _packageCheckTool.RunPackageCheck(_testProjectPath.DirectoryPath, PackageCheckType.Cspell, false, CancellationToken.None); - var snippetsResult = await _packageCheckTool.RunPackageCheck(_testProjectPath.DirectoryPath, PackageCheckType.Snippets, false, CancellationToken.None); + var allResult = await _packageCheckTool.RunPackageCheck(_testProjectPath.DirectoryPath, PackageCheckType.All, false); + var changelogResult = await _packageCheckTool.RunPackageCheck(_testProjectPath.DirectoryPath, PackageCheckType.Changelog, false); + var dependencyResult = await _packageCheckTool.RunPackageCheck(_testProjectPath.DirectoryPath, PackageCheckType.Dependency, false); + var readmeResult = await _packageCheckTool.RunPackageCheck(_testProjectPath.DirectoryPath, PackageCheckType.Readme, false); + var spellingResult = await _packageCheckTool.RunPackageCheck(_testProjectPath.DirectoryPath, PackageCheckType.Cspell, false); + var snippetsResult = await _packageCheckTool.RunPackageCheck(_testProjectPath.DirectoryPath, PackageCheckType.Snippets, false); // Assert Assert.IsNotNull(allResult); @@ -255,8 +257,8 @@ public async Task RunPackageCheck_WithCspellFixEnabled_WhenFileWithTypos_Returns try { // Act - Test both regular cspell check and with fix enabled - var normalResult = await _packageCheckTool.RunPackageCheck(_testProjectPath.DirectoryPath, PackageCheckType.Cspell, false, CancellationToken.None); - var fixResult = await _packageCheckTool.RunPackageCheck(_testProjectPath.DirectoryPath, PackageCheckType.Cspell, true, CancellationToken.None); + var normalResult = await _packageCheckTool.RunPackageCheck(_testProjectPath.DirectoryPath, PackageCheckType.Cspell, false); + var fixResult = await _packageCheckTool.RunPackageCheck(_testProjectPath.DirectoryPath, PackageCheckType.Cspell, true); // Assert Assert.IsNotNull(normalResult); diff --git a/tools/azsdk-cli/Azure.Sdk.Tools.Cli.Tests/Tools/Generators/SampleGeneratorToolTests.cs b/tools/azsdk-cli/Azure.Sdk.Tools.Cli.Tests/Tools/Generators/SampleGeneratorToolTests.cs index dcea5053f99..0c13e59e60b 100644 --- a/tools/azsdk-cli/Azure.Sdk.Tools.Cli.Tests/Tools/Generators/SampleGeneratorToolTests.cs +++ b/tools/azsdk-cli/Azure.Sdk.Tools.Cli.Tests/Tools/Generators/SampleGeneratorToolTests.cs @@ -26,6 +26,7 @@ public class SampleGeneratorToolTests private Mock telemetryServiceMock; private Mock _mockNpxHelper; private Mock _mockProcessHelper; + private Mock _mockPythonHelper; private Mock _mockPowerShellHelper; private Mock _mockGitHelper; private TestLogger _logger; @@ -347,6 +348,7 @@ public void Setup() _mockNpxHelper = new Mock(); _mockPowerShellHelper = new Mock(); _mockProcessHelper = new Mock(); + _mockPythonHelper = new Mock(); _mockGitHelper = new Mock(); _logger = new TestLogger(); _commonValidationHelpers = new Mock(); @@ -356,7 +358,7 @@ public void Setup() var gitHubServiceMock = new Mock(); realGitHelper = new GitHelper(gitHubServiceMock.Object, gitLogger); _languageServices = [ - new PythonLanguageService(_mockProcessHelper.Object, _mockNpxHelper.Object, realGitHelper, languageLogger, _commonValidationHelpers.Object), + new PythonLanguageService(_mockProcessHelper.Object, _mockPythonHelper.Object, _mockNpxHelper.Object, realGitHelper, languageLogger, _commonValidationHelpers.Object), new JavaLanguageService(_mockProcessHelper.Object, realGitHelper, new Mock().Object, microagentHostServiceMock.Object, languageLogger, _commonValidationHelpers.Object), new JavaScriptLanguageService(_mockProcessHelper.Object, _mockNpxHelper.Object, realGitHelper, languageLogger, _commonValidationHelpers.Object), new GoLanguageService(_mockProcessHelper.Object, _mockNpxHelper.Object, realGitHelper, languageLogger, _commonValidationHelpers.Object), diff --git a/tools/azsdk-cli/Azure.Sdk.Tools.Cli.Tests/Tools/Package/SdkBuildToolTests.cs b/tools/azsdk-cli/Azure.Sdk.Tools.Cli.Tests/Tools/Package/SdkBuildToolTests.cs index 66c01023846..c5a0d8b0272 100644 --- a/tools/azsdk-cli/Azure.Sdk.Tools.Cli.Tests/Tools/Package/SdkBuildToolTests.cs +++ b/tools/azsdk-cli/Azure.Sdk.Tools.Cli.Tests/Tools/Package/SdkBuildToolTests.cs @@ -31,6 +31,7 @@ public class SdkBuildToolTests private SdkBuildTool _tool; private Mock _mockGitHelper; private Mock _mockProcessHelper; + private Mock _mockPythonHelper; private Mock _mockNpxHelper; private Mock _mockPowerShellHelper; private Mock _mockSpecGenSdkConfigHelper; @@ -45,6 +46,7 @@ public void Setup() // Create mocks _mockGitHelper = new Mock(); _mockProcessHelper = new Mock(); + _mockPythonHelper = new Mock(); _mockSpecGenSdkConfigHelper = new Mock(); _mockNpxHelper = new Mock(); _mockPowerShellHelper = new Mock(); @@ -56,7 +58,7 @@ public void Setup() // Create temp directory for tests _tempDirectory = TempDirectory.Create("SdkBuildToolTests"); _languageServices = [ - new PythonLanguageService(_mockProcessHelper.Object, _mockNpxHelper.Object, _mockGitHelper.Object, languageLogger, _commonValidationHelpers.Object), + new PythonLanguageService(_mockProcessHelper.Object, _mockPythonHelper.Object, _mockNpxHelper.Object, _mockGitHelper.Object, languageLogger, _commonValidationHelpers.Object), new JavaLanguageService(_mockProcessHelper.Object, _mockGitHelper.Object, new Mock().Object, mockMicrohostAgent.Object, languageLogger, _commonValidationHelpers.Object), new JavaScriptLanguageService(_mockProcessHelper.Object, _mockNpxHelper.Object, _mockGitHelper.Object, languageLogger, _commonValidationHelpers.Object), new GoLanguageService(_mockProcessHelper.Object, _mockNpxHelper.Object, _mockGitHelper.Object, languageLogger, _commonValidationHelpers.Object), diff --git a/tools/azsdk-cli/Azure.Sdk.Tools.Cli.Tests/Tools/Verify/VerifySetupToolTests.cs b/tools/azsdk-cli/Azure.Sdk.Tools.Cli.Tests/Tools/Verify/VerifySetupToolTests.cs index 8e06bc9cb55..108b6ec9793 100644 --- a/tools/azsdk-cli/Azure.Sdk.Tools.Cli.Tests/Tools/Verify/VerifySetupToolTests.cs +++ b/tools/azsdk-cli/Azure.Sdk.Tools.Cli.Tests/Tools/Verify/VerifySetupToolTests.cs @@ -17,6 +17,7 @@ namespace Azure.Sdk.Tools.Cli.Tests.Tools.Verify; internal class VerifySetupToolTests { private Mock mockProcessHelper; + private Mock mockPythonHelper; private TestLogger logger; private List languageServices; private Mock _mockNpxHelper; @@ -30,6 +31,7 @@ internal class VerifySetupToolTests public void Setup() { mockProcessHelper = new Mock(); + mockPythonHelper = new Mock(); logger = new TestLogger(); _languageLogger = new TestLogger(); @@ -59,7 +61,7 @@ public void Setup() // Create temp directory for tests languageServices = [ - new PythonLanguageService(mockProcessHelper.Object, _mockNpxHelper.Object, _mockGitHelper.Object, _languageLogger, _commonValidationHelpers.Object), + new PythonLanguageService(mockProcessHelper.Object, mockPythonHelper.Object, _mockNpxHelper.Object, _mockGitHelper.Object, _languageLogger, _commonValidationHelpers.Object), new JavaLanguageService(mockProcessHelper.Object, _mockGitHelper.Object, new Mock().Object, _mockMicrohostAgent.Object, _languageLogger, _commonValidationHelpers.Object), new JavaScriptLanguageService(mockProcessHelper.Object, _mockNpxHelper.Object, _mockGitHelper.Object, _languageLogger, _commonValidationHelpers.Object), new GoLanguageService(mockProcessHelper.Object, _mockNpxHelper.Object, _mockGitHelper.Object, _languageLogger, _commonValidationHelpers.Object), diff --git a/tools/azsdk-cli/Azure.Sdk.Tools.Cli/CHANGELOG.md b/tools/azsdk-cli/Azure.Sdk.Tools.Cli/CHANGELOG.md index c0c3d0b6a57..74ac39e19fe 100644 --- a/tools/azsdk-cli/Azure.Sdk.Tools.Cli/CHANGELOG.md +++ b/tools/azsdk-cli/Azure.Sdk.Tools.Cli/CHANGELOG.md @@ -14,6 +14,8 @@ ### Other Changes +- Added a PythonOptions that allows the user to use an env var to declare a python venv + ## 0.5.7 (2025-11-05) ### Features Added diff --git a/tools/azsdk-cli/Azure.Sdk.Tools.Cli/Helpers/Process/CommandHelpers.cs b/tools/azsdk-cli/Azure.Sdk.Tools.Cli/Helpers/Process/CommandHelpers.cs index 23a25369fab..e017bd035f2 100644 --- a/tools/azsdk-cli/Azure.Sdk.Tools.Cli/Helpers/Process/CommandHelpers.cs +++ b/tools/azsdk-cli/Azure.Sdk.Tools.Cli/Helpers/Process/CommandHelpers.cs @@ -30,6 +30,11 @@ public interface IMavenHelper public Task Run(MavenOptions options, CancellationToken ct); } +public interface IPythonHelper +{ + public Task Run(PythonOptions options, CancellationToken ct); +} + public sealed class ProcessHelper(ILogger logger, IRawOutputHelper outputHelper) : ProcessHelperBase(logger, outputHelper), IProcessHelper { @@ -53,3 +58,9 @@ public sealed class MavenHelper(ILogger logger, IRawOutputHelper ou { public async Task Run(MavenOptions options, CancellationToken ct) => await base.Run(options, ct); } + +public sealed class PythonHelper(ILogger logger, IRawOutputHelper outputHelper) + : ProcessHelperBase(logger, outputHelper), IPythonHelper +{ + public async Task Run(PythonOptions options, CancellationToken ct) => await base.Run(options, ct); +} diff --git a/tools/azsdk-cli/Azure.Sdk.Tools.Cli/Helpers/Process/Options/PythonOptions.cs b/tools/azsdk-cli/Azure.Sdk.Tools.Cli/Helpers/Process/Options/PythonOptions.cs new file mode 100644 index 00000000000..8b344b0a8ce --- /dev/null +++ b/tools/azsdk-cli/Azure.Sdk.Tools.Cli/Helpers/Process/Options/PythonOptions.cs @@ -0,0 +1,78 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +using System.Runtime.InteropServices; + +namespace Azure.Sdk.Tools.Cli.Helpers; + +/// +/// Process options for running Python executables with automatic virtual environment resolution. +/// Resolves Python executables from AZSDKTOOLS_PYTHON_VENV_PATH environment variable. +/// +public class PythonOptions : ProcessOptions +{ + // Environment variable user can set in their system environment variables for specifying Python venv path + private static string VenvEnvironmentVariable = "AZSDKTOOLS_PYTHON_VENV_PATH"; + + /// + /// Creates process options for a Python executable with automatic venv resolution. + /// + /// Name of the Python executable (e.g., "python", "pytest", "azpysdk") + /// Command line arguments + /// Working directory for the process + /// Execution timeout + /// Whether to log stdout/stderr + public PythonOptions( + string executableName, + string[] args, + string? workingDirectory = null, + TimeSpan? timeout = null, + bool logOutputStream = true + ) : base( + ResolvePythonExecutable(executableName), + args, + logOutputStream, + workingDirectory, + timeout + ) + { + } + + /// + /// Resolves a Python executable path from venv. + /// Checks in order: AZSDKTOOLS_PYTHON_VENV_PATH env var. + /// + /// Name of the Python executable + /// Resolved executable path + public static string ResolvePythonExecutable(string executableName) + { + // Check environment variable + var venvPath = Environment.GetEnvironmentVariable(VenvEnvironmentVariable); + + // Try to resolve from venv if path is provided + if (!string.IsNullOrWhiteSpace(venvPath)) + { + if (!Directory.Exists(venvPath)) + { + throw new DirectoryNotFoundException( + $"Python venv path specified in {VenvEnvironmentVariable} does not exist: {venvPath}"); + } + + var binDir = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "Scripts" : "bin"; + var venvExecutablePath = Path.Combine(venvPath, binDir, executableName); + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + if (!executableName.EndsWith(".exe", StringComparison.OrdinalIgnoreCase)) + { + return venvExecutablePath + ".exe"; + } + return venvExecutablePath; + } + else + { + return venvExecutablePath; + } + } + return executableName; + } +} diff --git a/tools/azsdk-cli/Azure.Sdk.Tools.Cli/Services/Languages/PythonLanguageService.Checks.cs b/tools/azsdk-cli/Azure.Sdk.Tools.Cli/Services/Languages/PythonLanguageService.Checks.cs index 466caa90d43..a6781627d54 100644 --- a/tools/azsdk-cli/Azure.Sdk.Tools.Cli/Services/Languages/PythonLanguageService.Checks.cs +++ b/tools/azsdk-cli/Azure.Sdk.Tools.Cli/Services/Languages/PythonLanguageService.Checks.cs @@ -18,39 +18,23 @@ public override async Task UpdateSnippets(string packagePa { logger.LogInformation("Starting snippet update for Python project at: {PackagePath}", packagePath); - // Find the repository root from the package path using GitHelper var repoRoot = gitHelper.DiscoverRepoRoot(packagePath); - logger.LogInformation("Found repository root at: {RepoRoot}", repoRoot); - - // Construct path to the Python snippet updater script var scriptPath = Path.Combine(repoRoot, "eng", "tools", "azure-sdk-tools", "ci_tools", "snippet_update", "python_snippet_updater.py"); - // Check if the script exists if (!File.Exists(scriptPath)) { logger.LogError("Python snippet updater script not found at: {ScriptPath}", scriptPath); return new PackageCheckResponse(1, "", $"Python snippet updater script not found at: {scriptPath}"); } - logger.LogInformation("Using Python snippet updater script: {ScriptPath}", scriptPath); - - // Check if Python is available - var pythonCheckResult = await processHelper.Run(new("python", ["--version"], timeout: TimeSpan.FromSeconds(10)), cancellationToken); + var pythonCheckResult = await pythonHelper.Run(new PythonOptions("python", ["--version"]), cancellationToken); if (pythonCheckResult.ExitCode != 0) { logger.LogError("Python is not installed or not available in PATH"); return new PackageCheckResponse(1, "", "Python is not installed or not available in PATH. Please install Python to use snippet update functionality."); } - logger.LogInformation("Python is available: {PythonVersion}", pythonCheckResult.Output.Trim()); - - // Run the Python snippet updater - var command = "python"; - var args = new[] { scriptPath, packagePath }; - - logger.LogInformation("Executing command: {Command} {Arguments}", command, string.Join(" ", args)); - var timeout = TimeSpan.FromMinutes(5); - var result = await processHelper.Run(new(command, args, workingDirectory: packagePath, timeout: timeout), cancellationToken); + var result = await pythonHelper.Run(new PythonOptions("python", [scriptPath, packagePath], workingDirectory: packagePath), cancellationToken); if (result.ExitCode == 0) { @@ -75,29 +59,23 @@ public override async Task LintCode(string packagePath, bo try { logger.LogInformation("Starting code linting for Python project at: {PackagePath}", packagePath); - var timeout = TimeSpan.FromMinutes(10); - // Run multiple linting tools var lintingTools = new[] { - ("pylint", new[] { "azpysdk", "pylint", "--isolate", packagePath }), - ("mypy", new[] { "azpysdk", "mypy", "--isolate", packagePath }), + ("pylint", new PythonOptions("azpysdk", ["pylint", "--isolate", packagePath], workingDirectory: packagePath, timeout: TimeSpan.FromMinutes(3))), + ("mypy", new PythonOptions("azpysdk", ["mypy", "--isolate", packagePath], workingDirectory: packagePath, timeout: TimeSpan.FromMinutes(3))), }; logger.LogInformation("Starting {Count} linting tools in parallel", lintingTools.Length); - // Create tasks for all linting tools to run in parallel var lintingTasks = lintingTools.Select(async tool => { - var (toolName, command) = tool; - var result = await processHelper.Run(new(command[0], command.Skip(1).ToArray(), workingDirectory: packagePath, timeout: timeout), cancellationToken); + var (toolName, options) = tool; + var result = await pythonHelper.Run(options, cancellationToken); return (toolName, result); }); - // Wait for all linting tools to complete var allResults = await Task.WhenAll(lintingTasks); - - // Analyze results var failedTools = allResults.Where(r => r.result.ExitCode != 0).ToList(); if (failedTools.Count == 0) @@ -128,13 +106,8 @@ public override async Task FormatCode(string packagePath, try { logger.LogInformation("Starting code formatting for Python project at: {PackagePath}", packagePath); - // Run azpysdk black - var command = "azpysdk"; - var args = new[] { "black", "--isolate", packagePath }; - - logger.LogInformation("Executing command: {Command} {Arguments}", command, string.Join(" ", args)); - var timeout = TimeSpan.FromMinutes(10); - var result = await processHelper.Run(new(command, args, workingDirectory: packagePath, timeout: timeout), cancellationToken); + + var result = await pythonHelper.Run(new PythonOptions("azpysdk", ["black", "--isolate", packagePath], workingDirectory: packagePath), cancellationToken); if (result.ExitCode == 0) { diff --git a/tools/azsdk-cli/Azure.Sdk.Tools.Cli/Services/Languages/PythonLanguageService.cs b/tools/azsdk-cli/Azure.Sdk.Tools.Cli/Services/Languages/PythonLanguageService.cs index 82b49d8c9eb..b9c6951417c 100644 --- a/tools/azsdk-cli/Azure.Sdk.Tools.Cli/Services/Languages/PythonLanguageService.cs +++ b/tools/azsdk-cli/Azure.Sdk.Tools.Cli/Services/Languages/PythonLanguageService.cs @@ -12,14 +12,17 @@ namespace Azure.Sdk.Tools.Cli.Services.Languages; public sealed partial class PythonLanguageService : LanguageService { private readonly INpxHelper npxHelper; + private readonly IPythonHelper pythonHelper; public PythonLanguageService( IProcessHelper processHelper, + IPythonHelper pythonHelper, INpxHelper npxHelper, IGitHelper gitHelper, ILogger logger, ICommonValidationHelpers commonValidationHelpers) { + this.pythonHelper = pythonHelper; this.npxHelper = npxHelper; base.processHelper = processHelper; base.gitHelper = gitHelper; @@ -137,9 +140,9 @@ public override async Task GetPackageInfo(string packagePath, Cance public override async Task RunAllTests(string packagePath, CancellationToken ct = default) { - var result = await processHelper.Run(new ProcessOptions( - command: "pytest", - args: ["tests"], + var result = await pythonHelper.Run(new PythonOptions( + "pytest", + ["tests"], workingDirectory: packagePath ), ct @@ -148,60 +151,18 @@ public override async Task RunAllTests(string packagePath, CancellationTok return result.ExitCode == 0; } public override List GetRequirements(string packagePath, Dictionary> categories, CancellationToken ct = default) - { - return GetRequirements(packagePath, categories, null, ct); - } - - public List GetRequirements(string packagePath, Dictionary> categories, string venvPath, CancellationToken ct = default) { var reqs = categories.TryGetValue("python", out var requirements) ? requirements : new List(); - if (string.IsNullOrWhiteSpace(venvPath)) - { - venvPath = FindVenv(packagePath); - } - - if (string.IsNullOrWhiteSpace(venvPath) || !Directory.Exists(venvPath)) - { - logger.LogWarning("Provided venv path is invalid or does not exist: {VenvPath}", venvPath); - return reqs; - } - - logger.LogInformation("Using virtual environment at: {VenvPath}", venvPath); - return UpdateChecksWithVenv(reqs, venvPath); - } - - private List UpdateChecksWithVenv(List reqs, string venvPath) - { foreach (var req in reqs) { - // Update checks to use venv path - var binDir = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "Scripts" : "bin"; - req.check[0] = Path.Combine(venvPath, binDir, req.check[0]); - } - - return reqs; - } - - private string FindVenv(string packagePath) - { - // try to find existing venv in package path if none was provided - if (string.IsNullOrWhiteSpace(packagePath)) - { - packagePath = Environment.CurrentDirectory; - } - - var candidates = new[] { ".venv", "venv", ".env", "env" }; - - foreach (var c in candidates) - { - var path = Path.Combine(packagePath, c); - if (Directory.Exists(path)) + if (req.check != null && req.check.Length > 0) { - return Path.GetFullPath(path); + var executableName = req.check[0]; + req.check[0] = PythonOptions.ResolvePythonExecutable(executableName); } } - return null; + return reqs; } } diff --git a/tools/azsdk-cli/Azure.Sdk.Tools.Cli/Services/ServiceRegistrations.cs b/tools/azsdk-cli/Azure.Sdk.Tools.Cli/Services/ServiceRegistrations.cs index 0628eb44a11..8fe3be84fdf 100644 --- a/tools/azsdk-cli/Azure.Sdk.Tools.Cli/Services/ServiceRegistrations.cs +++ b/tools/azsdk-cli/Azure.Sdk.Tools.Cli/Services/ServiceRegistrations.cs @@ -69,6 +69,7 @@ public static void RegisterCommonServices(IServiceCollection services, OutputHel services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); // Services that need to be scoped so we can track/update state across services per request services.AddScoped(); diff --git a/tools/azsdk-cli/Azure.Sdk.Tools.Cli/Tools/Verify/VerifySetupTool.cs b/tools/azsdk-cli/Azure.Sdk.Tools.Cli/Tools/Verify/VerifySetupTool.cs index cbbed4462dc..4ff63d7fab0 100644 --- a/tools/azsdk-cli/Azure.Sdk.Tools.Cli/Tools/Verify/VerifySetupTool.cs +++ b/tools/azsdk-cli/Azure.Sdk.Tools.Cli/Tools/Verify/VerifySetupTool.cs @@ -50,19 +50,12 @@ public VerifySetupTool(IProcessHelper processHelper, ILogger lo DefaultValueFactory = _ => false }; - private readonly Option venvOption = new("--venv-path", "-venv") - { - Description = "Path to Python virtual environment to use for Python requirements checks.", - Required = false - }; - protected override Command GetCommand() => new("setup", "Verify environment setup for MCP release tools") { languagesParam, allLangOption, - SharedOptions.PackagePath, - venvOption + SharedOptions.PackagePath }; public override async Task HandleCommand(ParseResult parseResult, CancellationToken ct) @@ -71,16 +64,15 @@ public override async Task HandleCommand(ParseResult parseResul var allLangs = parseResult.GetValue(allLangOption); var parsed = allLangs ? Enum.GetValues().ToHashSet() : langs.ToHashSet(); var packagePath = parseResult.GetValue(SharedOptions.PackagePath); - var venvPath = parseResult.GetValue(venvOption); - return await VerifySetup(parsed, packagePath, venvPath, ct); + return await VerifySetup(parsed, packagePath, ct); } - [McpServerTool(Name = "azsdk_verify_setup"), Description("Verifies the developer environment for MCP release tool requirements. Accepts a list of supported languages to check requirements for, and the packagePath of the repo to check. Accepts a specific Python virtual environment path to use for Python requirements checks.")] - public async Task VerifySetup(HashSet langs = null, string packagePath = null, string venvPath = null, CancellationToken ct = default) + [McpServerTool(Name = "azsdk_verify_setup"), Description("Verifies the developer environment for MCP release tool requirements. Accepts a list of supported languages to check requirements for, and the packagePath of the repo to check.")] + public async Task VerifySetup(HashSet langs = null, string packagePath = null, CancellationToken ct = default) { try { - List reqsToCheck = GetRequirements(langs, packagePath ?? Environment.CurrentDirectory, venvPath, ct); + List reqsToCheck = GetRequirements(langs, packagePath ?? Environment.CurrentDirectory, ct); VerifySetupResponse response = new VerifySetupResponse { @@ -95,7 +87,7 @@ public async Task VerifySetup(HashSet langs = logger.LogInformation("Checking requirement: {Requirement}, Check: {Check}, Instructions: {Instructions}", req.requirement, req.check, req.instructions); - var task = RunCheck(req, packagePath, venvPath, ct).ContinueWith(t => (req, t.Result), TaskScheduler.Default); + var task = RunCheck(req, packagePath, ct).ContinueWith(t => (req, t.Result), TaskScheduler.Default); checkTasks.Add(task); } @@ -129,7 +121,7 @@ public async Task VerifySetup(HashSet langs = } } - private async Task RunCheck(SetupRequirements.Requirement req, string packagePath, string venvPath, CancellationToken ct) + private async Task RunCheck(SetupRequirements.Requirement req, string packagePath, CancellationToken ct) { var command = req.check; var options = new ProcessOptions( @@ -137,7 +129,7 @@ private async Task RunCheck(SetupRequirements.Requiremen args: command.Skip(1).ToArray(), timeout: TimeSpan.FromSeconds(COMMAND_TIMEOUT_IN_SECONDS), logOutputStream: true, - workingDirectory: venvPath ?? packagePath + workingDirectory: packagePath ); var trimmed = string.Empty; @@ -183,7 +175,7 @@ private async Task RunCheck(SetupRequirements.Requiremen }; } - private List GetRequirements(HashSet languages, string packagePath, string venvPath, CancellationToken ct) + private List GetRequirements(HashSet languages, string packagePath, CancellationToken ct) { // Check core requirements before language-specific requirements var parsedReqs = ParseRequirements(ct); @@ -223,13 +215,6 @@ private async Task RunCheck(SetupRequirements.Requiremen continue; } - if (getter is PythonLanguageService pythonReqCheck && !string.IsNullOrEmpty(venvPath)) - { - // If checking Python and venv path provided, use it - reqsToCheck.AddRange(pythonReqCheck.GetRequirements(packagePath, parsedReqs, venvPath, ct)); - continue; - } - reqsToCheck.AddRange(getter.GetRequirements(packagePath, parsedReqs, ct)); }