-
Notifications
You must be signed in to change notification settings - Fork 809
Add VersionChecker to detect and warn about newer Bicep CLI installations #18375
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Add VersionChecker to detect and warn about newer Bicep CLI installations #18375
Conversation
…ions VersionChecker helper scans well-known install locations, runs `bicep --version` to parse versions, and reports any installations newer than the running CLI.
|
Test this change out locally with the following install scripts (Action run 19186198907) VSCode
Azure CLI
|
Dotnet Test Results 102 files - 42 102 suites - 42 41m 1s ⏱️ - 35m 23s Results for commit f8c3d61. ± Comparison against base commit 8ea021c. This pull request removes 1954 and adds 690 tests. Note that renamed tests count towards both.♻️ This comment has been updated with latest results. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a pretty major behavioral change, and could have serious performance implications that we need to think through, as well as product design implications. At minimum, I think we need to cover this as a topic in Bicep Discussions before continuing on with the change.
Generally, I think my feeling with doing this work is that the cons outweigh the pros. Issue #5070 was created a long time ago, and since then, I don't think it's been as much of a problem as we imagined it being.
| var startInfo = new ProcessStartInfo | ||
| { | ||
| FileName = bicepPath, | ||
| Arguments = "--version", | ||
| RedirectStandardOutput = true, | ||
| RedirectStandardError = true, | ||
| UseShellExecute = false, | ||
| CreateNoWindow = true | ||
| }; | ||
|
|
||
| using var process = Process.Start(startInfo); | ||
| if (process == null) | ||
| { | ||
| return null; | ||
| } | ||
|
|
||
| process.WaitForExit(5000); | ||
|
|
||
| if (process.ExitCode != 0) | ||
| { | ||
| return null; | ||
| } | ||
|
|
||
| var output = process.StandardOutput.ReadToEnd(); | ||
|
|
||
| // Parse version from output like "Bicep CLI version 0.30.23 (abc123)" | ||
| // Extract the version number between "version " and the next space or parenthesis | ||
| var versionMatch = System.Text.RegularExpressions.Regex.Match( | ||
| output, | ||
| @"version\s+(\d+\.\d+\.\d+)", | ||
| System.Text.RegularExpressions.RegexOptions.IgnoreCase); | ||
|
|
||
| if (versionMatch.Success && Version.TryParse(versionMatch.Groups[1].Value, out var version)) | ||
| { | ||
| return version; | ||
| } | ||
|
|
||
| return null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is going to add significant time to process start in the best case (becuase of .NET cold start delays), and in the worst case, you may have created an elaborate fork bomb if there are any cycles.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fair concerns. I've addressed your points in the updated implementation;
Zero Process Start Impact:
- The check runs asynchronously with fire-and-forget (
Task.Runwithout awaiting) - CLI startup continues immediately without blocking. - No impact on cold start time - the background task runs completely independently
No Process Spawning (No Fork Bomb Risk):
- Uses
FileVersionInfo.GetVersionInfo()instead of spawning processes - Directly reads the binary metadata from the file system
- No child processes created, so no risk of fork bombs or cycles
Additional Safeguards:
- 250ms timeout with
CancellationToken- hard stop regardless of how many locations exist - Optimized scan - only checks specific well-known locations (no recursive directory search)
- Deduplication - tracks
checkedPathsto avoid scanning the same binary twice - Command filtering - only runs for "significant" commands (
build,deploy,test, etc.), skips--version,--help,jsonrpc, etc. - Terminal detection - skips when
stdout/stderris redirected - Silent failure - any exceptions are caught and ignored to never disrupt normal operations
Performance characteristics:
- User-perceived startup time: 0ms (async fire-and-forget)
- Background scan time: ≤250ms (enforced by timeout)
- Directories scanned: certain locations (platform-specific, non-recursive)
- no process spawning: uses
FileVersionInfoinstead
The implementation prioritizes CLI responsiveness while providing helpful warnings to users who may unknowingly have multiple installations.
What you think?
…ing process Avoid spawning a bicep process and parsing its stdout. Check file existence earlier and read FileVersionInfo.FileVersion, parsing it with Version.TryParse and returning null for missing/invalid versions.
Make VersionChecker.CheckForNewerVersions non-blocking by introducing CheckForNewerVersionsAsync that runs the scan in a background task with a 100ms timeout and cancellation token. Add a shouldCheck parameter so callers can skip the scan for lightweight commands or when output is redirected. Make scanning cancellation-aware and restrict file searches to the top directory only. Remove scanning of PATH entries from well-known locations. Update Program to compute whether the check should run (ShouldCheckForNewerVersions) and invoke the new async method.
- Add VersionCheckerTests with coverage for: - asynchronous check behavior (shouldCheck flag) - no/newer/older/multiple versions detection - platform-specific install locations (Linux/Windows) - non-existent directories, invalid versions, and cancellation handling - Add TestableVersionChecker to mock Bicep file versions and normalize paths for reliable tests - Make FindNewerVersions and GetWellKnownInstallLocations public, and make GetBicepVersion protected virtual to allow overriding in tests
VersionCheckerhelper scans well-known install locations, runsbicep --versionto parse versions, and reports any installations newer than the running CLI.Fixes #5070
Description
Well-known installation locations checked:
~/.bicep/bin(default location)~/.azure/bin(Azure CLI installation location)Windows-specific:
C:\Program Files\Bicep CLIC:\Program Files (x86)\Bicep CLILinux-specific:
/usr/local/bin/usr/bin/Example Usage
Run any bicep command. You may see this warning in the terminal;
Checklist
Microsoft Reviewers: Open in CodeFlow