Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 57 additions & 4 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,12 @@ jobs:

- job: BuildWindowsMSI
displayName: Build Windows MSI
strategy:
matrix:
x86:
Platform: x86
x64:
Platform: x64

dependsOn: ExtractMetadata
condition: succeeded()
Expand All @@ -200,6 +206,7 @@ jobs:


- script: |
set /p ARCH=$(Platform)
set /p CLI_VERSION=<$(System.ArtifactsDirectory)/metadata/version
set

Expand All @@ -209,12 +216,12 @@ jobs:
- task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0
displayName: 'SBOM'
inputs:
BuildDropPath: 'build_scripts/windows/out/'
BuildDropPath: 'build_scripts/windows/out/$(Platform)'

- task: PublishPipelineArtifact@0
displayName: 'Publish Artifact: MSI'
inputs:
TargetPath: 'build_scripts/windows/out/'
TargetPath: 'build_scripts/windows/out/$(Platform)'
ArtifactName: msi

- job: TestWindowsMSI
Expand All @@ -238,7 +245,53 @@ jobs:
artifactName: msi

- task: PowerShell@2
displayName: Install and Load CLI
displayName: Install and Load x86 CLI
inputs:
targetType: inline
script: |
if (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
# Start another Powershell process as Admin and execute this script again
$arguments = "& '" +$myinvocation.mycommand.definition + "'"
Start-Process powershell -Verb runAs -ArgumentList $arguments
# Stop if the PowerShell is not run as Admin
Break
}
# The following are executed by elevated PowerShell
az --version

