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/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/cli/installer/windows/azd.wxs b/cli/installer/windows/azd.wxs
index 6f241ab3173..db3815d11b7 100644
--- a/cli/installer/windows/azd.wxs
+++ b/cli/installer/windows/azd.wxs
@@ -12,16 +12,34 @@
-
+
-
-
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
@@ -36,14 +54,13 @@
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
diff --git a/eng/pipelines/templates/jobs/build-cli.yml b/eng/pipelines/templates/jobs/build-cli.yml
index 4c9c0eb8eee..30a1a478c4d 100644
--- a/eng/pipelines/templates/jobs/build-cli.yml
+++ b/eng/pipelines/templates/jobs/build-cli.yml
@@ -49,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
@@ -63,8 +69,9 @@ jobs:
targetType: filePath
filePath: cli/azd/ci-build.ps1
arguments: >-
- -Version $(CLI_VERSION)
- -SourceVersion $(Build.SourceVersion)
+ -Version "$(CLI_VERSION)"
+ -ExeVersion "$(MSI_VERSION)"
+ -SourceVersion "$(Build.SourceVersion)"
-CodeCoverageEnabled
-BuildRecordMode
-GitHubCopilotClientId "${{ parameters.ghCopilotClientId }}"
@@ -72,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
@@ -177,8 +179,9 @@ jobs:
targetType: filePath
filePath: cli/azd/ci-build.ps1
arguments: >-
- -Version $(CLI_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 649674db009..072a777eaff 100644
--- a/eng/pipelines/templates/jobs/cross-build-cli.yml
+++ b/eng/pipelines/templates/jobs/cross-build-cli.yml
@@ -60,14 +60,21 @@ 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
targetType: filePath
filePath: cli/azd/ci-build.ps1
arguments: >-
- -Version $(CLI_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/stages/build-and-test.yml b/eng/pipelines/templates/stages/build-and-test.yml
index 6e01fc7fd0a..d64697ea4fe 100644
--- a/eng/pipelines/templates/stages/build-and-test.yml
+++ b/eng/pipelines/templates/stages/build-and-test.yml
@@ -228,6 +228,8 @@ stages:
displayName: Show changelog content
# Create release metadata
+ - template: /eng/pipelines/templates/steps/set-msi-version.yml
+
- pwsh: |
New-Item -ItemType Directory -Path release-metadata -Force
# CLI Version may have been set by set-cli-version-cd.yml
@@ -247,7 +249,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/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')) }}:
diff --git a/eng/pipelines/templates/steps/build-msi.yml b/eng/pipelines/templates/steps/build-msi.yml
index a4f4970556e..2db3601a7f4 100644
--- a/eng/pipelines/templates/steps/build-msi.yml
+++ b/eng/pipelines/templates/steps/build-msi.yml
@@ -18,20 +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 for release
+ displayName: Set MSI_PRODUCT_NAME_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 version
- pwsh: |
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=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, and RELEASE_BUILD_PARAM for dev release
- task: MSBuild@1
displayName: ${{ parameters.Title }}
@@ -41,5 +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/pipelines/templates/steps/set-msi-version.yml b/eng/pipelines/templates/steps/set-msi-version.yml
new file mode 100644
index 00000000000..f326c9bc9b9
--- /dev/null
+++ b/eng/pipelines/templates/steps/set-msi-version.yml
@@ -0,0 +1,27 @@
+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"
+ 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
+ condition: >-
+ or(
+ in(variables['BuildReasonOverride'], 'IndividualCI', 'BatchedCI'),
+ and(
+ eq('', variables['BuildReasonOverride']),
+ in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI')
+ )
+ )
+
+ - 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'])
diff --git a/eng/scripts/Get-MsiDevVersion.ps1 b/eng/scripts/Get-MsiDevVersion.ps1
new file mode 100644
index 00000000000..b222d4af4f5
--- /dev/null
+++ b/eng/scripts/Get-MsiDevVersion.ps1
@@ -0,0 +1,75 @@
+# 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
+
+$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
+
+$devVersion = "$($parts[0]).$($parts[1]).$patch"
+
+Write-Host "MSI version (base): $msiVersionBase"
+Write-Host "Commit distance: $commitDistance"
+Write-Host "Computed dev version: $devVersion"
+
+return $devVersion