feat: add azd tool command group for unified Azure tooling management#7450
Conversation
jongio
left a comment
There was a problem hiding this comment.
POC for the azd tool command group - structure is clean and well-decomposed across manifest, detector, installer, manager, and update checker.
Issues to address:
- pkg/tool/installer.go:389 - splitCommand can't handle shell pipes, breaking Linux az-cli install
- cli/azd/cover - test coverage output file accidentally committed
- pkg/tool/manifest.go:247 - MCP server detection via npx downloads packages on every detection call
- cmd/tool.go:522 - upgrade action pre-executes then creates a task list for display only
- cmd/root.go:546 - isWorkflowCommand uses a hardcoded map instead of existing command group annotations
|
@jongio - review again please. |
jongio
left a comment
There was a problem hiding this comment.
Previous comments addressed. Two new findings on the latest code:
- pkg/tool/detector.go -
detectCommandBasedreports Server/Library tools (MCP server, azd AI extensions) as installed when only the shared binary (npm, azd) is on PATH, even when the specific package isn't installed - cmd/middleware/tool_first_run.go and tool_update_check.go have no unit tests despite complex skip logic and background goroutine management
One nit:
- cmd/tool.go - install strategies in
showcommand display in non-deterministic map iteration order
jongio
left a comment
There was a problem hiding this comment.
Previous comments addressed. A few new edge cases:
- cmd/middleware/tool_first_run.go:170 - detection failure permanently kills the first-run experience
- pkg/tool/installer.go:194 - ensurePlatform lazy cache has no synchronization
- pkg/tool/update_checker.go:176 - cache read errors silently discarded
One test gap: no coverage for the commandBased false-positive fix (Server/Library tool with VersionRegex set + non-matching output should report not installed).
jongio
left a comment
There was a problem hiding this comment.
Round 4 - three new findings in code paths previous rounds didn't cover.
Smaller items (not blocking):
-
detectCommandBasedtest gap: there's no test case for "VersionRegex is set but output doesn't match - tool should NOT be installed." The round 3 fix (gatingInstalledon regex match) works correctly, but a regression test inTestDetectTool_CommandBasedwould lock it down. -
HasUpdatesAvailable(update_checker.go) counts uninstalled tools as needing updates. The conditionlatest != s.InstalledVersionis true when the tool isn't installed (InstalledVersionis empty) butlatestis non-empty. Currently unreachable in the POC since no remote API populatesLatestVersion, but it'll bite when that's added. Addings.Installedto the guard fixes it. -
Middleware test coverage:
tool_first_run.go(327 lines) andtool_update_check.go(176 lines) still have zero tests despite complex skip logic, CI detection, and background goroutine management. Mentioned this in round 2 body but calling it out again.
- Fix VS Code extension strategies to use PackageId instead of InstallCommand - Fix task callbacks to find correct result by tool ID instead of using results[0] - Wire ShouldShowNotification through Manager to gate update notifications Fixes review comments from jongio on Azure#7450 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
jongio
left a comment
There was a problem hiding this comment.
Prior feedback addressed - the results[0] fix now iterates to find the matching tool by ID, VS Code extensions correctly use PackageId, and the middleware notification guard is in place. Two notes on the new direct download code.
- Add partial checksum config validation with clear error messages - Respect AZD_CONFIG_DIR for tool install directory via config.GetUserConfigDir() - Merge with main to resolve cspell.yaml conflict Fixes review comments from jongio on Azure#7450 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
jongio
left a comment
There was a problem hiding this comment.
Three items from prior rounds that still look unaddressed, plus one new observation.
The checksum validation and AZD_CONFIG_DIR fixes in the latest commit look good - those close out the round 4 feedback cleanly.
Still open:
- HasUpdatesAvailable can count uninstalled tools as having updates (details inline)
- No regression test for the VersionRegex no-match path in detectCommandBased
- Zero test coverage on both middleware files ( ool_first_run.go, ool_update_check.go)
- Guard HasUpdatesAvailable against uninstalled tools with cached versions - Add VersionRegexSetButNoMatch regression test for detector - Add test suites for tool_first_run and tool_update_check middleware Fixes review comments from jongio on Azure#7450 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Adds a new azd tool command group and supporting infrastructure (tool manifest, detection, installation/upgrade flows, and update-check caching) to help users manage common Azure development tooling directly from the Azure Developer CLI, including first-run and periodic update-notification middleware integration.
Changes:
- Introduces
azd toolcommand group (list,show,install,upgrade,check) with table/JSON outputs and interactive flows. - Adds
pkg/toolsubsystem: built-in tool registry (manifest), platform detection, tool detection, installer (package manager + direct download), and update-check caching. - Adds first-run and background update-check middleware, wiring into root command execution and updating usage/Fig spec snapshots.
Reviewed changes
Copilot reviewed 30 out of 30 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| cli/azd/pkg/tool/update_checker.go | Implements update-check gating, cache persistence, and notification timing logic. |
| cli/azd/pkg/tool/update_checker_test.go | Unit tests for update-check behavior and cache round-trips. |
| cli/azd/pkg/tool/platform.go | Detects OS and available package managers; selects install strategies. |
| cli/azd/pkg/tool/platform_test.go | Tests platform detection and strategy selection behavior. |
| cli/azd/pkg/tool/manifest.go | Defines tool types and the built-in tool registry (7 tools). |
| cli/azd/pkg/tool/manifest_test.go | Tests built-in manifest integrity and lookup helpers. |
| cli/azd/pkg/tool/manager.go | Orchestrates tool detection/install/upgrade and update-check delegation. |
| cli/azd/pkg/tool/manager_test.go | Tests manager orchestration including dependency resolution. |
| cli/azd/pkg/tool/installer.go | Implements installation/upgrade via package managers, shell commands, and direct downloads + checksum verification. |
| cli/azd/pkg/tool/installer_test.go | Tests installer behaviors across strategy types, including direct download/checksum paths. |
| cli/azd/pkg/tool/detector.go | Detects installed tools and extracts versions by category (CLI/extension/server/library). |
| cli/azd/pkg/tool/detector_test.go | Tests detection/version parsing across tool categories and error conditions. |
| cli/azd/internal/errors.go | Adds a sentinel error for tool upgrade failures. |
| cli/azd/internal/cmd/errors.go | Adds telemetry classification for the new sentinel error. |
| cli/azd/cmd/tool.go | Registers azd tool commands and implements CLI actions/UX/output. |
| cli/azd/cmd/root.go | Wires tool commands into root and adds tool first-run/update-check middleware for workflow commands. |
| cli/azd/cmd/middleware/tool_update_check.go | Adds cached update notification + background update check middleware. |
| cli/azd/cmd/middleware/tool_update_check_test.go | Tests update-check middleware gating/skip conditions. |
| cli/azd/cmd/middleware/tool_first_run.go | Adds first-run tool detection + optional install experience. |
| cli/azd/cmd/middleware/tool_first_run_test.go | Tests first-run skip conditions and non-blocking behavior. |
| cli/azd/cmd/container.go | Registers tool subsystem services in IoC (detector/installer/manager/update checker). |
| cli/azd/cmd/testdata/TestUsage-azd.snap | Updates usage snapshot to include the new tool group (and related help ordering changes). |
| cli/azd/cmd/testdata/TestUsage-azd-tool.snap | Adds usage snapshot for azd tool. |
| cli/azd/cmd/testdata/TestUsage-azd-tool-upgrade.snap | Adds usage snapshot for azd tool upgrade. |
| cli/azd/cmd/testdata/TestUsage-azd-tool-show.snap | Adds usage snapshot for azd tool show. |
| cli/azd/cmd/testdata/TestUsage-azd-tool-list.snap | Adds usage snapshot for azd tool list. |
| cli/azd/cmd/testdata/TestUsage-azd-tool-install.snap | Adds usage snapshot for azd tool install. |
| cli/azd/cmd/testdata/TestUsage-azd-tool-check.snap | Adds usage snapshot for azd tool check. |
| cli/azd/cmd/testdata/TestFigSpec.ts | Updates Fig completion spec to include tool and its subcommands/options. |
| cli/azd/.vscode/cspell.yaml | Adds spelling allowlist entries relevant to new tool/manifest content. |
vhvb1989
left a comment
There was a problem hiding this comment.
Review Feedback
Thanks for this feature! I have a few questions and suggestions across different areas. Posting them as a single review for easier tracking.
1. VS Code Extensions Visibility (cli/azd/cmd/tool.go — toolListAction.Run)
Consider filtering or visually distinguishing VS Code extensions when code is not on PATH. Currently azd tool list shows them as "Not Installed" (same as a CLI you just haven't installed yet), and azd tool install fails with a package-manager-unavailable error. Options: (1) hide extensions whose prerequisite isn't available, (2) show a distinct status like "Unavailable (requires VS Code)" instead of "Not Installed", or (3) add a --category filter flag so users can scope the list.
2. Azure MCP Server Install Strategy (cli/azd/pkg/tool/manifest.go — azureMCPServer())
Questions about the Azure MCP Server install strategy:
-
Global install vs npx: The MCP server is installed globally via
npm install -g @azure/mcp, but most MCP server configurations in VS Code usenpx(which doesn't require a global install). What's the benefit of a global install here? Is this the recommended approach from the Azure MCP team? -
No editor configuration: Installing the npm package globally doesn't actually configure it anywhere — the user still needs to manually wire it up in their VS Code MCP settings (or whatever agent tool they use). So
azd tool install azure-mcp-serverinstalls a package that can't be used until additional manual steps are taken. Shouldazd toolalso help with configuration, or at minimum surface a "next steps" message? -
Global vs local conflict: If a project already has
@azure/mcpas a local dev dependency (in the project'snode_modules), a global install could lead to version conflicts or confusion about which version is being used. Has this been considered?
3. First-Run Experience for Existing Users (cli/azd/cmd/middleware/tool_first_run.go — shouldSkip)
The first-run experience will trigger for all existing azd users after upgrading, not just new installations. The only gate is the absence of tool.firstRunCompleted in user config, which is a new key that no existing user will have.
Consider differentiating between truly new users and existing users who just upgraded. For example:
- Check if other user config keys already exist (indicating a returning user) and auto-set
tool.firstRunCompletedfor them - Check if
~/.azdpredates this version - Only show this for fresh azd installations where no prior config exists
For long-time users who already have their tools set up, being prompted with "Welcome to Azure Developer CLI! 🚀" and a tool check on their next azd up could feel disruptive rather than helpful.
Additionally:
- The opt-out mechanism is currently only via
AZD_SKIP_FIRST_RUNenv var, which is not discoverable. The welcome message should mention how to skip/disable this feature. - A user-config-based opt-out (e.g.,
azd config set tool.firstRun.enabled false) would be more natural and consistent with azd patterns than requiring a system environment variable. The env var is fine for CI, but for users who just don't want this feature,azd configis the right place.
4. Global Flags as No-Ops (cli/azd/cmd/tool.go)
Minor: azd tool accepts global flags like -e/--environment that it doesn't use. Is there any risk that passing -e myenv could trigger environment creation or resolution as a side effect before the tool action runs? Might be worth verifying that unused global flags are truly no-ops for commands that don't need them.
5. Telemetry for Product Insights (cli/azd/cmd/tool.go, cli/azd/pkg/tool/)
The generic telemetry middleware will track command-level usage and errors, but there's no custom telemetry to answer key product questions:
- Which tools are most/least installed across users? (detection results)
- Which tools are users choosing to install, and which do they skip?
- What install strategies are being used (brew vs winget vs npm vs manual)?
- What's the success/failure rate per tool and per strategy?
- In the first-run experience, do users accept or decline the tool check? Which tools do they select?
Adding span attributes for tool IDs, install strategies, and outcomes would make this feature's impact measurable. Without it, we'll know people ran azd tool install but not what they installed or whether it worked.
- Fix VS Code extension strategies to use PackageId instead of InstallCommand - Fix task callbacks to find correct result by tool ID instead of using results[0] - Wire ShouldShowNotification through Manager to gate update notifications Fixes review comments from jongio on Azure#7450 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add partial checksum config validation with clear error messages - Respect AZD_CONFIG_DIR for tool install directory via config.GetUserConfigDir() - Merge with main to resolve cspell.yaml conflict Fixes review comments from jongio on Azure#7450 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Guard HasUpdatesAvailable against uninstalled tools with cached versions - Add VersionRegexSetButNoMatch regression test for detector - Add test suites for tool_first_run and tool_update_check middleware Fixes review comments from jongio on Azure#7450 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Fix comment/code mismatch in tool_update_check middleware - Narrow shell operator detection from & to && to avoid URL false positives - Document AZD_SKIP_FIRST_RUN in environment-variables.md - Wire console handles into Confirm and MultiSelect prompts in first-run middleware Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Hide unused global flags (-e/--environment, -C/--cwd) from tool commands - Update first-run messaging from welcome framing to tool-check framing - Add skip/disable hint to first-run output Follow-up issues created: - Azure#7958 (VS Code extension visibility) - Azure#7959 (custom telemetry) - Azure#7960 (MCP Server install strategy) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Updated snapshot files to reflect new global flags (-C/--cwd and -e/--environment) added in origin/main.
Global flags like --environment and --cwd are safely ignored by tool commands (no side effects). Removing the hiding to stay consistent with other command groups (config, auth, extensions) that also ignore unused global flags without hiding them. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add retry with exponential backoff for post-install verification - Fix version regex patterns for pre-release versions (beta, preview) - Use real underlying tool IDs (e.g., GitHub.copilot, @azure/mcp, azure.ai.agents) - Switch azd extension detection to JSON parsing with --installed flag - Add spinner UX for all detection flows (list, check, show, install, upgrade) - Fix false positive detection for azd extensions Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Replace sync.Once with mutex in ensurePlatform to avoid caching transient errors - Only mark CLI tools as Installed on exec.ExitError, not arbitrary errors - Case-insensitive checksum comparison for direct downloads - Strip URL query parameters from download filenames - Cache compiled regexes in detector to avoid recompilation per call - Pass manifest to HasUpdatesAvailable instead of using package-level lookup - Batch install/upgrade calls to avoid N+1 dependency resolution - Extract shared runToolOperation helper to deduplicate install/upgrade flows - Add transitive dependency validation in buildInstallOrder - Document splitCommand quoted-argument limitation - Document isWorkflowCommand middleware scoping Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add ExitError guard to detectLibrary for consistent error handling - Replace sync.Once with sync.Mutex in getCacheFilePath to avoid caching transient errors - Include dependency results in runToolOperation TaskList and JSON output - Validate filename from download URL before filesystem operations - Fix gofmt alignment in installer struct - Break long line in buildInstallOrder error message - Add filesystems and redirections to cspell overrides Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…t ANSI contamination Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Azure Dev CLI Install InstructionsInstall scriptsMacOS/Linux
bash: pwsh: WindowsPowerShell install MSI install Standalone Binary
MSI
Documentationlearn.microsoft.com documentationtitle: Azure Developer CLI reference
|
|
Thanks @wbreza, nice surface, and the iteration with @jongio and @vhvb1989 tightened it up well. Three things I''d like to see in this PR before promoting to customers:
On the As I mentioned, we''ll hold on #7678 until after the On naming and help text ( Two small things on the user-facing surface:
On the package layout ( Heads up that Design doc and governance (soft ask): Could we add a short design doc under |
jongio
left a comment
There was a problem hiding this comment.
Re-reviewed against the rebased HEAD (d48d0c4). All prior findings confirmed addressed:
- PersistentPreRun override removed
- Shell pipe handling fixed via containsShellOperators routing
- Error wrapping consistent with fmt.Errorf %w
- Telemetry mapping added for ErrToolUpgradeFailed
Architecture is clean with proper separation (Manager/Detector/Installer/UpdateChecker), good DI via IoC, and middleware correctly gates on CI/non-interactive/no-prompt. Test coverage looks solid.
@wbreza Are we ready to release it in Beta/public-preview this week? I feel this needs more testing and dogfooding? @v-xuto can we prioritize and test the azd tool feature this week? |
Got it. We will begin testing |
|
@rajeshkamal5050 We completed the related testing for the azd tool feature and filed two issues: #8069, #8070. The following table shows the detailed test results: |
Summary
Adds
azd tool— a unified command group for discovering, installing, upgrading, and managing Azure development tools directly from the CLI. No more hunting for install docs or remembering package manager commands across platforms.Parent epic: #7676
What's Included
Core Command Group (Closes #7845)
azd tool list— shows all registered tools with install status, version, and categoryazd tool show <id>— displays detailed info for a specific tool (install strategies per-platform, website, etc.)azd tool check— checks installed tools for available updatesazd tool install [ids...]— installs tools by name, with--allfor all recommended tools and--dry-runfor previewazd tool upgrade [ids...]— upgrades installed tools, with--alland--dry-runsupportazd tool(bare) — interactive flow: shows installed/available tools, lets user select and install in one stepFirst-Run Experience (Closes #7846)
azd init,azd up,azd deploy, etc.)--no-prompt,AZD_SKIP_FIRST_RUN=true, or CI auto-detectionCI,TF_BUILD,GITHUB_ACTIONS) automatically bypass the promptUpdate Check Middleware (partial #7848)
Commands
azd toolazd tool listazd tool show <id>azd tool checkazd tool install [ids...]--all,--dry-run)azd tool upgrade [ids...]--all,--dry-run)All commands support
--output jsonfor scripting and CI consumption.Built-in Tools (7)
az)codeCLIcodeCLIcodeCLIArchitecture
CI / Non-Interactive
--no-prompt/AZD_NON_INTERACTIVE=truesuppresses all promptsCI,TF_BUILD,GITHUB_ACTIONS)--output json) for machine consumptioninstall/upgrade— no interactive selection neededTesting
pkg/tool/with 83.9% statement coverageExample Output
Issue References
Follow-Up Issues (deferred)
codeCLI is unavailable