88using Microsoft . Extensions . DependencyInjection ;
99using Microsoft . Extensions . Hosting ;
1010using Microsoft . Extensions . Options ;
11+ using Microsoft . OpenApi ;
1112using Microsoft . OpenApi . Writers ;
1213using Swashbuckle . AspNetCore . Swagger ;
1314
1415namespace Swashbuckle . AspNetCore . Cli
1516{
1617 internal class Program
1718 {
19+ private const string OpenApiVersionOption = "--openapiversion" ;
20+ private const string SerializeAsV2Flag = "--serializeasv2" ;
21+
1822 public static int Main ( string [ ] args )
1923 {
2024 // Helper to simplify command line parsing etc.
@@ -26,25 +30,28 @@ public static int Main(string[] args)
2630 // startupassembly and it's transitive dependencies. See https://github.com/dotnet/coreclr/issues/13277 for more.
2731
2832 // > dotnet swagger tofile ...
29- runner . SubCommand ( "tofile" , "retrieves Swagger from a startup assembly, and writes to file " , c =>
33+ runner . SubCommand ( "tofile" , "retrieves Swagger from a startup assembly, and writes to file" , c =>
3034 {
3135 c . Argument ( "startupassembly" , "relative path to the application's startup assembly" ) ;
3236 c . Argument ( "swaggerdoc" , "name of the swagger doc you want to retrieve, as configured in your startup class" ) ;
3337
3438 c . Option ( "--output" , "relative path where the Swagger will be output, defaults to stdout" ) ;
3539 c . Option ( "--host" , "a specific host to include in the Swagger output" ) ;
3640 c . Option ( "--basepath" , "a specific basePath to include in the Swagger output" ) ;
37- c . Option ( "--serializeasv2" , "output Swagger in the V2 format rather than V3" , true ) ;
41+ c . Option ( OpenApiVersionOption , "output Swagger in the specified version, defaults to 3.0" ) ;
3842 c . Option ( "--yaml" , "exports swagger in a yaml format" , true ) ;
3943
44+ // TODO Remove this option in the major version that adds support for OpenAPI 3.1
45+ c . Option ( SerializeAsV2Flag , "output Swagger in the V2 format rather than V3 [deprecated]" , true ) ;
46+
4047 c . OnRun ( ( namedArgs ) =>
4148 {
4249 string subProcessCommandLine = PrepareCommandLine ( args , namedArgs ) ;
4350
44- var subProcess = Process . Start ( "dotnet" , subProcessCommandLine ) ;
51+ using var child = Process . Start ( "dotnet" , subProcessCommandLine ) ;
4552
46- subProcess . WaitForExit ( ) ;
47- return subProcess . ExitCode ;
53+ child . WaitForExit ( ) ;
54+ return child . ExitCode ;
4855 } ) ;
4956 } ) ;
5057
@@ -56,8 +63,12 @@ public static int Main(string[] args)
5663 c . Option ( "--output" , "" ) ;
5764 c . Option ( "--host" , "" ) ;
5865 c . Option ( "--basepath" , "" ) ;
59- c . Option ( "--serializeasv2" , "" , true ) ;
66+ c . Option ( OpenApiVersionOption , "" ) ;
6067 c . Option ( "--yaml" , "" , true ) ;
68+
69+ // TODO Remove this option in the major version that adds support for OpenAPI 3.1
70+ c . Option ( SerializeAsV2Flag , "" , true ) ;
71+
6172 c . OnRun ( ( namedArgs ) =>
6273 {
6374 SetupAndRetrieveSwaggerProviderAndOptions ( namedArgs , out var swaggerProvider , out var swaggerOptions ) ;
@@ -94,24 +105,40 @@ public static int Main(string[] args)
94105 writer = new OpenApiJsonWriter ( streamWriter ) ;
95106 }
96107
97- if ( namedArgs . ContainsKey ( "--serializeasv2" ) )
108+ OpenApiSpecVersion specVersion = OpenApiSpecVersion . OpenApi3_0 ;
109+
110+ if ( namedArgs . TryGetValue ( OpenApiVersionOption , out var versionArg ) )
98111 {
99- if ( swaggerDocumentSerializer != null )
112+ specVersion = versionArg switch
100113 {
101- swaggerDocumentSerializer . SerializeDocument ( swagger , writer , Microsoft . OpenApi . OpenApiSpecVersion . OpenApi2_0 ) ;
102- }
103- else
104- {
105- swagger . SerializeAsV2 ( writer ) ;
106- }
114+ "2.0" => OpenApiSpecVersion . OpenApi2_0 ,
115+ "3.0" => OpenApiSpecVersion . OpenApi3_0 ,
116+ _ => throw new NotSupportedException ( $ "The specified OpenAPI version \" { versionArg } \" is not supported.") ,
117+ } ;
107118 }
108- else if ( swaggerDocumentSerializer != null )
119+ else if ( namedArgs . ContainsKey ( SerializeAsV2Flag ) )
120+ {
121+ specVersion = OpenApiSpecVersion . OpenApi2_0 ;
122+ WriteSerializeAsV2DeprecationWarning ( ) ;
123+ }
124+
125+ if ( swaggerDocumentSerializer != null )
109126 {
110- swaggerDocumentSerializer . SerializeDocument ( swagger , writer , Microsoft . OpenApi . OpenApiSpecVersion . OpenApi3_0 ) ;
127+ swaggerDocumentSerializer . SerializeDocument ( swagger , writer , specVersion ) ;
111128 }
112129 else
113130 {
114- swagger . SerializeAsV3 ( writer ) ;
131+ switch ( specVersion )
132+ {
133+ case OpenApiSpecVersion . OpenApi2_0 :
134+ swagger . SerializeAsV2 ( writer ) ;
135+ break ;
136+
137+ case OpenApiSpecVersion . OpenApi3_0 :
138+ default :
139+ swagger . SerializeAsV3 ( writer ) ;
140+ break ;
141+ }
115142 }
116143
117144 if ( outputPath != null )
@@ -132,10 +159,10 @@ public static int Main(string[] args)
132159 {
133160 string subProcessCommandLine = PrepareCommandLine ( args , namedArgs ) ;
134161
135- var subProcess = Process . Start ( "dotnet" , subProcessCommandLine ) ;
162+ using var child = Process . Start ( "dotnet" , subProcessCommandLine ) ;
136163
137- subProcess . WaitForExit ( ) ;
138- return subProcess . ExitCode ;
164+ child . WaitForExit ( ) ;
165+ return child . ExitCode ;
139166 } ) ;
140167 } ) ;
141168
@@ -147,7 +174,7 @@ public static int Main(string[] args)
147174 c . OnRun ( ( namedArgs ) =>
148175 {
149176 SetupAndRetrieveSwaggerProviderAndOptions ( namedArgs , out var swaggerProvider , out var swaggerOptions ) ;
150- IList < string > docNames = new List < string > ( ) ;
177+ IList < string > docNames = [ ] ;
151178
152179 string outputPath = namedArgs . TryGetValue ( "--output" , out var arg1 )
153180 ? Path . Combine ( Directory . GetCurrentDirectory ( ) , arg1 )
@@ -185,7 +212,7 @@ public static int Main(string[] args)
185212 return runner . Run ( args ) ;
186213 }
187214
188- private static void SetupAndRetrieveSwaggerProviderAndOptions ( System . Collections . Generic . IDictionary < string , string > namedArgs , out ISwaggerProvider swaggerProvider , out IOptions < SwaggerOptions > swaggerOptions )
215+ private static void SetupAndRetrieveSwaggerProviderAndOptions ( IDictionary < string , string > namedArgs , out ISwaggerProvider swaggerProvider , out IOptions < SwaggerOptions > swaggerOptions )
189216 {
190217 // 1) Configure host with provided startupassembly
191218 var startupAssembly = AssemblyLoadContext . Default . LoadFromAssemblyPath (
@@ -199,7 +226,7 @@ private static void SetupAndRetrieveSwaggerProviderAndOptions(System.Collections
199226 swaggerOptions = serviceProvider . GetService < IOptions < SwaggerOptions > > ( ) ;
200227 }
201228
202- private static string PrepareCommandLine ( string [ ] args , System . Collections . Generic . IDictionary < string , string > namedArgs )
229+ private static string PrepareCommandLine ( string [ ] args , IDictionary < string , string > namedArgs )
203230 {
204231 if ( ! File . Exists ( namedArgs [ "startupassembly" ] ) )
205232 {
@@ -222,7 +249,7 @@ private static string PrepareCommandLine(string[] args, System.Collections.Gener
222249 EscapePath ( runtimeConfig ) ,
223250 EscapePath ( typeof ( Program ) . GetTypeInfo ( ) . Assembly . Location ) ,
224251 commandName ,
225- string . Join ( " " , subProcessArguments . Select ( x => EscapePath ( x ) ) )
252+ string . Join ( " " , subProcessArguments . Select ( EscapePath ) )
226253 ) ;
227254 return subProcessCommandLine ;
228255 }
@@ -301,5 +328,21 @@ private static bool TryGetCustomHost<THost>(
301328 host = ( THost ) factoryMethod . Invoke ( null , null ) ;
302329 return true ;
303330 }
331+
332+ private static void WriteSerializeAsV2DeprecationWarning ( )
333+ {
334+ const string AppName = "Swashbuckle.AspNetCore.Cli" ;
335+
336+ string message = $ "The { SerializeAsV2Flag } flag will be removed in a future version of { AppName } . Use the { OpenApiVersionOption } option instead.";
337+
338+ Console . WriteLine ( message ) ;
339+
340+ // See https://docs.github.com/actions/writing-workflows/choosing-what-your-workflow-does/workflow-commands-for-github-actions#setting-a-warning-message
341+ // and https://docs.github.com/actions/writing-workflows/choosing-what-your-workflow-does/store-information-in-variables#default-environment-variables
342+ if ( Environment . GetEnvironmentVariable ( "GITHUB_ACTIONS" ) is "true" )
343+ {
344+ Console . WriteLine ( $ "::warning title={ AppName } ::{ message } ") ;
345+ }
346+ }
304347 }
305348}
0 commit comments