Add direct registry source support to azd extension commands#8792
Add direct registry source support to azd extension commands#8792Copilot wants to merge 7 commits into
azd extension commands#8792Conversation
azd extension install to register a source directly from a registry URL/path
2196197 to
187b55c
Compare
azd extension install to register a source directly from a registry URL/pathazd extension commands
There was a problem hiding this comment.
Pull request overview
This PR expands azd extension source resolution so -s/--source can be either a registered source name or a direct registry location (URL/file path), enabling install/upgrade to register-on-demand while keeping list/show side-effect free.
Changes:
- Adds support for temporary, non-persisted source filtering via
FilterOptions.SourceConfigin the extensions manager. - Updates
azd extension list/show/install/upgradeto accept direct locations for--source, registering locations for mutating commands and querying read-only for inspection commands. - Updates help text/docs/completions and adds targeted tests for direct-location source behavior.
Show a summary per file
| File | Description |
|---|---|
| cli/azd/pkg/extensions/manager.go | Adds SourceConfig to filter options and enables creating sources from a one-off config for list/show scenarios. |
| cli/azd/cmd/extension.go | Implements direct-location --source handling (register-on-demand for install/upgrade; read-only for list/show) and adds -s shorthand for list. |
| cli/azd/docs/extensions/extension-framework.md | Documents new --source behavior across extension commands, including read-only vs persisted semantics. |
| cli/azd/cmd/testdata/TestUsage-azd-extension-upgrade.snap | Updates upgrade help snapshot for the expanded --source meaning. |
| cli/azd/cmd/testdata/TestUsage-azd-extension-show.snap | Updates show help snapshot for the expanded --source meaning. |
| cli/azd/cmd/testdata/TestUsage-azd-extension-list.snap | Updates list help snapshot and reflects the new -s shorthand. |
| cli/azd/cmd/testdata/TestUsage-azd-extension-install.snap | Updates install help snapshot to describe registering from a location. |
| cli/azd/cmd/testdata/TestFigSpec.ts | Updates Fig completion spec descriptions and adds -s for extension list --source. |
| cli/azd/cmd/extension_upgrade_test.go | Adjusts upgrade tests to pass/construct a SourceManager where needed. |
| cli/azd/cmd/extension_source_location_test.go | Adds new tests for direct URL/file location behavior in list/show/upgrade. |
| cli/azd/cmd/extension_install_source_test.go | Adds tests covering source-kind inference and install-time registration-from-location behavior. |
| cli/azd/cmd/constructors_coverage3_test.go | Updates constructor coverage tests for the new sourceManager parameter. |
Copilot's findings
- Files reviewed: 12/12 changed files
- Comments generated: 2
jongio
left a comment
There was a problem hiding this comment.
First review of this PR. The approach of overloading --source to accept both registered names and direct locations is well-designed, with proper separation between read-only commands (list/show query without persisting) and mutating commands (install/upgrade register first). Test coverage is thorough, including edge cases for name validation, location reuse, and no-prompt blocking.
A few observations beyond what's already been raised:
normalizeSourceKeyincmd/extension.goduplicates the unexportednormalizeKeyinpkg/extensions/source_manager.go. If the normalization logic evolves in one place, the other won't track. Consider exportingNormalizeKeyfrom the extensions package to eliminate this.- For the
showcommand, when--sourceis a non-existent name that also doesn't look like a location, the user gets "failed to find extension" rather than "extension source not found" (whichlistproduces). Minor inconsistency in error messaging. inferSourceKindclassifies any string ending in.jsonas a file path. A typo likemy-source.json(intendingmy-source) produces a confusing "failed to validate extension source" error instead of "source not found". Acceptable trade-off for the heuristic, but worth noting.
📋 Milestone: June 2026This work is tracked for June 2026. The team will review it soon! |
jongio
left a comment
There was a problem hiding this comment.
Second review pass confirms no blocking issues. The design cleanly separates read-only commands (list/show query a temporary SourceConfig without persisting) from mutating commands (install/upgrade register the source first), and --no-prompt correctly rejects registration with guidance.
My earlier observations (normalizeSourceKey duplication, .json heuristic edge case, source persisting before extension lookup in upgrade) are informational suggestions, not blockers. The test coverage is thorough and addresses the key scenarios: name reuse, blank/invalid name retry loops, URL confirmation flow, relative-path normalization, and no-prompt rejection.
One additional note on the SourceConfig path in manager.go: the shallow copy + Source clearing pattern is correct for preventing the name-based filter from double-filtering when a raw SourceConfig is provided.
Azure Dev CLI Install InstructionsInstall scriptsMacOS/Linux
bash: pwsh: WindowsPowerShell install MSI install Standalone Binary
MSI
Documentationlearn.microsoft.com documentationtitle: Azure Developer CLI reference
|
…ry location Co-authored-by: JeffreyCA <9157833+JeffreyCA@users.noreply.github.com>
f4f5e13 to
e5a867c
Compare
jongio
left a comment
There was a problem hiding this comment.
Incremental review after new commits (f4f5e13 -> e5a867c).
My earlier observations have been addressed:
ormalizeSourceKey duplication is eliminated. NormalizeSourceKey is now exported from pkg/extensions/source_manager.go and used directly in cmd/extension.go.
- The .json heuristic edge case is resolved. inferSourceKind now uses os.Stat to check if the file exists rather than checking the extension. A typo like my-source.json that doesn't exist on disk correctly falls through to "source not found" rather than producing a confusing validation error.
The SourceConfig field added to FilterOptions cleanly separates the read-only (list/show) path from the mutating (install/upgrade) path. The locationsEqual helper handles URL case-insensitivity and platform-aware file comparison correctly. Test coverage for the new code is thorough, covering name validation retries, location reuse, no-prompt blocking, and relative path normalization.
No new blocking issues.
jongio
left a comment
There was a problem hiding this comment.
Incremental review (e5a867c -> f94aba9).
The resolveRegisteredSourceName extraction is clean. It consolidates the "try raw name, then try normalized" pattern into one place, adds proper error discrimination (non-ErrSourceNotFound errors no longer masquerade as "source not found"), and the test coverage for normalized lookups is solid.
One low-severity observation below about a redundant lookup in the read-only filter path.
hemarina
left a comment
There was a problem hiding this comment.
Nice extension to the source handling — accepting either a registered name or a direct location removes a real friction point. The read-only vs. mutating split is clean, help text and usage snapshots are updated, and the reuse-by-location / --no-prompt ordering is correct (already-registered locations are reused before the no-prompt guard, so they still resolve under --no-prompt).
The substantive items I found are already covered in the existing threads (silent empty results for a bad read-only location; Windows case-sensitive file dedupe in locationsEqual; normalizeSourceKey duplicating normalizeKey; the .json typo heuristic in inferSourceKind; and the upgrade registering the source before validating the extension). +1 especially on the Windows-casing dedupe — that one is the most likely to bite in practice.
One small additional note in the same vein as the casing issue:
locationsEqualdoesn't normalize URLs. URL locations are compared withEqualFoldonly, sohttps://host/reg.jsonvshttps://host/reg.json/(trailing slash) would be treated as different locations and register a duplicate source. Optional, but worth folding into the same normalization fix as the file-path casing comment.
Approving — none of these are blocking.
|
Thanks Jeffrey, this is a nice change. Collapsing "add source, then install" into a single A few thoughts from a PM lens, none blocking:
Approving the direction. Items 1–3 are worth landing before release; 4–6 are follow-ups. |
|
Pushed a commit addressing the latest round of review feedback: Unknown Unsupported registry schema gives actionable guidance — when a direct Deduplicated source-name resolution — refactored the read-only filter path into a single Consolidated the unsupported-schema error construction — the "upgrade azd" Telemetry — added the
|
jongio
left a comment
There was a problem hiding this comment.
Incremental review of Address feedback (f94aba9 -> 69befef, 1 commit, 10 files).
The resolveSourceFilter consolidation eliminates the redundant lookup I flagged in my prior review. The unified sourceFilterResolution struct cleanly separates the three outcomes (registered name, inferred location, unrecognized value) and the callers are simpler for it.
Verified in this pass:
normalizeUrlLocationcorrectly lowercases scheme/host and trims trailing path slashes without breaking percent-encoded paths or query strings. The test covers the trailing-slash dedup case.sourceArgKindcaptures the telemetry attribute before any resolution mutatesflags.source, so the metric reflects user intent regardless of whether the command succeeds.NewUnsupportedRegistrySchemaErrordeduplicates theErrorWithSuggestionwrapping from two call sites in manager.go into one. The new call site inregisterSourceFromLocationmeans users hitting an unsupported schema during interactive install now get the upgrade suggestion (previously they got a generic "failed to validate" message).shownow returnsErrSourceNotFoundfor unknown registered names, matching thelistbehavior I noted earlier.
No new issues.
Resolves #8581
This PR expands
azd extensionsource handling so-s/--sourcecan accept either a registered source name or a registry location. This removes the extraazd extension source addstep for interactive install and upgrade flows while preserving the existing registered-source behavior for scripts and users who already manage sources explicitly.Remote source:
Local source:
This also makes it possible to query a source's extensions with
azd ext listwithout having to add it:For
installandupgrade, direct locations are inferred as URL or file sources, validated before being saved, and registered as persisted sources before extension resolution continues.If the same location is already registered under any name,
azdreuses that source instead of prompting again or creating a duplicate. File locations are normalized to absolute paths so laterupgrade,list, andshowcommands resolve the same registry consistently from different working directories, and--no-promptcontinues to reject new source registration with guidance to add the source explicitly first.For
listandshow, direct URL and file locations are queried read-only without being saved to user configuration. That keeps inspection commands side-effect free, allows them to work in no-prompt scenarios, and makes the returned source clear by surfacing the queried location in output for unregistered sources. The change also adds-sshorthand forazd extension list --source.