fix(update): pin stable updates to the resolved version - #9325
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. |
There was a problem hiding this comment.
Pull request overview
Pins stable self-updates to the version resolved by the update check, preventing reported and installed versions from diverging.
Changes:
- Passes resolved
VersionInfothrough update paths. - Uses immutable versioned folders for stable installs.
- Avoids version promises for package-manager updates and adds documentation/tests.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
cli/azd/cmd/update.go |
Updates progress and success messaging. |
cli/azd/docs/design/azd-update.md |
Documents version pinning behavior. |
cli/azd/pkg/update/manager.go |
Implements resolved-version installation. |
cli/azd/pkg/update/manager_test.go |
Tests URL pinning and validation. |
cli/azd/pkg/update/msi_unix.go |
Updates the platform stub signature. |
cli/azd/pkg/update/msi_windows.go |
Passes exact stable versions to PowerShell. |
cli/azd/pkg/update/msi_windows_test.go |
Tests MSI script arguments. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 8 out of 8 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (2)
cli/azd/cmd/update.go:315
- [azd-code-reviewer] Daily direct installs are still treated as pinnable here, even though
VersionInfo.installVersion()returns the rollingdailyfolder. The daily pipeline uploadsrelease/*as independent blobs (eng/pipelines/templates/steps/publish-cli.yml:142), and another daily publish can also occur between the check and install, so these branches can still nametarget.Versionand then install a different build. Make pin capability channel-aware and suppress exact-version messages for daily, or download daily from its immutable archive path.
if !update.CanPinVersion(installedBy) {
cli/azd/pkg/update/msi_windows.go:181
- [azd-code-reviewer] The PR's Testing section says an unknown channel falls back to
-Version 'stable', but this branch now returns an error. The implementation and design doc consistently reject unsupported channels, so update the PR description to match.
default:
return nil, fmt.Errorf("unsupported channel: %s", target.Channel)
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 8 out of 8 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (2)
cli/azd/cmd/update.go:320
- The pinning gate is not applied to the channel-switch confirmation, which still always interpolates
versionInfo.Versionat lines 249–253. A Homebrew channel switch can therefore prompt for a specific version that Homebrew may not install, contradicting the new rule that unpinned installs must not report a version. UseCanPinVersionwhen building that prompt too, and add the package-manager switch case to the message tests.
case update.CanPinVersion(installedBy, target.Channel):
return fmt.Sprintf("Updating azd to %s (%s)", target.Version, target.Channel)
case update.UsesPackageManager(installedBy):
return fmt.Sprintf("Updating azd via %s (%s)", installedBy, target.Channel)
cli/azd/pkg/update/manager.go:331
- Checking only for an empty string does not enforce the stated invariant that stable targets use an immutable version folder.
VersionInfo{Channel: ChannelStable, Version: "stable"}passes validation and sends every self-managed path back torelease/stable/, reintroducing the drift this validation is intended to prevent; other arbitrary publicVersionInfovalues are accepted too. Parse the stable value as semver here and add a test that rejects the rolling channel token.
case ChannelStable:
if v.Version == "" {
return fmt.Errorf("no resolved version for the %s channel", ChannelStable)
}
|
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. |
Azure Dev CLI Install InstructionsInstall scriptsMacOS/Linux
bash: pwsh: WindowsPowerShell install MSI install Standalone Binary
MSI
Documentationlearn.microsoft.com documentationtitle: Azure Developer CLI reference
|
Fix #9145
azd updatereported one version and installed another:Root cause
The update check and the installer read two different artifacts:
checkStableVersionrelease/latest/version.txt(viaaka.ms/azure-dev/versions/cli/latest)--version stable/-Version 'stable')release/stable/eng/pipelines/templates/stages/publish.ymlpublishes them in separate stages. Binaries go out inPublishCLI→Publish_Release, which uploads torelease/$(CLI_VERSION);release/latestplusrelease/stablefor GA.version.txtis uploaded much later byPublishVersionTxt→Upload_Version_Txt, behind theReleaseGatedeployment on the manualpackage-publishenvironment plusWait-WinGetPackage.ps1/Wait-ChocoPackage.ps1(both of whichexit 1on timeout and take the upload down with them).During that window
release/stable/already holds the newer build whileversion.txtstill advertises the older one, so "install whatever is in the channel folder" silently delivers a different version than the one reported.Blob timestamps confirm the split is structural rather than incidental:
stabletarballJul 23 02:01:57vslatest/version.txtJul 23 07:20:36— +5h19m21:02:43vs21:02:49— +6sFix
Pin self-managed stable installs to the exact version the check resolved, downloading from the immutable
release/<version>/folder instead of the rollingrelease/stable/one.Rather than threading a version alongside the channel,
Manager.Updatenow takes the*VersionInfothatCheckForUpdatealready returns:VersionInfoalready carries bothChannelandVersion, and the update path only ever usedcfg.Channel— so this is a parameter replacement, not an addition, and it makes a channel/version mismatch unrepresentable. The install can only ever target the version that was actually resolved and reported.Per-version release folders are published in the same job as the rolling folders and before
version.txtadvertises them, so a pinned folder is always present by the time azd can resolve its version.Exceptions
release/daily/folder. Daily builds publish their version marker and archives in a single operation, so the folder never advertises a version it cannot serve — and per-version daily archives live atstandalone/daily/archive/<version>, outside the release base URL the install scripts use.brew,winget,choco) cannot be pinned — the package manager decides which version it makes available. azd therefore no longer names a version in its progress or success messages for those installs (Updating azd via winget (stable)/Updated azd!), and directs the user toazd version.Why pin rather than report what the rolling folder holds
The issue's stated expectation is the mirror image: have the check resolve to whatever
release/stable/currently serves, so azd reports1.27.1and installs1.27.1. That was rejected — there is no marker that describes the rolling folder's current contents (release/stable/version.txtis written by the late job, not with the binaries), so the check would have to infer the version from the archive it downloads, i.e. commit to an install before it can report one. Pinning gets the same consistency from data azd already has. The tradeoff is that a user can sit one release behind untilversion.txtcatches up, at which point they are prompted again.An unresolved stable target is rejected rather than silently unpinned:
Manager.UpdateandManager.StageUpdatefail if a stableVersionInfocarries no version, since falling back torelease/stable/would reintroduce exactly the drift this fixes.Testing
TestBuildDownloadURL— pinned stable / daily / invalid channelTestBuildDownloadURL_StableDoesNotUseRollingFolder— a stable URL never contains/stable/TestInstallVersion— stable pins, daily stays rollingTestUpdate_RequiresResolvedVersion—Update/StageUpdatereject a nil target and an unresolved stable target; daily needs no resolved versionTestUpdateViaInstallScript—--version 1.28.1for stable,dailyfor dailyTestBuildInstallScriptArgs—-Version '1.28.1'for the stable MSI path; unknown channel falls back to-Version 'stable'Verified with
go build ./...,go vet,golangci-lint run(0 issues),gofmt -s,cspell,go test ./pkg/update/...andgo test ./cmd/ -run 'Update|Version'.Also verified against live storage that the pinned layout exists and the installers accept an exact version:
release/1.28.1/{azd-windows-amd64.zip,azd-windows-amd64.msi,azd-linux-amd64.tar.gz,azd-darwin-arm64.zip}all return 200,install-azd.shbuilds$base_url/$version/azd-..., andinstall-azd.ps1builds$BaseUrl/$Version/$packageFilename.