diff --git a/src/WebApiDebugger/WebApiDebugger.csproj b/src/WebApiDebugger/WebApiDebugger.csproj
index f3bb99b..827948e 100644
--- a/src/WebApiDebugger/WebApiDebugger.csproj
+++ b/src/WebApiDebugger/WebApiDebugger.csproj
@@ -5,14 +5,13 @@
enable
false
false
- true
+ false
- false
true
GenerateContract
diff --git a/src/WebApiDebugger/openapi-v1.yaml b/src/WebApiDebugger/openapi-v1.yaml
index 2dc5786..5b8b523 100644
--- a/src/WebApiDebugger/openapi-v1.yaml
+++ b/src/WebApiDebugger/openapi-v1.yaml
@@ -1,4 +1,5 @@
-openapi: 3.0.1
+# DO NOT EDIT. This is a generated file
+openapi: 3.0.4
info:
title: WebApiDebugger
version: '1.0'
diff --git a/src/Workleap.OpenApi.MSBuild/SwaggerManager.cs b/src/Workleap.OpenApi.MSBuild/SwaggerManager.cs
index 1f7880b..3260c65 100644
--- a/src/Workleap.OpenApi.MSBuild/SwaggerManager.cs
+++ b/src/Workleap.OpenApi.MSBuild/SwaggerManager.cs
@@ -1,11 +1,13 @@
+using System.Diagnostics;
using System.Runtime.InteropServices;
+using System.Text.RegularExpressions;
using Microsoft.Build.Framework;
namespace Workleap.OpenApi.MSBuild;
internal sealed class SwaggerManager : ISwaggerManager
{
- private const string SwaggerVersion = "9.0.4";
+ private const string DefaultSwaggerVersion = "9.0.4";
private const string DoNotEditComment = "# DO NOT EDIT. This is a generated file\n";
private const int MaxRetryCount = 3;
private readonly IProcessWrapper _processWrapper;
@@ -14,6 +16,7 @@ internal sealed class SwaggerManager : ISwaggerManager
private readonly string _swaggerDirectory;
private readonly string _openApiToolsDirectoryPath;
private readonly string _swaggerExecutablePath;
+ private readonly string _swaggerVersion;
public SwaggerManager(ILoggerWrapper loggerWrapper, IProcessWrapper processWrapper, string openApiToolsDirectoryPath, string openApiWebApiAssemblyPath)
{
@@ -21,7 +24,20 @@ public SwaggerManager(ILoggerWrapper loggerWrapper, IProcessWrapper processWrapp
this._loggerWrapper = loggerWrapper;
this._openApiWebApiAssemblyPath = openApiWebApiAssemblyPath;
this._openApiToolsDirectoryPath = openApiToolsDirectoryPath;
- this._swaggerDirectory = Path.Combine(openApiToolsDirectoryPath, "swagger", SwaggerVersion);
+
+ // Detect Swashbuckle version from the application's assemblies
+ var assemblyDirectory = Path.GetDirectoryName(openApiWebApiAssemblyPath);
+ if (string.IsNullOrEmpty(assemblyDirectory))
+ {
+ this._loggerWrapper.LogMessage("Could not determine assembly directory from path. Using default Swashbuckle CLI version.", MessageImportance.Low);
+ this._swaggerVersion = DefaultSwaggerVersion;
+ }
+ else
+ {
+ this._swaggerVersion = this.DetectSwashbuckleVersion(assemblyDirectory) ?? DefaultSwaggerVersion;
+ }
+
+ this._swaggerDirectory = Path.Combine(openApiToolsDirectoryPath, "swagger", this._swaggerVersion);
this._swaggerExecutablePath = Path.Combine(this._swaggerDirectory, RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "swagger.exe" : "swagger");
}
@@ -48,13 +64,13 @@ public async Task InstallSwaggerCliAsync(CancellationToken cancellationToken)
return;
}
- this._loggerWrapper.LogMessage($"🔧 Installing Swashbuckle.AspNetCore.Cli {SwaggerVersion}...");
+ this._loggerWrapper.LogMessage($"🔧 Installing Swashbuckle.AspNetCore.Cli {this._swaggerVersion}...");
for (var retryCount = 0; retryCount < MaxRetryCount; retryCount++)
{
var result = await this._processWrapper.RunProcessAsync(
"dotnet",
- ["tool", "update", "Swashbuckle.AspNetCore.Cli", "--ignore-failed-sources", "--tool-path", this._swaggerDirectory, "--configfile", Path.Combine(this._openApiToolsDirectoryPath, "nuget.config"), "--version", SwaggerVersion],
+ ["tool", "update", "Swashbuckle.AspNetCore.Cli", "--ignore-failed-sources", "--tool-path", this._swaggerDirectory, "--configfile", Path.Combine(this._openApiToolsDirectoryPath, "nuget.config"), "--version", this._swaggerVersion],
cancellationToken);
var isLastRetry = retryCount == MaxRetryCount - 1;
@@ -75,7 +91,7 @@ public async Task InstallSwaggerCliAsync(CancellationToken cancellationToken)
break;
}
- this._loggerWrapper.LogMessage($"✅ Swashbuckle.AspNetCore.Cli {SwaggerVersion} installed successfully.");
+ this._loggerWrapper.LogMessage($"✅ Swashbuckle.AspNetCore.Cli {this._swaggerVersion} installed successfully.");
}
public async Task GenerateOpenApiSpecAsync(string swaggerExePath, string outputOpenApiSpecPath, string documentName, CancellationToken cancellationToken)
@@ -123,4 +139,58 @@ private void PrependComment(string outputOpenApiSpecPath, string comment)
this._loggerWrapper.LogWarning($"Failed to add comment to generated spec for {outputOpenApiSpecPath}.");
}
}
+
+ private string? DetectSwashbuckleVersion(string? assemblyDirectory)
+ {
+ if (string.IsNullOrEmpty(assemblyDirectory) || !Directory.Exists(assemblyDirectory))
+ {
+ this._loggerWrapper.LogMessage("Could not detect Swashbuckle version: assembly directory not found. Using default version.", MessageImportance.Low);
+ return null;
+ }
+
+ try
+ {
+ // Look for Swashbuckle.AspNetCore.SwaggerGen.dll in the assembly directory
+ // (Swashbuckle.AspNetCore is a metapackage, so we check for one of its actual assemblies)
+ var swashbuckleAssemblyPath = Path.Combine(assemblyDirectory, "Swashbuckle.AspNetCore.SwaggerGen.dll");
+
+ if (!File.Exists(swashbuckleAssemblyPath))
+ {
+ this._loggerWrapper.LogMessage("Could not detect Swashbuckle version: Swashbuckle.AspNetCore.SwaggerGen.dll not found in assembly directory. Using default version.", MessageImportance.Low);
+ return null;
+ }
+
+ // Get the assembly version using FileVersionInfo (doesn't load the assembly)
+ var versionInfo = FileVersionInfo.GetVersionInfo(swashbuckleAssemblyPath);
+ var productVersion = versionInfo.ProductVersion;
+
+ if (!string.IsNullOrEmpty(productVersion))
+ {
+ // Product version might include metadata like "9.0.6+commit", extract just the version number
+ var versionParts = productVersion.Split('+', '-');
+ var versionString = versionParts[0];
+
+ // Validate that the extracted version matches semantic versioning pattern (e.g., 9.0.4, 10.0.1)
+ // Pattern: major.minor.patch format, optionally followed by build number
+ // Requires at least 3 parts (major.minor.patch) as per semantic versioning
+ if (!string.IsNullOrWhiteSpace(versionString) &&
+ Regex.IsMatch(versionString, @"^\d{1,4}\.\d{1,4}\.\d{1,4}(\.\d{1,4})?$"))
+ {
+ this._loggerWrapper.LogMessage($"✓ Detected Swashbuckle.AspNetCore version: {versionString}", MessageImportance.Normal);
+ return versionString;
+ }
+
+ this._loggerWrapper.LogMessage($"Detected version '{versionString}' does not match expected format. Using default version.", MessageImportance.Low);
+ return null;
+ }
+
+ this._loggerWrapper.LogMessage("Could not detect Swashbuckle version from assembly metadata. Using default version.", MessageImportance.Low);
+ return null;
+ }
+ catch (Exception ex)
+ {
+ this._loggerWrapper.LogMessage($"Error detecting Swashbuckle version: {ex.Message}. Using default version.", MessageImportance.Low);
+ return null;
+ }
+ }
}
\ No newline at end of file