1
- // Copyright (c) .NET Foundation. All rights reserved.
1
+ // Copyright (c) .NET Foundation. All rights reserved.
2
2
// Licensed under the MIT License. See LICENSE in the project root for license information.
3
3
4
4
using System . Reflection ;
@@ -15,6 +15,8 @@ public static class DotnetHelpers
15
15
{
16
16
private const string WebJobsTemplateBasePackId = "Microsoft.Azure.WebJobs" ;
17
17
private const string IsolatedTemplateBasePackId = "Microsoft.Azure.Functions.Worker" ;
18
+ private const string TemplatesLockFileName = "func_dotnet_templates.lock" ;
19
+ private static readonly Lazy < Task < HashSet < string > > > _installedTemplatesList = new ( GetInstalledTemplatePackageIds ) ;
18
20
19
21
public static void EnsureDotnet ( )
20
22
{
@@ -67,7 +69,7 @@ public static async Task<string> DetermineTargetFramework(string projectDirector
67
69
68
70
public static async Task DeployDotnetProject ( string name , bool force , WorkerRuntime workerRuntime , string targetFramework = "" )
69
71
{
70
- await TemplateOperation (
72
+ await TemplateOperationAsync (
71
73
async ( ) =>
72
74
{
73
75
var frameworkString = string . IsNullOrEmpty ( targetFramework )
@@ -89,7 +91,7 @@ await TemplateOperation(
89
91
public static async Task DeployDotnetFunction ( string templateName , string functionName , string namespaceStr , string language , WorkerRuntime workerRuntime , AuthorizationLevel ? httpAuthorizationLevel = null )
90
92
{
91
93
ColoredConsole . WriteLine ( $ "{ Environment . NewLine } Creating dotnet function...") ;
92
- await TemplateOperation (
94
+ await TemplateOperationAsync (
93
95
async ( ) =>
94
96
{
95
97
// In .NET 6.0, the 'dotnet new' command requires the short name.
@@ -275,87 +277,128 @@ public static string GetCsprojOrFsproj()
275
277
}
276
278
}
277
279
278
- private static Task TemplateOperation ( Func < Task > action , WorkerRuntime workerRuntime )
280
+ private static async Task TemplateOperationAsync ( Func < Task > action , WorkerRuntime workerRuntime )
279
281
{
280
282
EnsureDotnet ( ) ;
281
283
282
284
if ( workerRuntime == WorkerRuntime . DotnetIsolated )
283
285
{
284
- return IsolatedTemplateOperation ( action ) ;
286
+ await EnsureIsolatedTemplatesInstalled ( ) ;
285
287
}
286
288
else
287
289
{
288
- return WebJobsTemplateOperation ( action ) ;
290
+ await EnsureWebJobsTemplatesInstalled ( ) ;
289
291
}
292
+
293
+ await action ( ) ;
290
294
}
291
295
292
- private static async Task IsolatedTemplateOperation ( Func < Task > action )
296
+ private static async Task EnsureIsolatedTemplatesInstalled ( )
293
297
{
294
- try
298
+ if ( AreDotnetTemplatePackagesInstalled ( await _installedTemplatesList . Value , WebJobsTemplateBasePackId ) )
295
299
{
296
300
await UninstallWebJobsTemplates ( ) ;
297
- await InstallIsolatedTemplates ( ) ;
298
- await action ( ) ;
299
301
}
300
- finally
302
+
303
+ if ( AreDotnetTemplatePackagesInstalled ( await _installedTemplatesList . Value , IsolatedTemplateBasePackId ) )
301
304
{
302
- await UninstallIsolatedTemplates ( ) ;
305
+ return ;
303
306
}
307
+
308
+ await FileLockHelper . WithFileLockAsync ( TemplatesLockFileName , InstallIsolatedTemplates ) ;
304
309
}
305
310
306
- private static async Task WebJobsTemplateOperation ( Func < Task > action )
311
+ private static async Task EnsureWebJobsTemplatesInstalled ( )
307
312
{
308
- try
313
+ if ( AreDotnetTemplatePackagesInstalled ( await _installedTemplatesList . Value , IsolatedTemplateBasePackId ) )
309
314
{
310
315
await UninstallIsolatedTemplates ( ) ;
311
- await InstallWebJobsTemplates ( ) ;
312
- await action ( ) ;
313
316
}
314
- finally
317
+
318
+ if ( AreDotnetTemplatePackagesInstalled ( await _installedTemplatesList . Value , WebJobsTemplateBasePackId ) )
315
319
{
316
- await UninstallWebJobsTemplates ( ) ;
320
+ return ;
317
321
}
322
+
323
+ await FileLockHelper . WithFileLockAsync ( TemplatesLockFileName , InstallWebJobsTemplates ) ;
318
324
}
319
325
320
- private static async Task UninstallIsolatedTemplates ( )
326
+ internal static bool AreDotnetTemplatePackagesInstalled ( HashSet < string > templates , string packageIdPrefix )
321
327
{
322
- string projTemplates = $ "{ IsolatedTemplateBasePackId } .ProjectTemplates";
323
- string itemTemplates = $ "{ IsolatedTemplateBasePackId } .ItemTemplates";
324
-
325
- var exe = new Executable ( "dotnet" , $ "new -u \" { projTemplates } \" ") ;
326
- await exe . RunAsync ( ) ;
328
+ var hasProjectTemplates = templates . Contains ( $ "{ packageIdPrefix } .ProjectTemplates", StringComparer . OrdinalIgnoreCase ) ;
329
+ var hasItemTemplates = templates . Contains ( $ "{ packageIdPrefix } .ItemTemplates", StringComparer . OrdinalIgnoreCase ) ;
327
330
328
- exe = new Executable ( "dotnet" , $ "new -u \" { itemTemplates } \" ") ;
329
- await exe . RunAsync ( ) ;
331
+ return hasProjectTemplates && hasItemTemplates ;
330
332
}
331
333
332
- private static async Task UninstallWebJobsTemplates ( )
334
+ private static async Task < HashSet < string > > GetInstalledTemplatePackageIds ( )
333
335
{
334
- string projTemplates = $ "{ WebJobsTemplateBasePackId } .ProjectTemplates";
335
- string itemTemplates = $ "{ WebJobsTemplateBasePackId } .ItemTemplates";
336
+ var exe = new Executable ( "dotnet" , "new uninstall" , shareConsole : false ) ;
337
+ var output = new StringBuilder ( ) ;
338
+ var exitCode = await exe . RunAsync ( o => output . AppendLine ( o ) , e => output . AppendLine ( e ) ) ;
339
+
340
+ if ( exitCode != 0 )
341
+ {
342
+ throw new CliException ( "Failed to get list of installed template packages" ) ;
343
+ }
344
+
345
+ var lines = output . ToString ( )
346
+ . Split ( [ '\r ' , '\n ' ] , StringSplitOptions . RemoveEmptyEntries ) ;
347
+
348
+ var packageIds = new HashSet < string > ( StringComparer . OrdinalIgnoreCase ) ;
349
+
350
+ const string uninstallPrefix = "dotnet new uninstall " ;
351
+
352
+ foreach ( var line in lines )
353
+ {
354
+ var trimmed = line . Trim ( ) ;
336
355
337
- var exe = new Executable ( "dotnet" , $ "new -u \" { projTemplates } \" ") ;
338
- await exe . RunAsync ( ) ;
356
+ if ( trimmed . StartsWith ( uninstallPrefix , StringComparison . OrdinalIgnoreCase ) )
357
+ {
358
+ var packageId = trimmed . Substring ( uninstallPrefix . Length ) . Trim ( ) ;
359
+ if ( ! string . IsNullOrWhiteSpace ( packageId ) )
360
+ {
361
+ packageIds . Add ( packageId ) ;
362
+ }
363
+ }
364
+ }
339
365
340
- exe = new Executable ( "dotnet" , $ "new -u \" { itemTemplates } \" ") ;
341
- await exe . RunAsync ( ) ;
366
+ return packageIds ;
342
367
}
343
368
369
+ private static Task UninstallIsolatedTemplates ( ) => DotnetTemplatesAction ( "uninstall" , nugetPackageList : [ $ "{ IsolatedTemplateBasePackId } .ProjectTemplates", $ "{ IsolatedTemplateBasePackId } .ItemTemplates"] ) ;
370
+
371
+ private static Task UninstallWebJobsTemplates ( ) => DotnetTemplatesAction ( "uninstall" , nugetPackageList : [ $ "{ WebJobsTemplateBasePackId } .ProjectTemplates", $ "{ WebJobsTemplateBasePackId } .ItemTemplates"] ) ;
372
+
344
373
private static Task InstallWebJobsTemplates ( ) => DotnetTemplatesAction ( "install" , "templates" ) ;
345
374
346
375
private static Task InstallIsolatedTemplates ( ) => DotnetTemplatesAction ( "install" , Path . Combine ( "templates" , $ "net-isolated") ) ;
347
376
348
- private static async Task DotnetTemplatesAction ( string action , string templateDirectory )
377
+ private static async Task DotnetTemplatesAction ( string action , string templateDirectory = null , string [ ] nugetPackageList = null )
349
378
{
350
- var templatesLocation = Path . Combine ( Path . GetDirectoryName ( Assembly . GetExecutingAssembly ( ) . Location ) , templateDirectory ) ;
351
- if ( ! FileSystemHelpers . DirectoryExists ( templatesLocation ) )
379
+ string [ ] list ;
380
+
381
+ if ( ! string . IsNullOrEmpty ( templateDirectory ) )
382
+ {
383
+ var templatesLocation = Path . Combine (
384
+ Path . GetDirectoryName ( Assembly . GetExecutingAssembly ( ) . Location ) ,
385
+ templateDirectory ) ;
386
+
387
+ if ( ! FileSystemHelpers . DirectoryExists ( templatesLocation ) )
388
+ {
389
+ throw new CliException ( $ "Can't find templates location. Looked under '{ templatesLocation } '") ;
390
+ }
391
+
392
+ list = Directory . GetFiles ( templatesLocation , "*.nupkg" , SearchOption . TopDirectoryOnly ) ;
393
+ }
394
+ else
352
395
{
353
- throw new CliException ( $ "Can't find templates location. Looked under ' { templatesLocation } '" ) ;
396
+ list = nugetPackageList ?? Array . Empty < string > ( ) ;
354
397
}
355
398
356
- foreach ( var nupkg in Directory . GetFiles ( templatesLocation , "*.nupkg" , SearchOption . TopDirectoryOnly ) )
399
+ foreach ( var nupkg in list )
357
400
{
358
- var exe = new Executable ( "dotnet" , $ "new -- { action } \" { nupkg } \" ") ;
401
+ var exe = new Executable ( "dotnet" , $ "new { action } \" { nupkg } \" ") ;
359
402
await exe . RunAsync ( ) ;
360
403
}
361
404
}
0 commit comments