feat(extensions): redesign auto-install UX - #9474
Conversation
|
Azure Pipelines: Successfully started running 1 pipeline(s). 21 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
ec06e61 to
79b7251
Compare
ec06e61 to
f5be41b
Compare
f5be41b to
b286887
Compare
There was a problem hiding this comment.
Pull request overview
Redesigns extension auto-installation to present and resolve the complete installation plan before modifying the environment.
Changes:
- Adds coordinated interactive, no-prompt, and CI installation planning.
- Preserves source candidates and performs source-aware dependency resolution.
- Adds consolidated UX, progress reporting, documentation, and tests.
Show a summary per file
| File | Description |
|---|---|
cli/azd/pkg/input/console.go |
Adds select filtering control. |
cli/azd/pkg/input/console_ux.go |
Passes filtering options to select UX. |
cli/azd/pkg/input/console_ux_test.go |
Tests filtering propagation. |
cli/azd/docs/extensions/extension-resolution-and-versioning.md |
Documents the redesigned behavior. |
cli/azd/cmd/project_extension_auto_install.go |
Aggregates requirements and source candidates. |
cli/azd/cmd/extension.go |
Generalizes dependency display support. |
cli/azd/cmd/auto_install.go |
Integrates coordinated installation across command paths. |
cli/azd/cmd/auto_install_ux.go |
Implements installation planning and UX. |
cli/azd/cmd/auto_install_ux_test.go |
Tests planning, prompts, guidance, and progress. |
cli/azd/cmd/auto_install_test.go |
Expands resolution and integration tests. |
Review details
Tip
Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
- Files reviewed: 10/10 changed files
- Comments generated: 3
- Review effort level: Balanced
There was a problem hiding this comment.
Review details
Suppressed comments (3)
cli/azd/cmd/auto_install_ux.go:162
- [azd-code-reviewer] This ordering only follows dependencies that are themselves direct selections. If selected
a@azddepends on unselectedb@azd, which depends on selectedc@local,cis not visited here; installingafirst installsc@azd, and the laterc@localselection is skipped as already installed. Traverse through unselected dependency metadata when finding selected descendants (or make installation consume the complete source plan) so transitive dependencies also honor the confirmed source.
for _, dependency := range version.Dependencies {
if dependencySelection, selected := byID[strings.ToLower(dependency.Id)]; selected {
cli/azd/cmd/auto_install_ux.go:190
- [azd-code-reviewer]
requirement.extensionis the alphabetically first source candidate, not necessarily the candidate that will be installed. A custom source named beforeazdcan therefore supply the displayed name/description while the default choice installs the official candidate with different metadata. Use metadata from the recommended candidate when one exists, and avoid presenting a shared description when candidate metadata differs.
extension := requirement.extension
cli/azd/cmd/auto_install_ux.go:83
- [azd-code-reviewer] This branch is also reached when no-prompt mode was enabled automatically by coding-agent detection (
ParseGlobalFlagssetsNoPromptfor detected agents), not only for an explicit local--no-promptas the PR description and extension-resolution documentation state. A detected local agent will therefore install unambiguous extensions without explicit consent. Either retain whether no-prompt was user-requested for this decision or document the automatic agent behavior.
if console.IsNoPromptMode() {
- Files reviewed: 10/10 changed files
- Comments generated: 0 new
- Review effort level: Balanced
e514fce to
ff0f08f
Compare
|
In the current PR, when installing extensions from different sources, the progress do not show the source info, like "from azd" or "from local". Change it to the image below where it indicates the source for each extension. Other than that, all good. Thanks JeffreyCA ! |
ff0f08f to
278cffb
Compare
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7e3d56dc-15a3-451f-af46-25939d0e3fd3
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7e3d56dc-15a3-451f-af46-25939d0e3fd3
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7e3d56dc-15a3-451f-af46-25939d0e3fd3
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7e3d56dc-15a3-451f-af46-25939d0e3fd3
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7e3d56dc-15a3-451f-af46-25939d0e3fd3
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7e3d56dc-15a3-451f-af46-25939d0e3fd3
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7e3d56dc-15a3-451f-af46-25939d0e3fd3
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7e3d56dc-15a3-451f-af46-25939d0e3fd3
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d743ee8d-6f2c-4244-9ca9-b64fb20643d1
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d743ee8d-6f2c-4244-9ca9-b64fb20643d1
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d743ee8d-6f2c-4244-9ca9-b64fb20643d1
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d743ee8d-6f2c-4244-9ca9-b64fb20643d1
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7e3d56dc-15a3-451f-af46-25939d0e3fd3
278cffb to
e3f3407
Compare
📋 Prioritization NoteThanks for the contribution! The linked issue isn't in the current milestone yet. |
|
Azure Pipelines: Successfully started running 1 pipeline(s). 20 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
There was a problem hiding this comment.
Review details
Suppressed comments (2)
cli/azd/cmd/auto_install_ux.go:178
- The dependency ordering still misses transitive selected dependencies. If selected
parent@azddepends on unselectedmiddle@azd, which depends on selectedchild@local, this loop does not visitchild; installingparentfirst makes the manager recursively installchild@azd, and the later plannedchild@localis skipped as already installed. Traverse same-source dependency metadata transitively (or otherwise order every selected ID found anywhere in the resolved graph) so the confirmed source plan is honored.
for _, dependency := range version.Dependencies {
if dependencySelection, selected := byID[strings.ToLower(dependency.Id)]; selected {
if err := visit(dependencySelection); err != nil {
return err
}
cli/azd/cmd/auto_install_ux.go:203
- This description can come from a different source than the extension the user ultimately installs.
chooseLogicalExtensionCandidatessorts candidates by source and stores the first one inrequirement.extension, while each registry supplies independentDisplayNameandDescriptionvalues and the selected/recommended candidate may be another entry. The consent screen can therefore describe source A and then install source B; render source-specific metadata or add a final confirmation based on the selected candidate.
extension := requirement.extension
- Files reviewed: 12/12 changed files
- Comments generated: 0 new
- Review effort level: Balanced
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7e3d56dc-15a3-451f-af46-25939d0e3fd3
There was a problem hiding this comment.
Review details
Suppressed comments (2)
cli/azd/cmd/auto_install_ux.go:179
- azd-code-reviewer: This ordering only follows dependencies that are themselves top-level selections. If selected
Adepends on unselectedC, which then depends on selectedB, visitingAnever visitsB; installingAfirst lets the manager install both intermediates fromA's source, and the laterBselection is skipped as already installed. Walk the full dependency graph through intermediary metadata, or pass the complete source plan into the manager, so transitive selected dependencies are installed first.
for _, dependency := range version.Dependencies {
if dependencySelection, selected := byID[strings.ToLower(dependency.Id)]; selected {
if err := visit(dependencySelection); err != nil {
return err
}
}
cli/azd/cmd/auto_install_ux.go:203
- azd-code-reviewer:
requirement.extensionis only the first candidate ordered by configured source name, while recommendation and installation may choose a different candidate. Independent registries can publish different display names and descriptions for the same ID, so this can describe a custom-source binary while asking the user to install the official candidate. Render source-specific metadata, or otherwise ensure the displayed details come from the candidate being recommended/selected rather than treating arbitrary candidate metadata as shared.
if len(requirements) == 1 {
requirement := requirements[0]
extension := requirement.extension
- Files reviewed: 16/16 changed files
- Comments generated: 0 new
- Review effort level: Balanced
Azure Dev CLI Install InstructionsInstall scriptsMacOS/Linux
bash: pwsh: WindowsPowerShell install MSI install Standalone Binary
MSI
Documentationlearn.microsoft.com documentationtitle: Azure Developer CLI reference
|


Fixes #9302
Summary
This PR redesigns extension auto-install so
azddiscovers the complete requirement set before asking for consent, explains why each extension is required, and resolves a complete source-aware install plan before changing the user's environment.It reuses the privacy-safe source categories introduced in #9452 to recognize the official azd registry without trusting a user-controlled source name.
azd up- single sourceazd up- multiple sourcesIssue
The previous flow prompted while requirements were still being discovered, repeated warning and confirmation output for each extension, and mixed alternative-extension selection with source selection. Multi-extension projects could not review the complete install plan first, while CI and ambiguous no-prompt failures provided limited manual recovery guidance.
Interactive experience
The new flow presents the complete requirement set before installation and resolves every source choice before the first install begins.
Extension required by azure.yamlheading with its ID, source or sources, and description.(+N more)suffix.azdrecommends it using the source's location-derived category while continuing to display the configured source name.azdprompts only for ambiguous sources.Canceled: required extension isn't installed., stops before any install, and exits successfully.Behaviour at a glance
(+N more)countfrom <source>text to each completed top-level install task--no-prompt, all requirements unambiguous--no-prompt, any source ambiguityazd extension installcommands without pinning versionsSource and dependency resolution
Source duplicates are retained as candidates instead of prompting during discovery. Alternative extension IDs are still selected with the existing prompt, while the selected logical extension's source candidates flow into one install-plan coordinator shared by project commands, unknown commands, partial namespaces, and the unsupported-host fallback.
Dependency suppression remains conservative before source selection:
azdassumes a dependency will satisfy a provider only when every eligible source candidate supplies it. After the user selects sources, the plan orders explicitly selected dependencies before their parents so installation honours those source choices instead of letting a parent implicitly install the dependency from another registry.Newly installed dependencies are rendered as flat completed tasks beneath the parent install. The complete top-level plan is selected before installation starts, so cancellation or a source-selection error cannot leave a partially installed set. A later install failure can still leave earlier successful installs in place, matching existing extension installation semantics.
Progress UX consistency
Extension install, update, uninstall, init-time install, and auto-install tasks now use the same concise task wording without a redundant
extensionsuffix. Extension IDs remain highlighted and version suffixes are muted consistently, includingazd extension uninstall --all.Completed auto-install tasks identify their configured source only when the resolved plan spans at least two distinct sources, keeping single-source output concise while making mixed-source results clear.
Scope
This PR includes the refactoring needed to remove duplicated auto-install branching and make the decision model directly testable. It does not redesign alternative-extension selection or add new auto-install telemetry. A small follow-up telemetry change can be considered after the UX stabilizes, using only fixed categories, counts, trigger, and terminal outcome while relying on existing
ext.installspans for completed operations.Testing
Covered single and multiple extension flows, official registry aliases, long source-list summaries, alternate and per-extension source selection, the conditional remaining-source shortcut, decline and cancel outcomes, local no-prompt behaviour, multi-extension CI guidance, source-specific manual commands, mixed-source task labels, dependency display and source-plan ordering, install success and failure progress, provider resolution, partial namespaces, and existing command execution integration. Also validated the affected command package with build, unit tests, Go modernization, lint, spelling, and whitespace checks.