@@ -15,7 +15,7 @@ internal class DataRetriever : IDisposable
15
15
private const string MetadataQueryTemplate = "{{\" command\" :\" {0}\" }}" ;
16
16
private const string MetadataEndpoint = "https://cli-validation-tool-meta-qry.azurewebsites.net/api/command_metadata" ;
17
17
18
- private static string s_azPythonPath ;
18
+ private static readonly string s_azCompleteCmd , s_azCompleteArg ;
19
19
private static readonly Dictionary < string , NamingRule > s_azNamingRules ;
20
20
private static readonly ConcurrentDictionary < string , AzCLICommand > s_azStaticDataCache ;
21
21
@@ -306,26 +306,45 @@ static DataRetriever()
306
306
s_azNamingRules . Add ( rule . AzPSCommand , rule ) ;
307
307
}
308
308
309
- s_azPythonPath = GetAzCLIPythonPath ( ) ;
309
+ ( s_azCompleteCmd , s_azCompleteArg ) = GetAzCLIPythonPath ( ) ;
310
310
s_azStaticDataCache = new ( StringComparer . OrdinalIgnoreCase ) ;
311
311
}
312
312
313
313
/// <summary>
314
314
/// TODO: Need to support Linux and macOS.
315
315
/// </summary>
316
- private static string GetAzCLIPythonPath ( )
316
+ private static ( string compCmd , string compArg ) GetAzCLIPythonPath ( )
317
317
{
318
318
if ( OperatingSystem . IsWindows ( ) )
319
319
{
320
- const string AzWindowsPath = @"Microsoft SDKs\Azure\CLI2\python.exe" ;
321
- string x64Path = Path . Combine ( Environment . GetFolderPath ( Environment . SpecialFolder . ProgramFiles ) , AzWindowsPath ) ;
322
- string x86Path = Path . Combine ( Environment . GetFolderPath ( Environment . SpecialFolder . ProgramFilesX86 ) , AzWindowsPath ) ;
320
+ const string AzWinCmd = @"Microsoft SDKs\Azure\CLI2\python.exe" ;
321
+ const string AzWinArg = "-Im azure.cli" ;
323
322
324
- if ( File . Exists ( x64Path ) ) { return x64Path ; }
325
- if ( File . Exists ( x86Path ) ) { return x86Path ; }
323
+ string x64Path = Path . Combine ( Environment . GetFolderPath ( Environment . SpecialFolder . ProgramFiles ) , AzWinCmd ) ;
324
+ string x86Path = Path . Combine ( Environment . GetFolderPath ( Environment . SpecialFolder . ProgramFilesX86 ) , AzWinCmd ) ;
325
+
326
+ if ( File . Exists ( x64Path ) ) { return ( x64Path , AzWinArg ) ; }
327
+ if ( File . Exists ( x86Path ) ) { return ( x86Path , AzWinArg ) ; }
328
+ }
329
+ else
330
+ {
331
+ // On Linux and macOS, simply run 'az' as starting a new process is cheap.
332
+ string pathEnv = Environment . GetEnvironmentVariable ( "PATH" ) ;
333
+ if ( ! string . IsNullOrEmpty ( pathEnv ) )
334
+ {
335
+ string [ ] paths = pathEnv . Split ( Path . PathSeparator , StringSplitOptions . RemoveEmptyEntries ) ;
336
+ foreach ( string path in paths )
337
+ {
338
+ string fullPath = Path . Combine ( path , "az" ) ;
339
+ if ( File . Exists ( fullPath ) )
340
+ {
341
+ return ( fullPath , string . Empty ) ;
342
+ }
343
+ }
344
+ }
326
345
}
327
346
328
- return null ;
347
+ return ( null , null ) ;
329
348
}
330
349
331
350
internal DataRetriever ( ResponseData data , HttpClient httpClient )
@@ -496,7 +515,7 @@ private List<string> GetArgValues(ArgumentPair pair)
496
515
hasCompleter = param . HasCompleter ;
497
516
}
498
517
499
- if ( _stop || ! hasCompleter || s_azPythonPath is null ) { return null ; }
518
+ if ( _stop || ! hasCompleter || s_azCompleteCmd is null ) { return null ; }
500
519
501
520
// Then, try to get dynamic argument values using AzCLI tab completion.
502
521
string commandLine = $ "{ pair . Command } { pair . Parameter } ";
@@ -510,11 +529,14 @@ private List<string> GetArgValues(ArgumentPair pair)
510
529
{
511
530
StartInfo = new ProcessStartInfo ( )
512
531
{
513
- FileName = s_azPythonPath ,
514
- Arguments = "-Im azure.cli" ,
532
+ FileName = s_azCompleteCmd ,
533
+ Arguments = s_azCompleteArg ,
515
534
UseShellExecute = false ,
516
535
RedirectStandardOutput = true ,
517
536
RedirectStandardError = true ,
537
+ // Redirect stdin to force installing a missing extension, otherwise 'az'
538
+ // may prompt interactively for user's approval to install.
539
+ RedirectStandardInput = true ,
518
540
}
519
541
} ;
520
542
0 commit comments