diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4e715171e6..8811159f84 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -27,6 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Emit `[GeneratedCode]` attribute for C# types. [#4907](https://github.com/microsoft/kiota/issues/4907)
- Fixes error property disambiguation when the property has the same name as class [#4893](https://github.com/microsoft/kiota/issues/)
- Fixes missing imports for `UntypedNode` for method parameter and return value scenarios. [#4925](https://github.com/microsoft/kiota/issues/4925)
+- Normalize path separators in lock and workspace files to use `/` as the path separator. [#4228](https://github.com/microsoft/kiota/issues/4228)
## [1.15.0] - 2024-06-06
diff --git a/src/Kiota.Builder/Configuration/GenerationConfiguration.cs b/src/Kiota.Builder/Configuration/GenerationConfiguration.cs
index 7200211e63..1f9de43628 100644
--- a/src/Kiota.Builder/Configuration/GenerationConfiguration.cs
+++ b/src/Kiota.Builder/Configuration/GenerationConfiguration.cs
@@ -197,7 +197,7 @@ private string NormalizeDescriptionLocation(string targetDirectory)
!OpenAPIFilePath.StartsWith("http", StringComparison.OrdinalIgnoreCase) &&
Path.IsPathRooted(OpenAPIFilePath) &&
Path.GetFullPath(OpenAPIFilePath).StartsWith(Path.GetFullPath(targetDirectory), StringComparison.Ordinal))
- return "./" + Path.GetRelativePath(targetDirectory, OpenAPIFilePath);
+ return "./" + Path.GetRelativePath(targetDirectory, OpenAPIFilePath).NormalizePathSeparators();
return OpenAPIFilePath;
}
public bool IsPluginConfiguration => PluginTypes.Count != 0;
diff --git a/src/Kiota.Builder/Extensions/StringExtensions.cs b/src/Kiota.Builder/Extensions/StringExtensions.cs
index 1eb95f5f9d..1c66ed83d3 100644
--- a/src/Kiota.Builder/Extensions/StringExtensions.cs
+++ b/src/Kiota.Builder/Extensions/StringExtensions.cs
@@ -299,4 +299,10 @@ public static string GetFileExtension(this string path)
if (string.IsNullOrEmpty(path)) return string.Empty;
return Path.GetExtension(path).TrimStart('.');
}
+ public static string NormalizePathSeparators(this string path)
+ {
+ if (string.IsNullOrEmpty(path)) return string.Empty;
+ if (Path.DirectorySeparatorChar != '/') return path.Replace(Path.DirectorySeparatorChar, '/');
+ return path;
+ }
}
diff --git a/src/Kiota.Builder/Lock/LockManagementService.cs b/src/Kiota.Builder/Lock/LockManagementService.cs
index db4a17c30d..b35c2ba12f 100644
--- a/src/Kiota.Builder/Lock/LockManagementService.cs
+++ b/src/Kiota.Builder/Lock/LockManagementService.cs
@@ -7,6 +7,7 @@
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
+using Kiota.Builder.Extensions;
namespace Kiota.Builder.Lock;
@@ -83,7 +84,7 @@ private static string GetRelativeDescriptionPath(string descriptionPath, string
{
if (IsDescriptionLocal(descriptionPath) &&
Path.GetDirectoryName(lockFilePath) is string lockFileDirectoryPath)
- return Path.GetRelativePath(lockFileDirectoryPath, descriptionPath);
+ return Path.GetRelativePath(lockFileDirectoryPath, descriptionPath).NormalizePathSeparators();
return descriptionPath;
}
///
diff --git a/src/Kiota.Builder/WorkspaceManagement/BaseApiConsumerConfiguration.cs b/src/Kiota.Builder/WorkspaceManagement/BaseApiConsumerConfiguration.cs
index 5bdbf36388..823b95cea8 100644
--- a/src/Kiota.Builder/WorkspaceManagement/BaseApiConsumerConfiguration.cs
+++ b/src/Kiota.Builder/WorkspaceManagement/BaseApiConsumerConfiguration.cs
@@ -3,6 +3,7 @@
using System.IO;
using System.Linq;
using Kiota.Builder.Configuration;
+using Kiota.Builder.Extensions;
using Microsoft.OpenApi.ApiManifest;
namespace Kiota.Builder.WorkspaceManagement;
@@ -41,12 +42,12 @@ private protected BaseApiConsumerConfiguration(GenerationConfiguration config)
public void NormalizeOutputPath(string targetDirectory)
{
if (Path.IsPathRooted(OutputPath))
- OutputPath = "./" + Path.GetRelativePath(targetDirectory, OutputPath);
+ OutputPath = "./" + Path.GetRelativePath(targetDirectory, OutputPath).NormalizePathSeparators();
}
public void NormalizeDescriptionLocation(string targetDirectory)
{
if (Path.IsPathRooted(DescriptionLocation) && Path.GetFullPath(DescriptionLocation).StartsWith(Path.GetFullPath(targetDirectory), StringComparison.Ordinal) && !DescriptionLocation.StartsWith("http", StringComparison.OrdinalIgnoreCase))
- DescriptionLocation = "./" + Path.GetRelativePath(targetDirectory, DescriptionLocation);
+ DescriptionLocation = "./" + Path.GetRelativePath(targetDirectory, DescriptionLocation).NormalizePathSeparators();
}
protected void CloneBase(BaseApiConsumerConfiguration target)
{
diff --git a/src/Kiota.Builder/WorkspaceManagement/WorkspaceManagementService.cs b/src/Kiota.Builder/WorkspaceManagement/WorkspaceManagementService.cs
index d98f548cc8..dd5ac7e8d5 100644
--- a/src/Kiota.Builder/WorkspaceManagement/WorkspaceManagementService.cs
+++ b/src/Kiota.Builder/WorkspaceManagement/WorkspaceManagementService.cs
@@ -331,7 +331,7 @@ public async Task> MigrateFromLockFileAsync(string clientNam
}
var generationConfiguration = new GenerationConfiguration();
lockInfo.UpdateGenerationConfigurationFromLock(generationConfiguration);
- generationConfiguration.OutputPath = "./" + Path.GetRelativePath(WorkingDirectory, lockFileDirectory);
+ generationConfiguration.OutputPath = "./" + Path.GetRelativePath(WorkingDirectory, lockFileDirectory).NormalizePathSeparators();
if (!string.IsNullOrEmpty(clientName))
{
generationConfiguration.ClientClassName = clientName;
diff --git a/tests/Kiota.Builder.Tests/Lock/LockManagementServiceTests.cs b/tests/Kiota.Builder.Tests/Lock/LockManagementServiceTests.cs
index a00aafe3ff..f88c264885 100644
--- a/tests/Kiota.Builder.Tests/Lock/LockManagementServiceTests.cs
+++ b/tests/Kiota.Builder.Tests/Lock/LockManagementServiceTests.cs
@@ -50,7 +50,7 @@ public async Task UsesRelativePaths()
var outputDirectory = Path.Combine(tmpPath, "output");
Directory.CreateDirectory(outputDirectory);
await lockManagementService.WriteLockFileAsync(outputDirectory, lockFile);
- Assert.Equal($"..{Path.DirectorySeparatorChar}information{Path.DirectorySeparatorChar}description.yml", lockFile.DescriptionLocation, StringComparer.OrdinalIgnoreCase);
+ Assert.Equal("../information/description.yml", lockFile.DescriptionLocation, StringComparer.OrdinalIgnoreCase);
}
[Fact]
public async Task DeletesALock()