@@ -8,13 +8,13 @@ import (
88 "errors"
99 "fmt"
1010 "io"
11+ "log"
1112
1213 "github.com/azure/azure-dev/cli/azd/cmd/actions"
1314 "github.com/azure/azure-dev/cli/azd/internal"
1415 "github.com/azure/azure-dev/cli/azd/internal/tracing"
1516 "github.com/azure/azure-dev/cli/azd/internal/tracing/fields"
1617 "github.com/azure/azure-dev/cli/azd/internal/tracing/resource"
17- "github.com/azure/azure-dev/cli/azd/pkg/alpha"
1818 "github.com/azure/azure-dev/cli/azd/pkg/config"
1919 "github.com/azure/azure-dev/cli/azd/pkg/exec"
2020 "github.com/azure/azure-dev/cli/azd/pkg/input"
@@ -57,20 +57,18 @@ func (f *updateFlags) Bind(local *pflag.FlagSet, global *internal.GlobalCommandO
5757
5858func newUpdateCmd () * cobra.Command {
5959 return & cobra.Command {
60- Use : "update" ,
61- Short : "Updates azd to the latest version." ,
62- Hidden : true ,
60+ Use : "update" ,
61+ Short : "Updates azd to the latest version." ,
6362 }
6463}
6564
6665type updateAction struct {
67- flags * updateFlags
68- console input.Console
69- formatter output.Formatter
70- writer io.Writer
71- configManager config.UserConfigManager
72- commandRunner exec.CommandRunner
73- alphaFeatureManager * alpha.FeatureManager
66+ flags * updateFlags
67+ console input.Console
68+ formatter output.Formatter
69+ writer io.Writer
70+ configManager config.UserConfigManager
71+ commandRunner exec.CommandRunner
7472}
7573
7674func newUpdateAction (
@@ -80,16 +78,14 @@ func newUpdateAction(
8078 writer io.Writer ,
8179 configManager config.UserConfigManager ,
8280 commandRunner exec.CommandRunner ,
83- alphaFeatureManager * alpha.FeatureManager ,
8481) actions.Action {
8582 return & updateAction {
86- flags : flags ,
87- console : console ,
88- formatter : formatter ,
89- writer : writer ,
90- configManager : configManager ,
91- commandRunner : commandRunner ,
92- alphaFeatureManager : alphaFeatureManager ,
83+ flags : flags ,
84+ console : console ,
85+ formatter : formatter ,
86+ writer : writer ,
87+ configManager : configManager ,
88+ commandRunner : commandRunner ,
9389 }
9490}
9591
@@ -102,27 +98,6 @@ func (a *updateAction) Run(ctx context.Context) (*actions.ActionResult, error) {
10298 }
10399 }
104100
105- // Auto-enable the alpha feature if not already enabled.
106- // The user's intent is clear by running `azd update` directly.
107- if ! a .alphaFeatureManager .IsEnabled (update .FeatureUpdate ) {
108- userCfg , err := a .configManager .Load ()
109- if err != nil {
110- userCfg = config .NewEmptyConfig ()
111- }
112-
113- if err := userCfg .Set (fmt .Sprintf ("alpha.%s" , update .FeatureUpdate ), "on" ); err != nil {
114- return nil , fmt .Errorf ("failed to enable update feature: %w" , err )
115- }
116-
117- if err := a .configManager .Save (userCfg ); err != nil {
118- return nil , fmt .Errorf ("failed to save config: %w" , err )
119- }
120-
121- a .console .MessageUxItem (ctx , & ux.MessageTitle {
122- Title : "azd update is in alpha. Channel-aware version checks are now enabled.\n " ,
123- })
124- }
125-
126101 // Track install method for telemetry
127102 installedBy := installer .InstalledBy ()
128103 tracing .SetUsageAttributes (
@@ -134,13 +109,31 @@ func (a *updateAction) Run(ctx context.Context) (*actions.ActionResult, error) {
134109 userConfig = config .NewEmptyConfig ()
135110 }
136111
112+ // Show notice on first use
113+ if ! update .HasUpdateConfig (userConfig ) {
114+ a .console .MessageUxItem (ctx , & ux.MessageTitle {
115+ Title : fmt .Sprintf (
116+ "azd update is currently in Beta. " +
117+ "To learn more about feature stages, visit %s." ,
118+ output .WithLinkFormat ("https://aka.ms/azd-feature-stages" )),
119+ })
120+
121+ // Write a default channel so HasUpdateConfig returns true next time.
122+ if err := update .SaveChannel (userConfig , update .LoadUpdateConfig (userConfig ).Channel ); err != nil {
123+ log .Printf ("warning: failed to persist default update channel: %v" , err )
124+ } else if err := a .configManager .Save (userConfig ); err != nil {
125+ log .Printf ("warning: failed to save config after setting default channel: %v" , err )
126+ }
127+ }
128+
137129 // Determine current channel BEFORE persisting any flags
138130 currentCfg := update .LoadUpdateConfig (userConfig )
139131 switchingChannels := a .flags .channel != "" && update .Channel (a .flags .channel ) != currentCfg .Channel
140132
141- // Persist non-channel config flags immediately (auto-update, check-interval)
133+ // Persist non-channel config flags immediately (check-interval)
142134 configChanged , err := a .persistNonChannelFlags (userConfig )
143135 if err != nil {
136+ tracing .SetUsageAttributes (fields .UpdateResult .String (update .CodeConfigFailed ))
144137 return nil , err
145138 }
146139
@@ -149,13 +142,15 @@ func (a *updateAction) Run(ctx context.Context) (*actions.ActionResult, error) {
149142 if switchingChannels {
150143 newChannel , err := update .ParseChannel (a .flags .channel )
151144 if err != nil {
145+ tracing .SetUsageAttributes (fields .UpdateResult .String (update .CodeInvalidInput ))
152146 return nil , err
153147 }
154148 _ = update .SaveChannel (userConfig , newChannel )
155149 configChanged = true
156150 } else if a .flags .channel != "" {
157151 // Same channel explicitly set — just persist it
158152 if err := update .SaveChannel (userConfig , update .Channel (a .flags .channel )); err != nil {
153+ tracing .SetUsageAttributes (fields .UpdateResult .String (update .CodeConfigFailed ))
159154 return nil , err
160155 }
161156 configChanged = true
@@ -169,6 +164,22 @@ func (a *updateAction) Run(ctx context.Context) (*actions.ActionResult, error) {
169164 fields .UpdateFromVersion .String (internal .VersionInfo ().Version .String ()),
170165 )
171166
167+ // If only config flags were set (no channel change, no update needed), just confirm
168+ if a .onlyConfigFlagsSet () {
169+ if configChanged {
170+ if err := a .configManager .Save (userConfig ); err != nil {
171+ tracing .SetUsageAttributes (fields .UpdateResult .String (update .CodeConfigFailed ))
172+ return nil , fmt .Errorf ("failed to save config: %w" , err )
173+ }
174+ }
175+ tracing .SetUsageAttributes (fields .UpdateResult .String (update .CodeSuccess ))
176+ return & actions.ActionResult {
177+ Message : & actions.ResultMessage {
178+ Header : "Update preferences saved." ,
179+ },
180+ }, nil
181+ }
182+
172183 mgr := update .NewManager (a .commandRunner , nil )
173184
174185 // Block update in CI/CD environments
@@ -201,21 +212,6 @@ func (a *updateAction) Run(ctx context.Context) (*actions.ActionResult, error) {
201212 }
202213 }
203214
204- // If only config flags were set (no channel change, no update needed), just confirm
205- if a .onlyConfigFlagsSet () {
206- if configChanged {
207- if err := a .configManager .Save (userConfig ); err != nil {
208- return nil , fmt .Errorf ("failed to save config: %w" , err )
209- }
210- }
211- tracing .SetUsageAttributes (fields .UpdateResult .String (update .CodeSuccess ))
212- return & actions.ActionResult {
213- Message : & actions.ResultMessage {
214- Header : "Update preferences saved." ,
215- },
216- }, nil
217- }
218-
219215 // Check for updates (always fresh for manual invocation)
220216 a .console .ShowSpinner (ctx , "Checking for updates..." , input .Step )
221217 versionInfo , err := mgr .CheckForUpdate (ctx , cfg , true )
@@ -273,6 +269,7 @@ func (a *updateAction) Run(ctx context.Context) (*actions.ActionResult, error) {
273269 // Now persist all config changes (including channel) after confirmation
274270 if configChanged {
275271 if err := a .configManager .Save (userConfig ); err != nil {
272+ tracing .SetUsageAttributes (fields .UpdateResult .String (update .CodeConfigFailed ))
276273 return nil , fmt .Errorf ("failed to save config: %w" , err )
277274 }
278275 }
0 commit comments