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