From 8549272571b03f643c9429bac9317fd5c11933f4 Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Tue, 3 Mar 2026 22:07:15 -0800 Subject: [PATCH 01/19] Set upgrade codes for release and pre-release builds --- cli/installer/windows/azd.wxs | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/cli/installer/windows/azd.wxs b/cli/installer/windows/azd.wxs index 6f241ab3173..5ee58c001bd 100644 --- a/cli/installer/windows/azd.wxs +++ b/cli/installer/windows/azd.wxs @@ -14,14 +14,32 @@ - - + + + + + + + + - - + + + + + + + + - - + + + + + + + + @@ -45,6 +63,11 @@ + + + + + From e1cb013234910c61bcc226a1a3d12d6ce5b416e7 Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Wed, 4 Mar 2026 09:19:14 -0800 Subject: [PATCH 02/19] 0.0.0 --- cli/installer/windows/azd.wxs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/installer/windows/azd.wxs b/cli/installer/windows/azd.wxs index 5ee58c001bd..f6ad3dbaaf4 100644 --- a/cli/installer/windows/azd.wxs +++ b/cli/installer/windows/azd.wxs @@ -65,7 +65,7 @@ - + From 38e1e25c1365bd5398390086303b25f68d543f9e Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Wed, 4 Mar 2026 09:40:45 -0800 Subject: [PATCH 03/19] Comment --- cli/installer/windows/azd.wxs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/installer/windows/azd.wxs b/cli/installer/windows/azd.wxs index f6ad3dbaaf4..de47114507d 100644 --- a/cli/installer/windows/azd.wxs +++ b/cli/installer/windows/azd.wxs @@ -12,7 +12,7 @@ - + From 468a820b8d68c59f23ecb86aef0d8a87dc39c618 Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Wed, 4 Mar 2026 18:01:24 -0800 Subject: [PATCH 04/19] Make ReleaseBuild an explicit property, calculate a version for the version's build number based on the pipeline's execution ID --- cli/installer/windows/azd.wixproj | 2 +- eng/pipelines/templates/steps/build-msi.yml | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/cli/installer/windows/azd.wixproj b/cli/installer/windows/azd.wixproj index cc461046a51..fceec5831cf 100644 --- a/cli/installer/windows/azd.wixproj +++ b/cli/installer/windows/azd.wixproj @@ -6,7 +6,7 @@ Release x64 Azure Developer CLI (dev) - true + true 0.1.0 $(ProductVersion.Substring(0, $(ProductVersion.IndexOf('-')))) diff --git a/eng/pipelines/templates/steps/build-msi.yml b/eng/pipelines/templates/steps/build-msi.yml index a4f4970556e..4f9bbb9bc7a 100644 --- a/eng/pipelines/templates/steps/build-msi.yml +++ b/eng/pipelines/templates/steps/build-msi.yml @@ -19,6 +19,7 @@ steps: - pwsh: | Write-Host "##vso[task.setvariable variable=MSI_PRODUCT_NAME_PARAM]/p:ProductName=`"Azure Developer CLI`"" Write-Host "##vso[task.setvariable variable=MSI_VERSION_PARAM]/p:ProductVersion=$(MSI_VERSION)" + Write-Host "##vso[task.setvariable variable=RELEASE_BUILD_PARAM]/p:ReleaseBuild=true" condition: ${{ parameters.Condition }} displayName: Set MSI_PRODUCT_NAME_PARAM for release @@ -28,8 +29,14 @@ steps: # or DevOps will substitute in the literal string # (e.g. '$(MSI_PRODUCT_NAME_PARAM)') into the msbuild command arguments. - pwsh: | + $msiVersion = '$(MSI_VERSION)' + $major = ($msiVersion -split '\.')[0] + $minor = ($msiVersion -split '\.')[1] + $build = $(System.BuildId) % 65536 + Write-Host "##vso[task.setvariable variable=MSI_PRODUCT_NAME_PARAM]" - Write-Host "##vso[task.setvariable variable=MSI_VERSION_PARAM]" + Write-Host "##vso[task.setvariable variable=MSI_VERSION_PARAM]/p:ProductVersion=$major.$minor.$build" + Write-Host "##vso[task.setvariable variable=RELEASE_BUILD_PARAM]" condition: ${{ parameters.Condition }} displayName: Set MSI_PRODUCT_NAME_PARAM and MSI_VERSION_PARAM for dev release From f7e417a4ad34acf6d8cca04a257cb09d4b23efd4 Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Wed, 4 Mar 2026 18:01:47 -0800 Subject: [PATCH 05/19] Include RELEASE_BUILD_PARAM --- eng/pipelines/templates/steps/build-msi.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/eng/pipelines/templates/steps/build-msi.yml b/eng/pipelines/templates/steps/build-msi.yml index 4f9bbb9bc7a..65d329614ae 100644 --- a/eng/pipelines/templates/steps/build-msi.yml +++ b/eng/pipelines/templates/steps/build-msi.yml @@ -50,3 +50,4 @@ steps: /p:Configuration=Release $(MSI_PRODUCT_NAME_PARAM) $(MSI_VERSION_PARAM) + $(RELEASE_BUILD_PARAM) From 2a71cad180f4a4acc2d976b5b064e265f3255b33 Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Wed, 4 Mar 2026 18:05:14 -0800 Subject: [PATCH 06/19] Comment --- eng/pipelines/templates/stages/sign.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eng/pipelines/templates/stages/sign.yml b/eng/pipelines/templates/stages/sign.yml index 23559cf3047..cad1260171d 100644 --- a/eng/pipelines/templates/stages/sign.yml +++ b/eng/pipelines/templates/stages/sign.yml @@ -141,8 +141,8 @@ stages: parameters: Title: Build Release MSI # Only build for release in a manual (releasing) build. Otherwise - # the package version will be 0.1.0 with upgrade logic that allows - # it to be installed over any previously installed version. + # the package version will be based on the pipeline's build ID and + # the MSI will allow downgrades which is desirable for CI builds. ShouldBuildForRelease: ${{ eq(variables['Build.Reason'], 'Manual') }} - ${{ if and(in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI', 'Manual'), eq(variables['Build.Repository.Name'], 'Azure/azure-dev')) }}: From f8da9fa0117540461db85c4fde847e544e015aba Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Wed, 4 Mar 2026 19:21:45 -0800 Subject: [PATCH 07/19] Build.BuildId --- eng/pipelines/templates/steps/build-msi.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eng/pipelines/templates/steps/build-msi.yml b/eng/pipelines/templates/steps/build-msi.yml index 65d329614ae..a0cbad81d89 100644 --- a/eng/pipelines/templates/steps/build-msi.yml +++ b/eng/pipelines/templates/steps/build-msi.yml @@ -32,7 +32,7 @@ steps: $msiVersion = '$(MSI_VERSION)' $major = ($msiVersion -split '\.')[0] $minor = ($msiVersion -split '\.')[1] - $build = $(System.BuildId) % 65536 + $build = $(Build.BuildId) % 65536 Write-Host "##vso[task.setvariable variable=MSI_PRODUCT_NAME_PARAM]" Write-Host "##vso[task.setvariable variable=MSI_VERSION_PARAM]/p:ProductVersion=$major.$minor.$build" From c1f8303f2723b9b359604d4bf6056b5ab5693cff Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Wed, 4 Mar 2026 19:24:38 -0800 Subject: [PATCH 08/19] Comment --- eng/pipelines/templates/steps/build-msi.yml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/eng/pipelines/templates/steps/build-msi.yml b/eng/pipelines/templates/steps/build-msi.yml index a0cbad81d89..89dc37615d2 100644 --- a/eng/pipelines/templates/steps/build-msi.yml +++ b/eng/pipelines/templates/steps/build-msi.yml @@ -21,13 +21,11 @@ steps: Write-Host "##vso[task.setvariable variable=MSI_VERSION_PARAM]/p:ProductVersion=$(MSI_VERSION)" Write-Host "##vso[task.setvariable variable=RELEASE_BUILD_PARAM]/p:ReleaseBuild=true" condition: ${{ parameters.Condition }} - displayName: Set MSI_PRODUCT_NAME_PARAM for release + displayName: Set MSI_PRODUCT_NAME_PARAM, MSI_VERSION_PARAM, and RELEASE_BUILD_PARAM for release - ${{ else }}: - # Leave version and product name empty for dev build (allows dev version to) - # install over any previously installed version. These variables must be set - # or DevOps will substitute in the literal string - # (e.g. '$(MSI_PRODUCT_NAME_PARAM)') into the msbuild command arguments. + # Set daily dev release specific parameters based on Build ID to ensure + # unique version and allow downgrades for easier testing. - pwsh: | $msiVersion = '$(MSI_VERSION)' $major = ($msiVersion -split '\.')[0] @@ -38,7 +36,7 @@ steps: Write-Host "##vso[task.setvariable variable=MSI_VERSION_PARAM]/p:ProductVersion=$major.$minor.$build" Write-Host "##vso[task.setvariable variable=RELEASE_BUILD_PARAM]" condition: ${{ parameters.Condition }} - displayName: Set MSI_PRODUCT_NAME_PARAM and MSI_VERSION_PARAM for dev release + displayName: Set MSI_PRODUCT_NAME_PARAM, MSI_VERSION_PARAM, and RELEASE_BUILD_PARAM for dev release - task: MSBuild@1 displayName: ${{ parameters.Title }} From e06393124e7820c28ceae160fbe36736f537a4fa Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Fri, 6 Mar 2026 10:25:03 -0800 Subject: [PATCH 09/19] Dev version number based on commit distance --- eng/pipelines/templates/steps/build-msi.yml | 17 ++++++++++------- eng/scripts/Get-MsiDevVersion.ps1 | 17 +++++++++++++++++ 2 files changed, 27 insertions(+), 7 deletions(-) create mode 100644 eng/scripts/Get-MsiDevVersion.ps1 diff --git a/eng/pipelines/templates/steps/build-msi.yml b/eng/pipelines/templates/steps/build-msi.yml index 89dc37615d2..748b739b0b1 100644 --- a/eng/pipelines/templates/steps/build-msi.yml +++ b/eng/pipelines/templates/steps/build-msi.yml @@ -24,16 +24,19 @@ steps: displayName: Set MSI_PRODUCT_NAME_PARAM, MSI_VERSION_PARAM, and RELEASE_BUILD_PARAM for release - ${{ else }}: - # Set daily dev release specific parameters based on Build ID to ensure - # unique version and allow downgrades for easier testing. + # Set daily dev release version - pwsh: | - $msiVersion = '$(MSI_VERSION)' - $major = ($msiVersion -split '\.')[0] - $minor = ($msiVersion -split '\.')[1] - $build = $(Build.BuildId) % 65536 + $since = (Get-Date).AddMonths(-6).ToString("yyyy-MM-dd") + Write-Host "Deepening git history since $since" + git fetch origin --shallow-since="$since" + + # Fetch tags now that the commits exist + git fetch origin --tags + + $devVersion = ./eng/scripts/Get-MsiDevVersion.ps1 Write-Host "##vso[task.setvariable variable=MSI_PRODUCT_NAME_PARAM]" - Write-Host "##vso[task.setvariable variable=MSI_VERSION_PARAM]/p:ProductVersion=$major.$minor.$build" + Write-Host "##vso[task.setvariable variable=MSI_VERSION_PARAM]/p:ProductVersion=$devVersion" Write-Host "##vso[task.setvariable variable=RELEASE_BUILD_PARAM]" condition: ${{ parameters.Condition }} displayName: Set MSI_PRODUCT_NAME_PARAM, MSI_VERSION_PARAM, and RELEASE_BUILD_PARAM for dev release diff --git a/eng/scripts/Get-MsiDevVersion.ps1 b/eng/scripts/Get-MsiDevVersion.ps1 new file mode 100644 index 00000000000..c8f5b6fb53c --- /dev/null +++ b/eng/scripts/Get-MsiDevVersion.ps1 @@ -0,0 +1,17 @@ +param( + [string] $PreviousReleaseTag = (git describe --tags --match 'azure-dev-cli_*' --abbrev=0) +) + +. "$PSScriptRoot../../common/scripts/common.ps1" + +$previousVersionString = $PreviousReleaseTag.Substring("azure-dev-cli_".Length) +$previousVersion = [AzureEngSemanticVersion]::ParseVersionString($previousVersionString) +Write-Host "Previous release version: $previousVersion" + +$commitDistance = git rev-list "$PreviousReleaseTag..HEAD" --count +Write-Host "Commit distance from last release: $commitDistance" + +$version = "$($previousVersion.Major).$($previousVersion.Minor).$($previousVersion.Patch + $commitDistance)" +Write-Host "New dev version: $version" + +return $version From 5db4c1e024175145416bd1d82dde5bc20248e151 Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Fri, 6 Mar 2026 14:32:25 -0800 Subject: [PATCH 10/19] MSI Version Strategy --- cli/azd/ci-build.ps1 | 4 +- eng/pipelines/templates/jobs/build-cli.yml | 6 ++ .../templates/jobs/cross-build-cli.yml | 5 ++ .../templates/stages/build-and-test.yml | 20 +++++- eng/pipelines/templates/steps/build-msi.yml | 17 +---- eng/scripts/Get-MsiDevVersion.ps1 | 70 +++++++++++++++---- 6 files changed, 93 insertions(+), 29 deletions(-) diff --git a/cli/azd/ci-build.ps1 b/cli/azd/ci-build.ps1 index a92915a8960..40e03446a1d 100644 --- a/cli/azd/ci-build.ps1 +++ b/cli/azd/ci-build.ps1 @@ -1,5 +1,6 @@ param( [string] $Version = (Get-Content "$PSScriptRoot/../version.txt"), + [string] $ExeVersion = (."$PSScriptRoot/../../eng/scripts/Get-MsiVersion.ps1" -CliVersion $Version), [string] $SourceVersion = (git rev-parse HEAD), [switch] $CodeCoverageEnabled, [switch] $BuildRecordMode, @@ -83,8 +84,7 @@ if ($IsWindows) { $VERSION_INFO_PATH = "$PSScriptRoot/versioninfo.json" - $exeFileVersion = ."$PSScriptRoot/../../eng/scripts/Get-MsiVersion.ps1" -CliVersion $Version - $splitExeFileVersion = $exeFileVersion -split '\.' + $splitExeFileVersion = $ExeVersion -split '\.' $versionInfo = Get-Content $VERSION_INFO_PATH | ConvertFrom-Json $versionInfo.FixedFileInfo.FileVersion.Major = [int]$splitExeFileVersion[0] diff --git a/eng/pipelines/templates/jobs/build-cli.yml b/eng/pipelines/templates/jobs/build-cli.yml index 4c9c0eb8eee..5a8b2022219 100644 --- a/eng/pipelines/templates/jobs/build-cli.yml +++ b/eng/pipelines/templates/jobs/build-cli.yml @@ -40,6 +40,10 @@ jobs: parameters: Condition: and(succeeded(), ne(variables['Skip.LiveTest'], 'true')) + # Sets variables based on release metadata, of primary import is + # MSI_VERSION. + - template: /eng/pipelines/templates/steps/set-metadata-variables.yml + - template: /eng/pipelines/templates/steps/set-cli-version-cd.yml - task: PowerShell@2 @@ -64,6 +68,7 @@ jobs: filePath: cli/azd/ci-build.ps1 arguments: >- -Version $(CLI_VERSION) + -ExeVersion $(MSI_VERSION) -SourceVersion $(Build.SourceVersion) -CodeCoverageEnabled -BuildRecordMode @@ -178,6 +183,7 @@ jobs: filePath: cli/azd/ci-build.ps1 arguments: >- -Version $(CLI_VERSION) + -ExeVersion $(MSI_VERSION) -SourceVersion $(Build.SourceVersion) -GitHubCopilotClientId "${{ parameters.ghCopilotClientId }}" -GitHubCopilotIntegrationId "${{ parameters.ghCopilotIntegrationId }}" diff --git a/eng/pipelines/templates/jobs/cross-build-cli.yml b/eng/pipelines/templates/jobs/cross-build-cli.yml index 649674db009..d648f067a0c 100644 --- a/eng/pipelines/templates/jobs/cross-build-cli.yml +++ b/eng/pipelines/templates/jobs/cross-build-cli.yml @@ -51,6 +51,10 @@ jobs: condition: and(succeeded(), eq(variables['GOOS'], 'windows')) displayName: Install goversioninfo (Windows x86_64) + # Set variables from build metadata artifact. Most interesting is + # MSI_VERSION used in Windows building. + - template: /eng/pipelines/templates/steps/set-metadata-variables.yml + - template: /eng/pipelines/templates/steps/set-cli-version-cd.yml - task: PowerShell@2 @@ -67,6 +71,7 @@ jobs: filePath: cli/azd/ci-build.ps1 arguments: >- -Version $(CLI_VERSION) + -ExeVersion $(MSI_VERSION) -SourceVersion $(Build.SourceVersion) -GitHubCopilotClientId "${{ parameters.ghCopilotClientId }}" -GitHubCopilotIntegrationId "${{ parameters.ghCopilotIntegrationId }}" diff --git a/eng/pipelines/templates/stages/build-and-test.yml b/eng/pipelines/templates/stages/build-and-test.yml index 6e01fc7fd0a..1de395b198c 100644 --- a/eng/pipelines/templates/stages/build-and-test.yml +++ b/eng/pipelines/templates/stages/build-and-test.yml @@ -228,6 +228,24 @@ stages: displayName: Show changelog content # Create release metadata + - ${{ if in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI') }}: + - pwsh: | + $since = (Get-Date).AddMonths(-6).ToString("yyyy-MM-dd") + Write-Host "Deepening git history since: $since" + git fetch --shallow-since="$since" --tags + + $msiVersion = & eng/scripts/Get-MsiDevVersion.ps1 + Write-Host "Using MSI version: $msiVersion" + Write-Host "##vso[task.setvariable variable=MSI_VERSION]$msiVersion" + displayName: Set MSI_VERSION for CI build + + - ${{ else }}: + - pwsh: | + $msiVersion = & eng/scripts/Get-MsiVersion.ps1 -CliVersion $cliVersion + Write-Host "Using MSI version: $msiVersion" + Write-Host "##vso[task.setvariable variable=MSI_VERSION]$msiVersion" + displayName: Set MSI_VERSION + - pwsh: | New-Item -ItemType Directory -Path release-metadata -Force # CLI Version may have been set by set-cli-version-cd.yml @@ -247,7 +265,7 @@ stages: $releaseMetadata = @{ cliVersion = $cliVersion; - msiVersion = (eng/scripts/Get-MsiVersion.ps1 -CliVersion $cliVersion); + msiVersion = "$(MSI_VERSION)"; isPublishingGa = $submitPackage; submitChocoPackage = $submitPackage; submitWingetPackage = $submitPackage; diff --git a/eng/pipelines/templates/steps/build-msi.yml b/eng/pipelines/templates/steps/build-msi.yml index 748b739b0b1..2db3601a7f4 100644 --- a/eng/pipelines/templates/steps/build-msi.yml +++ b/eng/pipelines/templates/steps/build-msi.yml @@ -18,28 +18,17 @@ steps: - ${{ if eq(parameters.ShouldBuildForRelease, 'true') }}: - pwsh: | Write-Host "##vso[task.setvariable variable=MSI_PRODUCT_NAME_PARAM]/p:ProductName=`"Azure Developer CLI`"" - Write-Host "##vso[task.setvariable variable=MSI_VERSION_PARAM]/p:ProductVersion=$(MSI_VERSION)" Write-Host "##vso[task.setvariable variable=RELEASE_BUILD_PARAM]/p:ReleaseBuild=true" condition: ${{ parameters.Condition }} - displayName: Set MSI_PRODUCT_NAME_PARAM, MSI_VERSION_PARAM, and RELEASE_BUILD_PARAM for release + displayName: Set MSI_PRODUCT_NAME_PARAM, and RELEASE_BUILD_PARAM for release - ${{ else }}: # Set daily dev release version - pwsh: | - $since = (Get-Date).AddMonths(-6).ToString("yyyy-MM-dd") - Write-Host "Deepening git history since $since" - git fetch origin --shallow-since="$since" - - # Fetch tags now that the commits exist - git fetch origin --tags - - $devVersion = ./eng/scripts/Get-MsiDevVersion.ps1 - Write-Host "##vso[task.setvariable variable=MSI_PRODUCT_NAME_PARAM]" - Write-Host "##vso[task.setvariable variable=MSI_VERSION_PARAM]/p:ProductVersion=$devVersion" Write-Host "##vso[task.setvariable variable=RELEASE_BUILD_PARAM]" condition: ${{ parameters.Condition }} - displayName: Set MSI_PRODUCT_NAME_PARAM, MSI_VERSION_PARAM, and RELEASE_BUILD_PARAM for dev release + displayName: Set MSI_PRODUCT_NAME_PARAM, and RELEASE_BUILD_PARAM for dev release - task: MSBuild@1 displayName: ${{ parameters.Title }} @@ -49,6 +38,6 @@ steps: msbuildArguments: >- /p:RunWixToolsOutOfProc=true /p:Configuration=Release + /p:ProductVersion=$(MSI_VERSION) $(MSI_PRODUCT_NAME_PARAM) - $(MSI_VERSION_PARAM) $(RELEASE_BUILD_PARAM) diff --git a/eng/scripts/Get-MsiDevVersion.ps1 b/eng/scripts/Get-MsiDevVersion.ps1 index c8f5b6fb53c..8b789e5d224 100644 --- a/eng/scripts/Get-MsiDevVersion.ps1 +++ b/eng/scripts/Get-MsiDevVersion.ps1 @@ -1,17 +1,63 @@ -param( - [string] $PreviousReleaseTag = (git describe --tags --match 'azure-dev-cli_*' --abbrev=0) -) - . "$PSScriptRoot../../common/scripts/common.ps1" -$previousVersionString = $PreviousReleaseTag.Substring("azure-dev-cli_".Length) -$previousVersion = [AzureEngSemanticVersion]::ParseVersionString($previousVersionString) -Write-Host "Previous release version: $previousVersion" +Set-StrictMode -Version 4 + +$tagPrefix = 'azure-dev-cli_' + +# Step 1: Most recent release tag +$mostRecentRelease = git describe --tags --match "${tagPrefix}*" --abbrev=0 HEAD +if ($LASTEXITCODE -ne 0 -or !$mostRecentRelease) { + throw "Could not find a release tag matching '${tagPrefix}*'" +} + +$mostRecentVersion = $mostRecentRelease.Substring($tagPrefix.Length) +$parsedMostRecent = [AzureEngSemanticVersion]::ParseVersionString($mostRecentVersion) +if (!$parsedMostRecent) { + throw "Could not parse version from tag '$mostRecentRelease'" +} + +Write-Host "Most recent release: $mostRecentRelease (minor=$($parsedMostRecent.Minor))" + +# Step 2: First release tag that introduced the current minor version +$currentRef = "$mostRecentRelease^" +$minorVersionRevRelease = $mostRecentRelease +while ($true) { + $candidate = git describe --tags --match "${tagPrefix}*" --abbrev=0 $currentRef 2>$null + if ($LASTEXITCODE -ne 0 -or !$candidate) { + # The current tag introduces the minor version + break + } + + $versionCandidate = $candidate.Substring($tagPrefix.Length) + $parsedCandidate = [AzureEngSemanticVersion]::ParseVersionString($versionCandidate) + + if (!$parsedCandidate) { + throw "Could not parse version from tag '$candidate'" + } + + if ($parsedCandidate.Minor -ne $parsedMostRecent.Minor) { + break + } + + $minorVersionRevRelease = $candidate + $currentRef = "$candidate^" +} + +Write-Host "Minor intro release: $minorVersionRevRelease" + +# Step 3: MSI version for minor rev release + commit distance +$minorRevVersion = $minorVersionRevRelease.Substring($tagPrefix.Length) +$msiVersionBase = & "$PSScriptRoot/Get-MsiVersion.ps1" -CliVersion $minorRevVersion + +$commitDistance = [int](git rev-list --count "$minorVersionRevRelease..HEAD") + +$parts = $msiVersionBase -split '\.' +$patch = [int]$parts[2] + $commitDistance -$commitDistance = git rev-list "$PreviousReleaseTag..HEAD" --count -Write-Host "Commit distance from last release: $commitDistance" +$devVersion = "$($parts[0]).$($parts[1]).$patch" -$version = "$($previousVersion.Major).$($previousVersion.Minor).$($previousVersion.Patch + $commitDistance)" -Write-Host "New dev version: $version" +Write-Host "MSI version (base): $msiVersionBase" +Write-Host "Commit distance: $commitDistance" +Write-Host "Computed dev version: $devVersion" -return $version +return $devVersion From 031fe52cad860a9db291e181d2c870e518c66b3c Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Fri, 6 Mar 2026 14:33:45 -0800 Subject: [PATCH 11/19] azd.wxs version enforcement --- cli/installer/windows/azd.wxs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/cli/installer/windows/azd.wxs b/cli/installer/windows/azd.wxs index de47114507d..db3815d11b7 100644 --- a/cli/installer/windows/azd.wxs +++ b/cli/installer/windows/azd.wxs @@ -54,16 +54,10 @@ - - - - - - - - - - + + + + From 7ae8218d8ac242fe50d65592192d4f8d34fbe977 Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Fri, 6 Mar 2026 14:48:05 -0800 Subject: [PATCH 12/19] CLI_VERSION --- eng/pipelines/templates/stages/build-and-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eng/pipelines/templates/stages/build-and-test.yml b/eng/pipelines/templates/stages/build-and-test.yml index 1de395b198c..b01663ff08d 100644 --- a/eng/pipelines/templates/stages/build-and-test.yml +++ b/eng/pipelines/templates/stages/build-and-test.yml @@ -241,7 +241,7 @@ stages: - ${{ else }}: - pwsh: | - $msiVersion = & eng/scripts/Get-MsiVersion.ps1 -CliVersion $cliVersion + $msiVersion = & eng/scripts/Get-MsiVersion.ps1 -CliVersion "$(CLI_VERSION)" Write-Host "Using MSI version: $msiVersion" Write-Host "##vso[task.setvariable variable=MSI_VERSION]$msiVersion" displayName: Set MSI_VERSION From f1c40ea8f5896bc384f0eec2dd0c2944a048d0be Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Fri, 6 Mar 2026 14:52:39 -0800 Subject: [PATCH 13/19] Handle step dependencies --- eng/pipelines/templates/jobs/build-cli.yml | 8 +++++--- .../templates/jobs/cross-build-cli.yml | 8 +++++--- .../templates/stages/build-and-test.yml | 18 +----------------- .../templates/steps/set-msi-version.yml | 18 ++++++++++++++++++ 4 files changed, 29 insertions(+), 23 deletions(-) create mode 100644 eng/pipelines/templates/steps/set-msi-version.yml diff --git a/eng/pipelines/templates/jobs/build-cli.yml b/eng/pipelines/templates/jobs/build-cli.yml index 5a8b2022219..5eb11ee331b 100644 --- a/eng/pipelines/templates/jobs/build-cli.yml +++ b/eng/pipelines/templates/jobs/build-cli.yml @@ -40,9 +40,11 @@ jobs: parameters: Condition: and(succeeded(), ne(variables['Skip.LiveTest'], 'true')) - # Sets variables based on release metadata, of primary import is - # MSI_VERSION. - - template: /eng/pipelines/templates/steps/set-metadata-variables.yml + - ${{ if eq(parameters.OS, 'windows') }}: + - template: /eng/pipelines/templates/steps/set-msi-version.yml + - ${{ else }}: + - pwsh: Write-Host "###vso[task.setvariable variable=MSI_VERSION]" + displayName: Set MSI_VERSION for non-windows build - template: /eng/pipelines/templates/steps/set-cli-version-cd.yml diff --git a/eng/pipelines/templates/jobs/cross-build-cli.yml b/eng/pipelines/templates/jobs/cross-build-cli.yml index d648f067a0c..a242b22b3c3 100644 --- a/eng/pipelines/templates/jobs/cross-build-cli.yml +++ b/eng/pipelines/templates/jobs/cross-build-cli.yml @@ -51,9 +51,11 @@ jobs: condition: and(succeeded(), eq(variables['GOOS'], 'windows')) displayName: Install goversioninfo (Windows x86_64) - # Set variables from build metadata artifact. Most interesting is - # MSI_VERSION used in Windows building. - - template: /eng/pipelines/templates/steps/set-metadata-variables.yml + - ${{ if eq(parameters.OS, 'windows') }}: + - template: /eng/pipelines/templates/steps/set-msi-version.yml + - ${{ else }}: + - pwsh: Write-Host "###vso[task.setvariable variable=MSI_VERSION]" + displayName: Set MSI_VERSION for non-windows build - template: /eng/pipelines/templates/steps/set-cli-version-cd.yml diff --git a/eng/pipelines/templates/stages/build-and-test.yml b/eng/pipelines/templates/stages/build-and-test.yml index b01663ff08d..d64697ea4fe 100644 --- a/eng/pipelines/templates/stages/build-and-test.yml +++ b/eng/pipelines/templates/stages/build-and-test.yml @@ -228,23 +228,7 @@ stages: displayName: Show changelog content # Create release metadata - - ${{ if in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI') }}: - - pwsh: | - $since = (Get-Date).AddMonths(-6).ToString("yyyy-MM-dd") - Write-Host "Deepening git history since: $since" - git fetch --shallow-since="$since" --tags - - $msiVersion = & eng/scripts/Get-MsiDevVersion.ps1 - Write-Host "Using MSI version: $msiVersion" - Write-Host "##vso[task.setvariable variable=MSI_VERSION]$msiVersion" - displayName: Set MSI_VERSION for CI build - - - ${{ else }}: - - pwsh: | - $msiVersion = & eng/scripts/Get-MsiVersion.ps1 -CliVersion "$(CLI_VERSION)" - Write-Host "Using MSI version: $msiVersion" - Write-Host "##vso[task.setvariable variable=MSI_VERSION]$msiVersion" - displayName: Set MSI_VERSION + - template: /eng/pipelines/templates/steps/set-msi-version.yml - pwsh: | New-Item -ItemType Directory -Path release-metadata -Force diff --git a/eng/pipelines/templates/steps/set-msi-version.yml b/eng/pipelines/templates/steps/set-msi-version.yml new file mode 100644 index 00000000000..d9088e0fd51 --- /dev/null +++ b/eng/pipelines/templates/steps/set-msi-version.yml @@ -0,0 +1,18 @@ +steps: + - ${{ if in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI') }}: + - pwsh: | + $since = (Get-Date).AddMonths(-6).ToString("yyyy-MM-dd") + Write-Host "Deepening git history since: $since" + git fetch --shallow-since="$since" --tags + + $msiVersion = & eng/scripts/Get-MsiDevVersion.ps1 + Write-Host "Using MSI version: $msiVersion" + Write-Host "##vso[task.setvariable variable=MSI_VERSION]$msiVersion" + displayName: Set MSI_VERSION for CI build + + - ${{ else }}: + - pwsh: | + $msiVersion = & eng/scripts/Get-MsiVersion.ps1 -CliVersion "$(CLI_VERSION)" + Write-Host "Using MSI version: $msiVersion" + Write-Host "##vso[task.setvariable variable=MSI_VERSION]$msiVersion" + displayName: Set MSI_VERSION \ No newline at end of file From 664c029a14afd64444b980e1bf674f591c22410f Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Fri, 6 Mar 2026 15:28:35 -0800 Subject: [PATCH 14/19] Sequence --- eng/pipelines/templates/jobs/cross-build-cli.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/eng/pipelines/templates/jobs/cross-build-cli.yml b/eng/pipelines/templates/jobs/cross-build-cli.yml index a242b22b3c3..6130667da1d 100644 --- a/eng/pipelines/templates/jobs/cross-build-cli.yml +++ b/eng/pipelines/templates/jobs/cross-build-cli.yml @@ -51,12 +51,6 @@ jobs: condition: and(succeeded(), eq(variables['GOOS'], 'windows')) displayName: Install goversioninfo (Windows x86_64) - - ${{ if eq(parameters.OS, 'windows') }}: - - template: /eng/pipelines/templates/steps/set-msi-version.yml - - ${{ else }}: - - pwsh: Write-Host "###vso[task.setvariable variable=MSI_VERSION]" - displayName: Set MSI_VERSION for non-windows build - - template: /eng/pipelines/templates/steps/set-cli-version-cd.yml - task: PowerShell@2 @@ -66,6 +60,12 @@ jobs: filePath: eng/scripts/Set-CliVersionVariable.ps1 displayName: Set CLI_VERSION + - ${{ if eq(parameters.OS, 'windows') }}: + - template: /eng/pipelines/templates/steps/set-msi-version.yml + - ${{ else }}: + - pwsh: Write-Host "###vso[task.setvariable variable=MSI_VERSION]" + displayName: Set MSI_VERSION for non-windows build + - task: PowerShell@2 inputs: pwsh: true From 5f557b4440d998bb1b34dec1671b1d97c971aa79 Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Fri, 6 Mar 2026 15:32:48 -0800 Subject: [PATCH 15/19] Include build-cli.yml --- eng/pipelines/templates/jobs/build-cli.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/eng/pipelines/templates/jobs/build-cli.yml b/eng/pipelines/templates/jobs/build-cli.yml index 5eb11ee331b..1615f225249 100644 --- a/eng/pipelines/templates/jobs/build-cli.yml +++ b/eng/pipelines/templates/jobs/build-cli.yml @@ -40,12 +40,6 @@ jobs: parameters: Condition: and(succeeded(), ne(variables['Skip.LiveTest'], 'true')) - - ${{ if eq(parameters.OS, 'windows') }}: - - template: /eng/pipelines/templates/steps/set-msi-version.yml - - ${{ else }}: - - pwsh: Write-Host "###vso[task.setvariable variable=MSI_VERSION]" - displayName: Set MSI_VERSION for non-windows build - - template: /eng/pipelines/templates/steps/set-cli-version-cd.yml - task: PowerShell@2 @@ -55,6 +49,12 @@ jobs: filePath: eng/scripts/Set-CliVersionVariable.ps1 displayName: Set CLI_VERSION + - ${{ if eq(parameters.OS, 'windows') }}: + - template: /eng/pipelines/templates/steps/set-msi-version.yml + - ${{ else }}: + - pwsh: Write-Host "###vso[task.setvariable variable=MSI_VERSION]" + displayName: Set MSI_VERSION for non-windows build + - task: PowerShell@2 inputs: pwsh: true From 0764a2915731ed60d37f21cb800e784e9c8b42f5 Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Fri, 6 Mar 2026 15:37:45 -0800 Subject: [PATCH 16/19] Quotes --- eng/pipelines/templates/jobs/build-cli.yml | 12 ++++++------ eng/pipelines/templates/jobs/cross-build-cli.yml | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/eng/pipelines/templates/jobs/build-cli.yml b/eng/pipelines/templates/jobs/build-cli.yml index 1615f225249..232cba51673 100644 --- a/eng/pipelines/templates/jobs/build-cli.yml +++ b/eng/pipelines/templates/jobs/build-cli.yml @@ -69,9 +69,9 @@ jobs: targetType: filePath filePath: cli/azd/ci-build.ps1 arguments: >- - -Version $(CLI_VERSION) - -ExeVersion $(MSI_VERSION) - -SourceVersion $(Build.SourceVersion) + -Version "$(CLI_VERSION)" + -ExeVersion "$(MSI_VERSION)" + -SourceVersion "$(Build.SourceVersion)" -CodeCoverageEnabled -BuildRecordMode -GitHubCopilotClientId "${{ parameters.ghCopilotClientId }}" @@ -184,9 +184,9 @@ jobs: targetType: filePath filePath: cli/azd/ci-build.ps1 arguments: >- - -Version $(CLI_VERSION) - -ExeVersion $(MSI_VERSION) - -SourceVersion $(Build.SourceVersion) + -Version "$(CLI_VERSION)" + -ExeVersion "$(MSI_VERSION)" + -SourceVersion "$(Build.SourceVersion)" -GitHubCopilotClientId "${{ parameters.ghCopilotClientId }}" -GitHubCopilotIntegrationId "${{ parameters.ghCopilotIntegrationId }}" workingDirectory: cli/azd diff --git a/eng/pipelines/templates/jobs/cross-build-cli.yml b/eng/pipelines/templates/jobs/cross-build-cli.yml index 6130667da1d..072a777eaff 100644 --- a/eng/pipelines/templates/jobs/cross-build-cli.yml +++ b/eng/pipelines/templates/jobs/cross-build-cli.yml @@ -72,9 +72,9 @@ jobs: targetType: filePath filePath: cli/azd/ci-build.ps1 arguments: >- - -Version $(CLI_VERSION) - -ExeVersion $(MSI_VERSION) - -SourceVersion $(Build.SourceVersion) + -Version "$(CLI_VERSION)" + -ExeVersion "$(MSI_VERSION)" + -SourceVersion "$(Build.SourceVersion)" -GitHubCopilotClientId "${{ parameters.ghCopilotClientId }}" -GitHubCopilotIntegrationId "${{ parameters.ghCopilotIntegrationId }}" workingDirectory: cli/azd From 38b902e37e3d4c3d1d8cc8a374c8834e12cf790f Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Fri, 6 Mar 2026 15:58:03 -0800 Subject: [PATCH 17/19] Cleanup, more conditions --- eng/pipelines/templates/jobs/build-cli.yml | 5 ----- eng/pipelines/templates/steps/set-msi-version.yml | 4 ++-- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/eng/pipelines/templates/jobs/build-cli.yml b/eng/pipelines/templates/jobs/build-cli.yml index 232cba51673..30a1a478c4d 100644 --- a/eng/pipelines/templates/jobs/build-cli.yml +++ b/eng/pipelines/templates/jobs/build-cli.yml @@ -79,11 +79,6 @@ jobs: workingDirectory: cli/azd displayName: Build Go Binary (For tests) - - pwsh: | - eng/scripts/Get-MsiVersion.ps1 -DevOpsOutput - displayName: Set MSI_VERSION - condition: and(succeeded(), eq(variables['BuildTestMsi'], 'true')) - - template: /eng/pipelines/templates/steps/build-msi.yml parameters: Title: Build Test MSI diff --git a/eng/pipelines/templates/steps/set-msi-version.yml b/eng/pipelines/templates/steps/set-msi-version.yml index d9088e0fd51..1216ef78651 100644 --- a/eng/pipelines/templates/steps/set-msi-version.yml +++ b/eng/pipelines/templates/steps/set-msi-version.yml @@ -1,5 +1,5 @@ steps: - - ${{ if in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI') }}: + - ${{ if or(in(variables['BuildReasonOverride'], 'IndividualCI', 'BatchedCI'), and( eq('', variables['BuildReasonOverride']), in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI'))) }}: - pwsh: | $since = (Get-Date).AddMonths(-6).ToString("yyyy-MM-dd") Write-Host "Deepening git history since: $since" @@ -15,4 +15,4 @@ steps: $msiVersion = & eng/scripts/Get-MsiVersion.ps1 -CliVersion "$(CLI_VERSION)" Write-Host "Using MSI version: $msiVersion" Write-Host "##vso[task.setvariable variable=MSI_VERSION]$msiVersion" - displayName: Set MSI_VERSION \ No newline at end of file + displayName: Set MSI_VERSION From 7ae7366ec41f3ad170c70e52083b1773effc206a Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Fri, 6 Mar 2026 16:27:19 -0800 Subject: [PATCH 18/19] Runtime conditions --- .../templates/steps/set-msi-version.yml | 37 +++++++++++-------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/eng/pipelines/templates/steps/set-msi-version.yml b/eng/pipelines/templates/steps/set-msi-version.yml index 1216ef78651..be8d0142c8d 100644 --- a/eng/pipelines/templates/steps/set-msi-version.yml +++ b/eng/pipelines/templates/steps/set-msi-version.yml @@ -1,18 +1,25 @@ steps: - - ${{ if or(in(variables['BuildReasonOverride'], 'IndividualCI', 'BatchedCI'), and( eq('', variables['BuildReasonOverride']), in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI'))) }}: - - pwsh: | - $since = (Get-Date).AddMonths(-6).ToString("yyyy-MM-dd") - Write-Host "Deepening git history since: $since" - git fetch --shallow-since="$since" --tags + - pwsh: | + $since = (Get-Date).AddMonths(-6).ToString("yyyy-MM-dd") + Write-Host "Deepening git history since: $since" + git fetch --shallow-since="$since" --tags - $msiVersion = & eng/scripts/Get-MsiDevVersion.ps1 - Write-Host "Using MSI version: $msiVersion" - Write-Host "##vso[task.setvariable variable=MSI_VERSION]$msiVersion" - displayName: Set MSI_VERSION for CI build + $msiVersion = & eng/scripts/Get-MsiDevVersion.ps1 + Write-Host "Using MSI version: $msiVersion" + Write-Host "##vso[task.setvariable variable=MSI_VERSION]$msiVersion" + displayName: Set MSI_VERSION for CI build + condition: >- + or( + in(variables['BuildReasonOverride'], 'IndividualCI', 'BatchedCI'), + and( + eq('', variables['BuildReasonOverride']), + in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI') + ) + ) - - ${{ else }}: - - pwsh: | - $msiVersion = & eng/scripts/Get-MsiVersion.ps1 -CliVersion "$(CLI_VERSION)" - Write-Host "Using MSI version: $msiVersion" - Write-Host "##vso[task.setvariable variable=MSI_VERSION]$msiVersion" - displayName: Set MSI_VERSION + - pwsh: | + $msiVersion = & eng/scripts/Get-MsiVersion.ps1 -CliVersion "$(CLI_VERSION)" + Write-Host "Using MSI version: $msiVersion" + Write-Host "##vso[task.setvariable variable=MSI_VERSION]$msiVersion" + displayName: Set MSI_VERSION + condition: eq('', variables['MSI_VERSION']) From fdd761af823ca11494a53eccb5b838a4315456b7 Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Mon, 9 Mar 2026 11:04:51 -0700 Subject: [PATCH 19/19] Documentation --- eng/pipelines/templates/steps/set-msi-version.yml | 2 ++ eng/scripts/Get-MsiDevVersion.ps1 | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/eng/pipelines/templates/steps/set-msi-version.yml b/eng/pipelines/templates/steps/set-msi-version.yml index be8d0142c8d..f326c9bc9b9 100644 --- a/eng/pipelines/templates/steps/set-msi-version.yml +++ b/eng/pipelines/templates/steps/set-msi-version.yml @@ -1,4 +1,6 @@ steps: + # Deepen the commit history by 6 months to ensure that the + # minor-version-introducing tag is present. - pwsh: | $since = (Get-Date).AddMonths(-6).ToString("yyyy-MM-dd") Write-Host "Deepening git history since: $since" diff --git a/eng/scripts/Get-MsiDevVersion.ps1 b/eng/scripts/Get-MsiDevVersion.ps1 index 8b789e5d224..b222d4af4f5 100644 --- a/eng/scripts/Get-MsiDevVersion.ps1 +++ b/eng/scripts/Get-MsiDevVersion.ps1 @@ -1,3 +1,15 @@ +# This script returns a dev MSI version by finding the most recent release tag, +# then finding the first release tag that introduced the current minor version, +# and then calculating the "release distance" from that tag to the current +# commit. +# +# The MSI version is of the form: ... +# This ensures a monotonically increasing version number for each build on the +# "main" branch. +# +# By default, the git history is assumed to be deepened by 6 months in the CI +# environment to ensure that a minor intro release is found. + . "$PSScriptRoot../../common/scripts/common.ps1" Set-StrictMode -Version 4