Conversation
|
Thanks for sharing the work here, @wbreza! Just sharing some high-level UX observations and wonders for now while I find time to parse through more details. Sharing some observations:
Sharing some wonders I have:
Overall, I think this is a really great start that will help us guide us towards productive conversations as we chat more about this today, so thanks once again for sharing. |
|
Do you envision different types of extensions, such as root-level command extensions, command groups with multiple commands, or extensions added to existing commands (e.g., adding an extension to 'azd up')? Additionally, can I add an extension to extend infrastructure providers? |
Yes, ultimately I envision different types of extensions. The first set of extensions would allow extension authors to create new commands that can be executed through Over time I can also see extensions providing additional functionality to customize experience within Regarding |
3a6ba44 to
161ee79
Compare
ellismg
left a comment
There was a problem hiding this comment.
Partial review - the public key stuff scares me the most - I think if we want to do this we need to engage with the security team and talk with them about how we do this - I think they may want changes to how we transport the signature so we don't, for example, have to change the data we download in order to validate the signature.
Would love to better understand the treat model for that stuff (I'm onboard with doing the SHA validation of downloads, as we do for other tools).
Like how clean and small this is - great first step on the new path forward.
| return nil, fmt.Errorf("failed to resolve data: %w", err) | ||
| } | ||
|
|
||
| if err := c.Set(value); err != nil { |
There was a problem hiding this comment.
We'll call this (and write the data to the cache) even when AZD_NO_CACHE is set. Is that expected?
There was a problem hiding this comment.
I have no strong opinion if we should just exit early here or not if the flag is set.The AZD_NO_CACHE flag was primarily added to force get on the underlying data store vs whether the file should be written or not since the primary purpose of this component is to cache outputs.
|
|
||
| var paragraph []string | ||
| for _, title := range groups { | ||
| groupCommands := commandGroups[string(title)] |
There was a problem hiding this comment.
NIT: I'd personally either rewrite such that we don't introduce the groupCommands local here (by just inlining this into the len call) OR update L458 to now use groupCommands.
|
|
||
| // Conditionally register the 'extension' commands if the feature is enabled | ||
| var alphaFeatureManager *alpha.FeatureManager | ||
| if err := rootContainer.Resolve(&alphaFeatureManager); err == nil { |
There was a problem hiding this comment.
My guess is that if we fail to resolve the *alpha.FeatureManager here, stuff is in a bad way and we should just panic instead of just not running this code, but maybe I'm wrong? We are okay in panicing elsewhere in this method for cases where we can't resolve things we expect, is there a reason this is different?
cli/azd/pkg/extensions/manager.go
Outdated
|
|
||
| // Verify verifies the given data and its Base64-encoded signature | ||
| func verifySignature(data []byte, signature string) error { | ||
| publicKey, err := loadPublicKey(resources.PublicKey) |
There was a problem hiding this comment.
If we end up keeping this public key stuff around to do signature verification - I think we should arrange things such that we do as much of this loading and praising at package init time and just panic on errors, then so that verifySignature can never fail due to an error reading the key we're going to use for verification. In theory if the call to loadPublicKey(resources.PublicKey) fails, it means there's a problem with our embedded key and I'd prefer if that failure was loud and prompt.
|
|
||
| forceColor := !color.NoColor | ||
| if forceColor { | ||
| allEnv = append(allEnv, "FORCE_COLOR=1") |
There was a problem hiding this comment.
Is this some new convention we're building?
|
|
||
| extensionPath := filepath.Join(homeDir, extension.Path) | ||
|
|
||
| _, err = os.Stat(extensionPath) |
There was a problem hiding this comment.
Would it be possible to move this logic in to GetInstalled? Could the extension returned have a full path (including the home directory) and do the Stat to ensure the binary is there?
bd41553 to
5e7f02a
Compare
bb68445 to
9db9aad
Compare
Azure Dev CLI Install InstructionsInstall scriptsMacOS/Linux
bash: pwsh: WindowsPowerShell install MSI install Standalone Binary
MSI
Documentationlearn.microsoft.com documentationtitle: Azure Developer CLI reference
|
| @@ -0,0 +1,832 @@ | |||
| package cmd | |||
There was a problem hiding this comment.
| package cmd | |
| // Copyright (c) Microsoft Corporation. All rights reserved. | |
| // Licensed under the MIT License. | |
| package cmd |
| } | ||
|
|
||
| // NewFileCache creates a new file cache. | ||
| func NewFileCache[T any](cacheFilePath string, cacheDuration time.Duration, resolver CacheResolver[T]) *FileCache[T] { |
There was a problem hiding this comment.
How is this FileCache used for the extension commands? Should it be a different PR? What's the relation? I only see it used from tests
|
Closing and using #4766 instead. |
This PR adds the
extensioncommand group intoazdand enables the execution of installed extensions.Important
The extensions feature is behind a feature flag and must be enabled by running:
azd config set alpha.extensions onCommands
azd extension list- List available extensions in the registryazd extension show <name>- Show details of a specified extensionazd extension install <name>- Install an extensionazd extension uninstall <name>- Uninstall an extensionazd extension upgrade <name>- Upgrades an extensionRegistry
The registry is loaded from an aka URL @ https://aka.ms/azd/extensions/registry
Examples
azd extension listazd extension show aiazd extension install aiazd -hExtensions show up in standard azd help commands.

azd test -hExtension help shells into extension executable

azd test --unitInvoke extensions through azd command interface

azd extension uninstall ai