From 1f9c90c8021579a3d6f0e82138682406bc39aec8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 30 Jan 2026 09:11:01 +0000 Subject: [PATCH 1/7] Initial plan From 6deec8cdf21ea50e3ae6bdaac77d6a25dd51bb09 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 30 Jan 2026 09:15:40 +0000 Subject: [PATCH 2/7] Add update notifications for daily/continuous builds Co-authored-by: Petermarcu <8228359+Petermarcu@users.noreply.github.com> --- cli/azd/docs/environment-variables.md | 1 + cli/azd/main.go | 15 +++++++++++++- cli/azd/resources/config_options.yaml | 5 +++++ eng/pipelines/templates/stages/publish.yml | 24 ++++++++++++++++++++++ 4 files changed, 44 insertions(+), 1 deletion(-) diff --git a/cli/azd/docs/environment-variables.md b/cli/azd/docs/environment-variables.md index e96152a41be..6af120064ba 100644 --- a/cli/azd/docs/environment-variables.md +++ b/cli/azd/docs/environment-variables.md @@ -13,6 +13,7 @@ Environment variables that can be used to configure `azd` behavior, usually set - `AZD_FORCE_TTY`: If true, forces `azd` to write terminal-style output. - `AZD_IN_CLOUDSHELL`: If true, `azd` runs with Azure Cloud Shell specific behavior. - `AZD_SKIP_UPDATE_CHECK`: If true, skips the out-of-date update check output that is typically printed at the end of the command. +- `AZD_UPDATE_CHANNEL`: Choose which update channel to check for new versions. Set to `daily` to receive notifications for daily/continuous builds, or `stable`/`latest` (default) for official releases only. For tools that are auto-acquired by `azd`, you are able to configure the following environment variables to use a different version of the tool installed on the machine: diff --git a/cli/azd/main.go b/cli/azd/main.go index a3ea8f23542..0b111014c2b 100644 --- a/cli/azd/main.go +++ b/cli/azd/main.go @@ -251,7 +251,20 @@ func fetchLatestVersion(version chan<- semver.Version) { // If we don't have a cached version we can use, fetch one (and cache it) if cachedLatestVersion == nil { log.Print("fetching latest version information for update check") - req, err := http.NewRequest(http.MethodGet, "https://aka.ms/azure-dev/versions/cli/latest", nil) + + // Determine which update channel to check (stable or daily) + updateChannel := "latest" // Default to stable channel + if channel, has := os.LookupEnv("AZD_UPDATE_CHANNEL"); has { + if channel == "daily" { + updateChannel = "daily" + log.Print("using daily update channel") + } else if channel != "stable" && channel != "latest" { + log.Printf("unknown update channel '%s', using stable channel", channel) + } + } + + versionUrl := fmt.Sprintf("https://aka.ms/azure-dev/versions/cli/%s", updateChannel) + req, err := http.NewRequest(http.MethodGet, versionUrl, nil) if err != nil { log.Printf("failed to create request object: %v, skipping update check", err) } diff --git a/cli/azd/resources/config_options.yaml b/cli/azd/resources/config_options.yaml index b91e5dfc99d..0c97db433b0 100644 --- a/cli/azd/resources/config_options.yaml +++ b/cli/azd/resources/config_options.yaml @@ -84,3 +84,8 @@ description: "Override the default configuration directory location." type: envvar example: "/path/to/config" +- key: (env) AZD_UPDATE_CHANNEL + description: "Choose which update channel to check for new versions. Set to 'daily' to receive notifications for daily/continuous builds, or 'stable' (default) for official releases only." + type: envvar + allowedValues: ["stable", "latest", "daily"] + example: "daily" diff --git a/eng/pipelines/templates/stages/publish.yml b/eng/pipelines/templates/stages/publish.yml index e9c8e57984c..a019d3eaf6e 100644 --- a/eng/pipelines/templates/stages/publish.yml +++ b/eng/pipelines/templates/stages/publish.yml @@ -375,6 +375,30 @@ stages: CreateGitHubRelease: false PublishUploadLocations: release/daily;daily/archive/$(Build.BuildId)-$(Build.SourceVersion) + # Upload version.txt for daily builds to enable update notifications + - pwsh: | + New-Item -ItemType Directory -Path daily-version -Force + Write-Output $(CLI_VERSION) | Out-File -Encoding utf8 -FilePath ./daily-version/version.txt + displayName: Write version.txt file for daily builds + + - task: AzurePowerShell@5 + displayName: Upload version.txt to daily channel + inputs: + azureSubscription: 'Azure SDK Artifacts' + azurePowerShellVersion: LatestVersion + pwsh: true + ScriptType: InlineScript + Inline: | + Get-ChildItem daily-version/ + Write-Host "Upload to azd/standalone/release/daily" + azcopy copy "daily-version/*" "$(publish-storage-location)/`$web/azd/standalone/release/daily" --overwrite=true + if ($LASTEXITCODE) { + Write-Error "Upload failed" + exit 1 + } + env: + AZCOPY_AUTO_LOGIN_TYPE: 'PSCRED' + - deployment: Publish_For_PR environment: none condition: >- From 134636ea242911adf2b9d632ee07e497742e4c90 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 30 Jan 2026 09:18:40 +0000 Subject: [PATCH 3/7] Fix formatting in main.go Co-authored-by: Petermarcu <8228359+Petermarcu@users.noreply.github.com> --- cli/azd/main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/azd/main.go b/cli/azd/main.go index 0b111014c2b..5b833685884 100644 --- a/cli/azd/main.go +++ b/cli/azd/main.go @@ -251,7 +251,7 @@ func fetchLatestVersion(version chan<- semver.Version) { // If we don't have a cached version we can use, fetch one (and cache it) if cachedLatestVersion == nil { log.Print("fetching latest version information for update check") - + // Determine which update channel to check (stable or daily) updateChannel := "latest" // Default to stable channel if channel, has := os.LookupEnv("AZD_UPDATE_CHANNEL"); has { @@ -262,7 +262,7 @@ func fetchLatestVersion(version chan<- semver.Version) { log.Printf("unknown update channel '%s', using stable channel", channel) } } - + versionUrl := fmt.Sprintf("https://aka.ms/azure-dev/versions/cli/%s", updateChannel) req, err := http.NewRequest(http.MethodGet, versionUrl, nil) if err != nil { From 64cbfe7c0ce568e0ced5b0bdcd09f69755b43946 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 30 Jan 2026 09:20:49 +0000 Subject: [PATCH 4/7] Address code review feedback - fix cache file naming and improve comments Co-authored-by: Petermarcu <8228359+Petermarcu@users.noreply.github.com> --- cli/azd/main.go | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/cli/azd/main.go b/cli/azd/main.go index 5b833685884..6c24d180b6f 100644 --- a/cli/azd/main.go +++ b/cli/azd/main.go @@ -198,15 +198,29 @@ func fetchLatestVersion(version chan<- semver.Version) { } } + // Determine which update channel to check (latest/stable or daily) + // "latest" and "stable" both refer to official releases + updateChannel := "latest" + if channel, has := os.LookupEnv("AZD_UPDATE_CHANNEL"); has { + if channel == "daily" { + updateChannel = "daily" + log.Print("using daily update channel") + } else if channel != "stable" && channel != "latest" { + log.Printf("unknown update channel '%s', using latest channel", channel) + } + } + // To avoid fetching the latest version of the CLI on every invocation, we cache the result for a period - // of time, in the user's home directory. + // of time, in the user's home directory. Use channel-specific cache file to avoid conflicts when + // switching between update channels. configDir, err := config.GetUserConfigDir() if err != nil { log.Printf("could not determine config directory: %v, skipping update check", err) return } - cacheFilePath := filepath.Join(configDir, updateCheckCacheFileName) + cacheFileName := fmt.Sprintf("update-check-%s.json", updateChannel) + cacheFilePath := filepath.Join(configDir, cacheFileName) cacheFile, err := os.ReadFile(cacheFilePath) if err != nil && !errors.Is(err, fs.ErrNotExist) { log.Printf("error reading update cache file: %v, skipping update check", err) @@ -252,19 +266,8 @@ func fetchLatestVersion(version chan<- semver.Version) { if cachedLatestVersion == nil { log.Print("fetching latest version information for update check") - // Determine which update channel to check (stable or daily) - updateChannel := "latest" // Default to stable channel - if channel, has := os.LookupEnv("AZD_UPDATE_CHANNEL"); has { - if channel == "daily" { - updateChannel = "daily" - log.Print("using daily update channel") - } else if channel != "stable" && channel != "latest" { - log.Printf("unknown update channel '%s', using stable channel", channel) - } - } - - versionUrl := fmt.Sprintf("https://aka.ms/azure-dev/versions/cli/%s", updateChannel) - req, err := http.NewRequest(http.MethodGet, versionUrl, nil) + versionURL := fmt.Sprintf("https://aka.ms/azure-dev/versions/cli/%s", updateChannel) + req, err := http.NewRequest(http.MethodGet, versionURL, nil) if err != nil { log.Printf("failed to create request object: %v, skipping update check", err) } From 29af9eafed973b15379157041cc2d3bd39eee59d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 30 Jan 2026 09:24:26 +0000 Subject: [PATCH 5/7] Add documentation for enabling daily build update notifications Co-authored-by: Petermarcu <8228359+Petermarcu@users.noreply.github.com> --- cli/azd/extensions/azure.ai.finetune/go.mod | 2 +- .../extensions/microsoft.azd.concurx/go.mod | 4 +-- .../extensions/microsoft.azd.concurx/go.sum | 8 ++--- cli/installer/README.md | 33 +++++++++++++++++++ 4 files changed, 40 insertions(+), 7 deletions(-) diff --git a/cli/azd/extensions/azure.ai.finetune/go.mod b/cli/azd/extensions/azure.ai.finetune/go.mod index 90b0dd3d22c..c056931f2b3 100644 --- a/cli/azd/extensions/azure.ai.finetune/go.mod +++ b/cli/azd/extensions/azure.ai.finetune/go.mod @@ -12,6 +12,7 @@ require ( github.com/openai/openai-go/v3 v3.2.0 github.com/sethvargo/go-retry v0.3.0 github.com/spf13/cobra v1.10.1 + github.com/stretchr/testify v1.11.1 gopkg.in/yaml.v3 v3.0.1 ) @@ -69,7 +70,6 @@ require ( github.com/rivo/uniseg v0.4.7 // indirect github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect github.com/spf13/pflag v1.0.10 // indirect - github.com/stretchr/testify v1.11.1 // indirect github.com/theckman/yacspin v0.13.12 // indirect github.com/tidwall/gjson v1.18.0 // indirect github.com/tidwall/match v1.2.0 // indirect diff --git a/cli/azd/extensions/microsoft.azd.concurx/go.mod b/cli/azd/extensions/microsoft.azd.concurx/go.mod index 1164ab05cfb..f6ca13b2857 100644 --- a/cli/azd/extensions/microsoft.azd.concurx/go.mod +++ b/cli/azd/extensions/microsoft.azd.concurx/go.mod @@ -76,9 +76,9 @@ require ( go.opentelemetry.io/otel/sdk v1.38.0 // indirect go.opentelemetry.io/otel/trace v1.38.0 // indirect go.uber.org/atomic v1.11.0 // indirect - golang.org/x/net v0.47.0 // indirect + golang.org/x/net v0.48.0 // indirect golang.org/x/sys v0.39.0 // indirect - golang.org/x/term v0.37.0 // indirect + golang.org/x/term v0.38.0 // indirect golang.org/x/text v0.32.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20251007200510-49b9836ed3ff // indirect google.golang.org/grpc v1.76.0 // indirect diff --git a/cli/azd/extensions/microsoft.azd.concurx/go.sum b/cli/azd/extensions/microsoft.azd.concurx/go.sum index f9299daa7c5..a3768ddd5b4 100644 --- a/cli/azd/extensions/microsoft.azd.concurx/go.sum +++ b/cli/azd/extensions/microsoft.azd.concurx/go.sum @@ -205,8 +205,8 @@ go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0 golang.org/x/exp v0.0.0-20250911091902-df9299821621 h1:2id6c1/gto0kaHYyrixvknJ8tUK/Qs5IsmBtrc+FtgU= golang.org/x/exp v0.0.0-20250911091902-df9299821621/go.mod h1:TwQYMMnGpvZyc+JpB/UAuTNIsVJifOlSkrZkhcvpVUk= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= -golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= +golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= +golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -214,8 +214,8 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= -golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= +golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q= +golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= diff --git a/cli/installer/README.md b/cli/installer/README.md index 12a9151953e..80d4ab14e76 100644 --- a/cli/installer/README.md +++ b/cli/installer/README.md @@ -170,6 +170,39 @@ The `--install-folder` parameter places the `azd` binary in the specified locati The `daily` feed is periodically updated with builds from the latest source code in the `main` branch. Use the `version` parameter to download the latest daily release. +#### Enabling update notifications for daily builds + +By default, `azd` checks for updates from the stable release channel. If you're using daily builds and want to receive update notifications for new daily builds, set the `AZD_UPDATE_CHANNEL` environment variable to `daily`: + +**Windows (PowerShell):** +```powershell +[System.Environment]::SetEnvironmentVariable('AZD_UPDATE_CHANNEL', 'daily', 'User') +``` + +**Linux/MacOS (bash):** +```bash +echo 'export AZD_UPDATE_CHANNEL=daily' >> ~/.bashrc +source ~/.bashrc +``` + +**Linux/MacOS (zsh):** +```bash +echo 'export AZD_UPDATE_CHANNEL=daily' >> ~/.zshrc +source ~/.zshrc +``` + +To switch back to stable releases, unset the variable or set it to `stable`: + +**Windows (PowerShell):** +```powershell +[System.Environment]::SetEnvironmentVariable('AZD_UPDATE_CHANNEL', 'stable', 'User') +``` + +**Linux/MacOS:** +```bash +export AZD_UPDATE_CHANNEL=stable +``` + #### Windows ##### Install From 657baa3dc8caa52d3ca2558ef1b9e732783de6ad Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 30 Jan 2026 17:24:32 +0000 Subject: [PATCH 6/7] Add global config support for update channel and improve documentation - Revert unintended extension go.mod/go.sum changes - Add support for defaults.updateChannel config setting - Environment variable AZD_UPDATE_CHANNEL overrides config - Simplify installer README with recommended config approach - Emphasize using install scripts for daily builds Co-authored-by: danieljurek <2158838+danieljurek@users.noreply.github.com> --- cli/azd/docs/environment-variables.md | 2 +- cli/azd/extensions/azure.ai.finetune/go.mod | 2 +- .../extensions/microsoft.azd.concurx/go.mod | 4 +- .../extensions/microsoft.azd.concurx/go.sum | 8 ++-- cli/azd/main.go | 40 +++++++++++++++++-- cli/azd/resources/config_options.yaml | 11 ++--- cli/installer/README.md | 32 +++++---------- 7 files changed, 61 insertions(+), 38 deletions(-) diff --git a/cli/azd/docs/environment-variables.md b/cli/azd/docs/environment-variables.md index 6af120064ba..7d8d9166b66 100644 --- a/cli/azd/docs/environment-variables.md +++ b/cli/azd/docs/environment-variables.md @@ -13,7 +13,7 @@ Environment variables that can be used to configure `azd` behavior, usually set - `AZD_FORCE_TTY`: If true, forces `azd` to write terminal-style output. - `AZD_IN_CLOUDSHELL`: If true, `azd` runs with Azure Cloud Shell specific behavior. - `AZD_SKIP_UPDATE_CHECK`: If true, skips the out-of-date update check output that is typically printed at the end of the command. -- `AZD_UPDATE_CHANNEL`: Choose which update channel to check for new versions. Set to `daily` to receive notifications for daily/continuous builds, or `stable`/`latest` (default) for official releases only. +- `AZD_UPDATE_CHANNEL`: Choose which update channel to check for new versions. Set to `daily` to receive notifications for daily/continuous builds, or `stable`/`latest` (default) for official releases only. This environment variable overrides the `defaults.updateChannel` config setting. For a persistent setting, use `azd config set defaults.updateChannel daily`. For tools that are auto-acquired by `azd`, you are able to configure the following environment variables to use a different version of the tool installed on the machine: diff --git a/cli/azd/extensions/azure.ai.finetune/go.mod b/cli/azd/extensions/azure.ai.finetune/go.mod index c056931f2b3..90b0dd3d22c 100644 --- a/cli/azd/extensions/azure.ai.finetune/go.mod +++ b/cli/azd/extensions/azure.ai.finetune/go.mod @@ -12,7 +12,6 @@ require ( github.com/openai/openai-go/v3 v3.2.0 github.com/sethvargo/go-retry v0.3.0 github.com/spf13/cobra v1.10.1 - github.com/stretchr/testify v1.11.1 gopkg.in/yaml.v3 v3.0.1 ) @@ -70,6 +69,7 @@ require ( github.com/rivo/uniseg v0.4.7 // indirect github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect github.com/spf13/pflag v1.0.10 // indirect + github.com/stretchr/testify v1.11.1 // indirect github.com/theckman/yacspin v0.13.12 // indirect github.com/tidwall/gjson v1.18.0 // indirect github.com/tidwall/match v1.2.0 // indirect diff --git a/cli/azd/extensions/microsoft.azd.concurx/go.mod b/cli/azd/extensions/microsoft.azd.concurx/go.mod index f6ca13b2857..1164ab05cfb 100644 --- a/cli/azd/extensions/microsoft.azd.concurx/go.mod +++ b/cli/azd/extensions/microsoft.azd.concurx/go.mod @@ -76,9 +76,9 @@ require ( go.opentelemetry.io/otel/sdk v1.38.0 // indirect go.opentelemetry.io/otel/trace v1.38.0 // indirect go.uber.org/atomic v1.11.0 // indirect - golang.org/x/net v0.48.0 // indirect + golang.org/x/net v0.47.0 // indirect golang.org/x/sys v0.39.0 // indirect - golang.org/x/term v0.38.0 // indirect + golang.org/x/term v0.37.0 // indirect golang.org/x/text v0.32.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20251007200510-49b9836ed3ff // indirect google.golang.org/grpc v1.76.0 // indirect diff --git a/cli/azd/extensions/microsoft.azd.concurx/go.sum b/cli/azd/extensions/microsoft.azd.concurx/go.sum index a3768ddd5b4..f9299daa7c5 100644 --- a/cli/azd/extensions/microsoft.azd.concurx/go.sum +++ b/cli/azd/extensions/microsoft.azd.concurx/go.sum @@ -205,8 +205,8 @@ go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0 golang.org/x/exp v0.0.0-20250911091902-df9299821621 h1:2id6c1/gto0kaHYyrixvknJ8tUK/Qs5IsmBtrc+FtgU= golang.org/x/exp v0.0.0-20250911091902-df9299821621/go.mod h1:TwQYMMnGpvZyc+JpB/UAuTNIsVJifOlSkrZkhcvpVUk= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= -golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= +golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= +golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -214,8 +214,8 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q= -golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg= +golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= +golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= diff --git a/cli/azd/main.go b/cli/azd/main.go index 6c24d180b6f..1b9772f3646 100644 --- a/cli/azd/main.go +++ b/cli/azd/main.go @@ -200,20 +200,52 @@ func fetchLatestVersion(version chan<- semver.Version) { // Determine which update channel to check (latest/stable or daily) // "latest" and "stable" both refer to official releases + // Priority: 1) Environment variable, 2) User config file, 3) Default to "latest" updateChannel := "latest" + + // First, try to read from user config file + configDir, err := config.GetUserConfigDir() + if err != nil { + log.Printf("could not determine config directory: %v, will use default channel", err) + } else { + configFilePath := filepath.Join(configDir, "config.json") + if configFile, err := os.ReadFile(configFilePath); err == nil { + if userConfig, err := config.Parse(configFile); err == nil { + if channel, ok := userConfig.GetString("defaults.updateChannel"); ok { + if channel == "daily" || channel == "stable" || channel == "latest" { + updateChannel = channel + log.Printf("using update channel '%s' from config file", channel) + } else { + log.Printf("unknown update channel '%s' in config file, using latest channel", channel) + } + } + } + } + } + + // Environment variable overrides config file setting if channel, has := os.LookupEnv("AZD_UPDATE_CHANNEL"); has { if channel == "daily" { updateChannel = "daily" - log.Print("using daily update channel") - } else if channel != "stable" && channel != "latest" { - log.Printf("unknown update channel '%s', using latest channel", channel) + log.Print("using daily update channel from environment variable") + } else if channel == "stable" || channel == "latest" { + updateChannel = channel + log.Printf("using %s update channel from environment variable", channel) + } else { + log.Printf("unknown update channel '%s' in environment variable, using latest channel", channel) } } // To avoid fetching the latest version of the CLI on every invocation, we cache the result for a period // of time, in the user's home directory. Use channel-specific cache file to avoid conflicts when // switching between update channels. - configDir, err := config.GetUserConfigDir() + if err != nil { + log.Printf("could not determine config directory: %v, skipping update check", err) + return + } + + // Re-get config dir in case we couldn't get it earlier + configDir, err = config.GetUserConfigDir() if err != nil { log.Printf("could not determine config directory: %v, skipping update check", err) return diff --git a/cli/azd/resources/config_options.yaml b/cli/azd/resources/config_options.yaml index 0c97db433b0..a30273063f0 100644 --- a/cli/azd/resources/config_options.yaml +++ b/cli/azd/resources/config_options.yaml @@ -80,12 +80,13 @@ - key: pipeline.config.applicationServiceManagementReference description: "Application Service Management Reference for Azure pipeline configuration." type: string +- key: defaults.updateChannel + description: "Choose which update channel to check for new versions. Set to 'daily' to receive notifications for daily/continuous builds, or 'stable'/'latest' (default) for official releases only. Environment variable AZD_UPDATE_CHANNEL takes precedence over this config setting." + type: string + allowedValues: ["stable", "latest", "daily"] + envVar: "AZD_UPDATE_CHANNEL" + example: "daily" - key: (env) AZD_CONFIG_DIR description: "Override the default configuration directory location." type: envvar example: "/path/to/config" -- key: (env) AZD_UPDATE_CHANNEL - description: "Choose which update channel to check for new versions. Set to 'daily' to receive notifications for daily/continuous builds, or 'stable' (default) for official releases only." - type: envvar - allowedValues: ["stable", "latest", "daily"] - example: "daily" diff --git a/cli/installer/README.md b/cli/installer/README.md index 80d4ab14e76..d318f372a51 100644 --- a/cli/installer/README.md +++ b/cli/installer/README.md @@ -172,41 +172,31 @@ The `daily` feed is periodically updated with builds from the latest source code #### Enabling update notifications for daily builds -By default, `azd` checks for updates from the stable release channel. If you're using daily builds and want to receive update notifications for new daily builds, set the `AZD_UPDATE_CHANNEL` environment variable to `daily`: +By default, `azd` checks for updates from the stable release channel. If you're using daily builds and want to receive update notifications for new daily builds, you can configure this using either a config setting or an environment variable. -**Windows (PowerShell):** -```powershell -[System.Environment]::SetEnvironmentVariable('AZD_UPDATE_CHANNEL', 'daily', 'User') -``` - -**Linux/MacOS (bash):** +**Using config (recommended for persistent setting):** ```bash -echo 'export AZD_UPDATE_CHANNEL=daily' >> ~/.bashrc -source ~/.bashrc +azd config set defaults.updateChannel daily ``` -**Linux/MacOS (zsh):** +**Using environment variable (overrides config):** ```bash -echo 'export AZD_UPDATE_CHANNEL=daily' >> ~/.zshrc -source ~/.zshrc -``` - -To switch back to stable releases, unset the variable or set it to `stable`: - -**Windows (PowerShell):** -```powershell -[System.Environment]::SetEnvironmentVariable('AZD_UPDATE_CHANNEL', 'stable', 'User') +export AZD_UPDATE_CHANNEL=daily ``` -**Linux/MacOS:** +To switch back to stable releases: ```bash -export AZD_UPDATE_CHANNEL=stable +azd config set defaults.updateChannel stable ``` +**Note:** When using daily builds, ensure you've installed azd using the install script with the `--version daily` parameter (shown below) to get the latest daily build. Update notifications will alert you when newer daily builds become available. + #### Windows ##### Install +##### Install + ```pwsh powershell -ex AllSigned -c "Invoke-RestMethod 'https://aka.ms/install-azd.ps1' -OutFile 'install-azd.ps1'; ./install-azd.ps1 -Version 'daily'" ``` From 8ae518c8d0b5a0aff2ad58532e36446c13d9d3a7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 2 Feb 2026 18:13:38 +0000 Subject: [PATCH 7/7] Fix update notifications for daily channel users - Pass channel info from fetchLatestVersion through versionCheckResult struct - Show daily-specific upgrade instructions for daily channel users - Only show script-based install commands for daily builds - Package managers (winget, choco, brew, deb, rpm) don't support daily channel - Remove redundant "Install" header in installer README Co-authored-by: danieljurek <2158838+danieljurek@users.noreply.github.com> --- cli/azd/main.go | 126 ++++++++++++++++++++++++++-------------- cli/installer/README.md | 2 - 2 files changed, 84 insertions(+), 44 deletions(-) diff --git a/cli/azd/main.go b/cli/azd/main.go index 1b9772f3646..3cf83db1ad9 100644 --- a/cli/azd/main.go +++ b/cli/azd/main.go @@ -64,7 +64,7 @@ func main() { ctx = tracing.ContextFromEnv(ctx) } - latest := make(chan semver.Version) + latest := make(chan versionCheckResult) go fetchLatestVersion(latest) rootContainer := ioc.NewNestedContainer(nil) @@ -86,7 +86,7 @@ func main() { } } - latestVersion, ok := <-latest + checkResult, ok := <-latest // If we were able to fetch a latest version, check to see if we are up to date and // print a warning if we are not. Note that we don't print this warning when the CLI version @@ -100,55 +100,88 @@ func main() { // This is a dev build (i.e. built using `go install without setting a version`) - don't print a warning in this // case log.Printf("eliding update message for dev build") - } else if latestVersion.GT(internal.VersionInfo().Version) { + } else if checkResult.version.GT(internal.VersionInfo().Version) { var upgradeText string installedBy := installer.InstalledBy() - if runtime.GOOS == "windows" { - switch installedBy { - case installer.InstallTypePs: - //nolint:lll - upgradeText = "run:\npowershell -ex AllSigned -c \"Invoke-RestMethod 'https://aka.ms/install-azd.ps1' | Invoke-Expression\"\n\nIf the install script was run with custom parameters, ensure that the same parameters are used for the upgrade. For advanced install instructions, see: https://aka.ms/azd/upgrade/windows" - case installer.InstallTypeWinget: - upgradeText = "run:\nwinget upgrade Microsoft.Azd" - case installer.InstallTypeChoco: - upgradeText = "run:\nchoco upgrade azd" - default: - // Also covers "msi" case where the user installed directly - // via MSI - upgradeText = "visit https://aka.ms/azd/upgrade/windows" - } - } else if runtime.GOOS == "linux" { - switch installedBy { - case installer.InstallTypeSh: - //nolint:lll - upgradeText = "run:\ncurl -fsSL https://aka.ms/install-azd.sh | bash\n\nIf the install script was run with custom parameters, ensure that the same parameters are used for the upgrade. For advanced install instructions, see: https://aka.ms/azd/upgrade/linux" - default: - // Also covers "deb" and "rpm" cases which are currently - // documented. When package manager distribution support is - // added, this will need to be updated. - upgradeText = "visit https://aka.ms/azd/upgrade/linux" - } - } else if runtime.GOOS == "darwin" { - switch installedBy { - case installer.InstallTypeBrew: - upgradeText = "run:\nbrew update && brew upgrade azd" - case installer.InstallTypeSh: - //nolint:lll - upgradeText = "run:\ncurl -fsSL https://aka.ms/install-azd.sh | bash\n\nIf the install script was run with custom parameters, ensure that the same parameters are used for the upgrade. For advanced install instructions, see: https://aka.ms/azd/upgrade/mac" - default: - upgradeText = "visit https://aka.ms/azd/upgrade/mac" + + // For daily channel, only show upgrade instructions for script-based installations + if checkResult.channel == "daily" { + if runtime.GOOS == "windows" { + if installedBy == installer.InstallTypePs { + //nolint:lll + upgradeText = "run:\npowershell -ex AllSigned -c \"Invoke-RestMethod 'https://aka.ms/install-azd.ps1' -OutFile 'install-azd.ps1'; ./install-azd.ps1 -Version 'daily'\"\n\nFor more information on daily builds, see: https://aka.ms/azd/install" + } else { + //nolint:lll + upgradeText = "visit https://aka.ms/azd/install and use the PowerShell install script with '-Version daily' to upgrade to the latest daily build" + } + } else if runtime.GOOS == "linux" { + if installedBy == installer.InstallTypeSh { + //nolint:lll + upgradeText = "run:\ncurl -fsSL https://aka.ms/install-azd.sh | bash -s -- --version daily\n\nFor more information on daily builds, see: https://aka.ms/azd/install" + } else { + //nolint:lll + upgradeText = "visit https://aka.ms/azd/install and use the install script with '--version daily' to upgrade to the latest daily build" + } + } else if runtime.GOOS == "darwin" { + if installedBy == installer.InstallTypeSh { + //nolint:lll + upgradeText = "run:\ncurl -fsSL https://aka.ms/install-azd.sh | bash -s -- --version daily\n\nFor more information on daily builds, see: https://aka.ms/azd/install" + } else { + //nolint:lll + upgradeText = "visit https://aka.ms/azd/install and use the install script with '--version daily' to upgrade to the latest daily build" + } + } else { + upgradeText = "visit https://aka.ms/azd/install and use the install script with '--version daily' to upgrade to the latest daily build" } } else { - // Platform is not recognized, use the generic install link - upgradeText = "visit https://aka.ms/azd/upgrade" + // Standard upgrade instructions for stable/latest channel + if runtime.GOOS == "windows" { + switch installedBy { + case installer.InstallTypePs: + //nolint:lll + upgradeText = "run:\npowershell -ex AllSigned -c \"Invoke-RestMethod 'https://aka.ms/install-azd.ps1' | Invoke-Expression\"\n\nIf the install script was run with custom parameters, ensure that the same parameters are used for the upgrade. For advanced install instructions, see: https://aka.ms/azd/upgrade/windows" + case installer.InstallTypeWinget: + upgradeText = "run:\nwinget upgrade Microsoft.Azd" + case installer.InstallTypeChoco: + upgradeText = "run:\nchoco upgrade azd" + default: + // Also covers "msi" case where the user installed directly + // via MSI + upgradeText = "visit https://aka.ms/azd/upgrade/windows" + } + } else if runtime.GOOS == "linux" { + switch installedBy { + case installer.InstallTypeSh: + //nolint:lll + upgradeText = "run:\ncurl -fsSL https://aka.ms/install-azd.sh | bash\n\nIf the install script was run with custom parameters, ensure that the same parameters are used for the upgrade. For advanced install instructions, see: https://aka.ms/azd/upgrade/linux" + default: + // Also covers "deb" and "rpm" cases which are currently + // documented. When package manager distribution support is + // added, this will need to be updated. + upgradeText = "visit https://aka.ms/azd/upgrade/linux" + } + } else if runtime.GOOS == "darwin" { + switch installedBy { + case installer.InstallTypeBrew: + upgradeText = "run:\nbrew update && brew upgrade azd" + case installer.InstallTypeSh: + //nolint:lll + upgradeText = "run:\ncurl -fsSL https://aka.ms/install-azd.sh | bash\n\nIf the install script was run with custom parameters, ensure that the same parameters are used for the upgrade. For advanced install instructions, see: https://aka.ms/azd/upgrade/mac" + default: + upgradeText = "visit https://aka.ms/azd/upgrade/mac" + } + } else { + // Platform is not recognized, use the generic install link + upgradeText = "visit https://aka.ms/azd/upgrade" + } } fmt.Fprintln( os.Stderr, output.WithWarningFormat( "WARNING: your version of azd is out of date, you have %s and the latest version is %s", - internal.VersionInfo().Version.String(), latestVersion.String())) + internal.VersionInfo().Version.String(), checkResult.version.String())) fmt.Fprintln(os.Stderr) fmt.Fprintln( os.Stderr, @@ -180,10 +213,16 @@ func main() { // which is used to cache version information for our up to date check. const updateCheckCacheFileName = "update-check.json" +// versionCheckResult contains the result of checking for the latest version +type versionCheckResult struct { + version semver.Version + channel string +} + // fetchLatestVersion fetches the latest version of the CLI and sends the result // across the version channel, which it then closes. If the latest version can not // be determined, the channel is closed without writing a value. -func fetchLatestVersion(version chan<- semver.Version) { +func fetchLatestVersion(version chan<- versionCheckResult) { defer close(version) // Allow the user to skip the update check if they wish, by setting AZD_SKIP_UPDATE_CHECK to @@ -361,7 +400,10 @@ func fetchLatestVersion(version chan<- semver.Version) { } // Publish our value, the defer above will close the channel. - version <- *cachedLatestVersion + version <- versionCheckResult{ + version: *cachedLatestVersion, + channel: updateChannel, + } } type updateCacheFile struct { diff --git a/cli/installer/README.md b/cli/installer/README.md index d318f372a51..416d45c2e7c 100644 --- a/cli/installer/README.md +++ b/cli/installer/README.md @@ -195,8 +195,6 @@ azd config set defaults.updateChannel stable ##### Install -##### Install - ```pwsh powershell -ex AllSigned -c "Invoke-RestMethod 'https://aka.ms/install-azd.ps1' -OutFile 'install-azd.ps1'; ./install-azd.ps1 -Version 'daily'" ```