$InstallArgs = @(
"/i"
"`"$env:SYSTEM_ARTIFACTSDIRECTORY\msi\x86\Microsoft Azure CLI.msi`""
"/q"
"/norestart"
"/l*v"
".\install_logs.txt"
)
$pre_installed_version=az version --query '\"azure-cli\"' -o tsv
$to_be_installed_version=Get-Content $(System.ArtifactsDirectory)/metadata/version
if ($pre_installed_version -eq $to_be_installed_version){
# See https://docs.microsoft.com/windows/win32/msi/reinstallmode about options of REINSTALLMODE
$reinstall_option="REINSTALL=ALL REINSTALLMODE=emus"
$InstallArgs += $reinstall_option
}
Start-Process "msiexec.exe" -ArgumentList $InstallArgs -Wait -NoNewWindow
$install_time=Measure-Command {Start-Process "msiexec.exe" -ArgumentList $InstallArgs -Wait -NoNewWindow} | select -expand TotalSeconds
$installed_version=az version --query '\"azure-cli\"' -o tsv
if ($installed_version -ne $to_be_installed_version){
echo "The MSI failed to install."
Exit 1
}
echo 'Install time(seconds):' $install_time
az --version
# Test bundled pip with extension installation
az extension add -n rdbms-connect
az self-test

Get-Content .\install_logs.txt

- task: PowerShell@2
# Test install (upgrade of x86) of x64 MSI as well.
displayName: Install and Load x64 CLI
inputs:
targetType: inline
script: |
Expand All @@ -254,7 +307,7 @@ jobs:

$InstallArgs = @(
"/i"
"`"$env:SYSTEM_ARTIFACTSDIRECTORY\msi\Microsoft Azure CLI.msi`""
"`"$env:SYSTEM_ARTIFACTSDIRECTORY\msi\x64\Microsoft Azure CLI.msi`""
"/q"
"/norestart"
"/l*v"
Expand Down
53 changes: 45 additions & 8 deletions build_scripts/windows/Product.wxs
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,55 @@
<?define ProductAuthor = "Microsoft Corporation" ?>
<?define ProductResources = ".\resources\" ?>

<?if $(var.Platform) = "x64" ?>
<?define AzureCliRegistryGuid = "90b6367e-4e15-4313-af33-a0ef459a56fc" ?>
<?define AzureCliSystemPathGuid = "12c8f0dc-0475-4814-93b6-fcbe82e89532" ?>
<?define AzureCliVersionGuid = "4df4882f-4604-4cdc-a0b4-bffc4b8c5967" ?>
<?define ProgramFilesFolder = "ProgramFiles64Folder" ?>
<?define RemoveAzureCLIFolderGuid = "7ce8a521-7429-4ded-9cce-0948e393cc83" ?>
<?define RemoveCLIFolderGuid = "59fb416c-91d0-49aa-a185-d4330c435a93" ?>
<?define UpgradeCode = "90762fec-9554-4729-a107-c6a8ea316698" ?>

<?elseif $(var.Platform) = "x86" ?>
<?define AzureCliRegistryGuid = "BDEEE50E-70D3-4990-BFF9-FCF8114AA701" ?>
<?define AzureCliSystemPathGuid = "3B60CA67-DB92-465A-BC96-3CA6CAEFF41B" ?>
<?define AzureCliVersionGuid = "A5647E93-02B7-4CED-802A-DDD7416E0231" ?>
<?define ProgramFilesFolder = "ProgramFilesFolder" ?>
<?define RemoveAzureCLIFolderGuid = "6AF97701-6A7A-4292-95D6-ED34CD4C97C0" ?>
<?define RemoveCLIFolderGuid = "24DA394D-B03D-4623-A0D9-852DB51EF9F4" ?>
<?define UpgradeCode = "dff82af0-3f95-4ac9-8efd-948604fdb028" ?>

<?else ?>
<?error Unsupported platform "$(var.Platform)" ?>
<?endif ?>

<Product Id="*"
Name="$(var.ProductName)"
Copy link
Member

@jiasli jiasli Jul 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some programs attach x86 and x64 to the product name. Some don't:

image

  1. Shall we do the same and change $(var.ProductName) to include $(var.Platform) too?
  2. Shall we allow 32-bit and 64-bit MSIs to be installed side-by-side? I don't feel this is necessary as there can be only one active az.cmd on PATH.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shall we do the same and change $(var.ProductName) too?

Sure I can do that.

Shall we allow 32-bit and 64-bit MSIs to be installed side-by-side? I don't feel this is necessary as there can be only one active az.cmd on PATH.

I don't recommend it. 1) what purpose does it serve? These are EXEs where, unlike libraries e.g., DLLs, architecture doesn't typically matter. 32- and 64-bit code can call any architecture of an EXE. It would be a UX problem because,

  1. Depending on the ordering of updates to PATH, customer will call the first or last architecture installed. It's not deterministic.
  2. Every time there's an update, the ~15-20 minute upgrade of 1 MSI will now be doubled.

And all that for what reason? Again, it's an EXE (well, effectively - an executable, more specifically) so architecture doesn't matter. SxS just unnecessarily complicates things in this case.

Copy link
Member

@jiasli jiasli Jul 20, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Microsoft products usually use (x86) and (x64). Python uses (32-bit) and (64-bit). I an not sure which convention we should follow.

image

Language="1033"
Version="$(var.ProductVersion)"
Manufacturer="$(var.ProductAuthor)"
UpgradeCode="dff82af0-3f95-4ac9-8efd-948604fdb028">
UpgradeCode="$(var.UpgradeCode)">

<Package InstallerVersion="200"
Compressed="yes"
InstallScope="perMachine" />

<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." Schedule="afterInstallExecute" />
<Upgrade Id="$(var.UpgradeCode)">
<UpgradeVersion Property="WIX_UPGRADE_DETECTED" Maximum="$(var.ProductVersion)" IncludeMinimum="no" MigrateFeatures="yes" />
<UpgradeVersion Property="WIX_DOWNGRADE_DETECTED" Minimum="$(var.ProductVersion)" IncludeMinimum="no" OnlyDetect="yes" />
</Upgrade>
<Condition Message="A newer version of [ProductName] is already installed.">NOT WIX_DOWNGRADE_DETECTED</Condition>
<InstallExecuteSequence>
<RemoveExistingProducts After="InstallExecute" />
</InstallExecuteSequence>

<!-- New product architectures should upgrade the original x86 product - even of the same version. -->
<?if $(var.UpgradeCode) != "dff82af0-3f95-4ac9-8efd-948604fdb028" ?>
<Upgrade Id="dff82af0-3f95-4ac9-8efd-948604fdb028">
<UpgradeVersion Property="WIX_UPGRADE_DETECTED" Maximum="$(var.ProductVersion)" IncludeMinimum="yes" MigrateFeatures="yes" />
<UpgradeVersion Property="WIX_DOWNGRADE_DETECTED" Minimum="$(var.ProductVersion)" IncludeMinimum="no" OnlyDetect="yes" />
</Upgrade>
<?endif ?>

<Media Id="1" Cabinet="WindowsAzureCLI.cab" EmbedCab="yes" CompressionLevel="high" />

Expand Down Expand Up @@ -61,7 +98,7 @@

<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="$(var.ProgramFilesFolder)">
<Directory Id="SDKFOLDER" Name="Microsoft SDKs">
<Directory Id="AZUREFOLDER" Name="Azure">
<Directory Id="AZURECLIFOLDER" Name="CLI2">
Expand All @@ -78,17 +115,17 @@
<ComponentGroup Id="AzureCliSettingsGroup">
<Component Id="RemoveCLIFolder"
Directory="DynamicCliDir"
Guid="{24DA394D-B03D-4623-A0D9-852DB51EF9F4}">
Guid="$(var.RemoveCLIFolderGuid)">
<RemoveFolder Id="DynamicCliDir" On="uninstall" />
</Component>
<Component Id="RemoveAzureCLIFolder"
Directory="AZURECLIFOLDER"
Guid="{6AF97701-6A7A-4292-95D6-ED34CD4C97C0}">
Guid="$(var.RemoveAzureCLIFolderGuid)">
<RemoveFolder Id="AZURECLIFOLDER" On="uninstall" />
</Component>
<Component Id="AzureCliSystemPath"
Directory="DynamicCliDir"
Guid="{3B60CA67-DB92-465A-BC96-3CA6CAEFF41B}">
Guid="$(var.AzureCliSystemPathGuid)">
<Environment Id="AzureCliAddedToPATH"
Name="PATH"
Value="[DynamicCliDir]wbin"
Expand All @@ -100,7 +137,7 @@
</Component>
<Component Id="AzureCliRegistry"
Directory="DynamicCliDir"
Guid="{BDEEE50E-70D3-4990-BFF9-FCF8114AA701}">
Guid="$(var.AzureCliRegistryGuid)">
<RegistryValue Root="HKCU"
Key="Software\Microsoft\$(var.ProductName)"
Name="installed"
Expand All @@ -110,7 +147,7 @@
</Component>
<Component Id="AzureCliVersion"
Directory="DynamicCliDir"
Guid="{A5647E93-02B7-4CED-802A-DDD7416E0231}">
Guid="$(var.AzureCliVersionGuid)">
<RegistryValue Root="HKLM"
Key="Software\Microsoft\$(var.ProductName)"
Name="version"
Expand Down
6 changes: 6 additions & 0 deletions build_scripts/windows/azure-cli.sln
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,18 @@ Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x86 = Debug|x86
Release|x86 = Release|x86
Debug|x64 = Debug|x64
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{9A0DC66D-3186-4EE4-B471-6C0F1DD6E159}.Debug|x86.ActiveCfg = Debug|x86
{9A0DC66D-3186-4EE4-B471-6C0F1DD6E159}.Debug|x86.Build.0 = Debug|x86
{9A0DC66D-3186-4EE4-B471-6C0F1DD6E159}.Release|x86.ActiveCfg = Release|x86
{9A0DC66D-3186-4EE4-B471-6C0F1DD6E159}.Release|x86.Build.0 = Release|x86
{9A0DC66D-3186-4EE4-B471-6C0F1DD6E159}.Debug|x64.ActiveCfg = Debug|x64
{9A0DC66D-3186-4EE4-B471-6C0F1DD6E159}.Debug|x64.Build.0 = Debug|x64
{9A0DC66D-3186-4EE4-B471-6C0F1DD6E159}.Release|x64.ActiveCfg = Release|x64
{9A0DC66D-3186-4EE4-B471-6C0F1DD6E159}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
36 changes: 23 additions & 13 deletions build_scripts/windows/azure-cli.wixproj
Original file line number Diff line number Diff line change
@@ -1,14 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- Local WiX -->
<PropertyGroup>
<LocalWixRoot>artifacts\wix\</LocalWixRoot>
<WixToolPath>$(LocalWixRoot)</WixToolPath>
<WixTargetsPath>$(WixToolPath)Wix.targets</WixTargetsPath>
<WixTasksPath>wixtasks.dll</WixTasksPath>
<AzureCliSource>artifacts\cli</AzureCliSource>
<LinkerAdditionalOptions>-fv</LinkerAdditionalOptions>
</PropertyGroup>
<!-- Project -->
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
Expand All @@ -21,14 +12,33 @@
<WixTargetsPath Condition=" '$(WixTargetsPath)' == '' AND '$(MSBuildExtensionsPath32)' != '' ">$(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets</WixTargetsPath>
<WixTargetsPath Condition=" '$(WixTargetsPath)' == '' ">$(MSBuildExtensionsPath)\Microsoft\WiX\v3.x\Wix.targets</WixTargetsPath>
</PropertyGroup>
<!-- Local WiX -->
<PropertyGroup>
<LocalWixRoot>artifacts\$(Platform)\wix</LocalWixRoot>
<WixToolPath>$(LocalWixRoot)</WixToolPath>
<WixTargetsPath Condition="Exists('$(WixToolPath)\Wix.targets')">$(WixToolPath)\Wix.targets</WixTargetsPath>
<WixTasksPath Condition="Exists('$(WixToolPath)\wixtasks.dll')">wixtasks.dll</WixTasksPath>
<AzureCliSource>artifacts\$(Platform)\cli</AzureCliSource>
<LinkerAdditionalOptions>-fv</LinkerAdditionalOptions>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<OutputPath>out\$(Configuration)\</OutputPath>
<IntermediateOutputPath>out\obj\$(Configuration)\</IntermediateOutputPath>
<OutputPath>out\$(Platform)\$(Configuration)\</OutputPath>
<IntermediateOutputPath>out\obj\$(Platform)\$(Configuration)\</IntermediateOutputPath>
<DefineConstants>Debug;AzureCliSource=$(AzureCliSource)</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<OutputPath>out\</OutputPath>
<IntermediateOutputPath>out\obj\$(Configuration)\</IntermediateOutputPath>
<OutputPath>out\$(Platform)\</OutputPath>
<IntermediateOutputPath>out\obj\$(Platform)\$(Configuration)\</IntermediateOutputPath>
<DefineConstants>AzureCliSource=$(AzureCliSource)</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x64' ">
<OutputPath>out\$(Platform)\$(Configuration)\</OutputPath>
<IntermediateOutputPath>out\obj\$(Platform)\$(Configuration)\</IntermediateOutputPath>
<DefineConstants>Debug;AzureCliSource=$(AzureCliSource)</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x64' ">
<OutputPath>out\$(Platform)\</OutputPath>
<IntermediateOutputPath>out\obj\$(Platform)\$(Configuration)\</IntermediateOutputPath>
<DefineConstants>AzureCliSource=$(AzureCliSource)</DefineConstants>
</PropertyGroup>
<ItemGroup>
Expand Down
26 changes: 22 additions & 4 deletions build_scripts/windows/scripts/build.cmd
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,22 @@ if "%CLI_VERSION%"=="" (
echo Please set the CLI_VERSION environment variable, e.g. 2.0.13
goto ERROR
)

if "%ARCH%"=="" (
set ARCH=x86
)
if "%ARCH%"=="x86" (
set PYTHON_ARCH=win32
) else if "%ARCH%"=="x64" (
set PYTHON_ARCH=amd64
) else (
echo Please set ARCH to "x86" or "x64"
goto ERROR
)
set PYTHON_VERSION=3.10.10

set WIX_DOWNLOAD_URL="https://azurecliprod.blob.core.windows.net/msi/wix310-binaries-mirror.zip"
set PYTHON_DOWNLOAD_URL="https://www.python.org/ftp/python/%PYTHON_VERSION%/python-%PYTHON_VERSION%-embed-win32.zip"
set PYTHON_DOWNLOAD_URL="https://www.python.org/ftp/python/%PYTHON_VERSION%/python-%PYTHON_VERSION%-embed-%PYTHON_ARCH%.zip"

REM https://pip.pypa.io/en/stable/installation/#get-pip-py
set GET_PIP_DOWNLOAD_URL="https://bootstrap.pypa.io/get-pip.py"
Expand All @@ -27,14 +39,17 @@ set OUTPUT_DIR=%~dp0..\out
if exist %OUTPUT_DIR% rmdir /s /q %OUTPUT_DIR%
mkdir %OUTPUT_DIR%

set ARTIFACTS_DIR=%~dp0..\artifacts
set ARTIFACTS_DIR=%~dp0..\artifacts\%ARCH%
mkdir %ARTIFACTS_DIR%
set TEMP_SCRATCH_FOLDER=%ARTIFACTS_DIR%\cli_scratch
set BUILDING_DIR=%ARTIFACTS_DIR%\cli
set WIX_DIR=%ARTIFACTS_DIR%\wix
set PYTHON_DIR=%ARTIFACTS_DIR%\Python

set REPO_ROOT=%~dp0..\..\..
REM Get the absolute directory since we pushd into different levels of subdirectories.
PUSHD %~dp0..\..\..
SET REPO_ROOT=%CD%
POPD

REM reset working folders
if exist %BUILDING_DIR% rmdir /s /q %BUILDING_DIR%
Expand Down Expand Up @@ -72,6 +87,9 @@ if not exist %WIX_DIR% (
popd
)

REM Use only downloaded python and ignore machine state
set PYTHONHOME=%PYTHON_DIR%

REM ensure Python is available
if exist %PYTHON_DIR% (
echo Using existing Python at %PYTHON_DIR%
Expand Down Expand Up @@ -181,7 +199,7 @@ popd
if %errorlevel% neq 0 goto ERROR

echo Building MSI...
msbuild /t:rebuild /p:Configuration=Release %REPO_ROOT%\build_scripts\windows\azure-cli.wixproj
msbuild /t:rebuild /p:Configuration=Release /p:Platform=%ARCH% %REPO_ROOT%\build_scripts\windows\azure-cli.wixproj

if %errorlevel% neq 0 goto ERROR

Expand Down