Add exe wrapper for Microsoft Update scenarios (#14737)

This commit is contained in:
Travis Plunk 2021-02-19 15:39:35 -08:00 committed by GitHub
parent 425c40dc8f
commit b1ad606818
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 251 additions and 40 deletions

BIN
assets/wix/ExeLicense.rtf Normal file

Binary file not shown.

31
assets/wix/bundle.wxs Normal file
View file

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:bal="http://schemas.microsoft.com/wix/BalExtension">
<!-- UpgradeCode GUID MUST REMAIN SAME THROUGHOUT ALL VERSIONS, otherwise, updates won't occur. -->
<?if $(sys.BUILDARCH)=x64?>
<?define UpgradeCodePreview = "3C90221B-D500-43C6-A4A6-0BE6C2C1B317"?>
<?define UpgradeCodeRelease = "7A804CBB-648E-4276-9A58-081862DB1B99"?>
<?if $(var.IsPreview)=True?>
<?define UpgradeCode = $(var.UpgradeCodePreview)?>
<?else?>
<?define UpgradeCode = $(var.UpgradeCodeRelease)?>
<?endif?>
<?else?>
<?define UpgradeCodePreview = "4A699A9C-E904-4024-BCD2-44E098A8C6BD"?>
<?define UpgradeCodeRelease = "ED46CB02-64B3-43FD-A63E-6CF269D8C21C"?>
<?if $(var.IsPreview)=True?>
<?define UpgradeCode = $(var.UpgradeCodePreview)?>
<?else?>
<?define UpgradeCode = $(var.UpgradeCodeRelease)?>
<?endif?>
<?endif?>
<Bundle Name="PowerShell $(var.WindowsVersion)-$(sys.BUILDARCH)" Version="$(var.WindowsVersion)" Manufacturer="Microsoft Corporation" UpgradeCode="$(var.UpgradeCode)">
<!-- See https://wixtoolset.org/documentation/manual/v3/bundle/wixstdba/ for a list of WiX standard bootstrapper types. -->
<BootstrapperApplicationRef Id="WixStandardBootstrapperApplication.RtfLicense">
<bal:WixStandardBootstrapperApplication LicenseFile="assets\wix\ExeLicense.rtf" LogoFile="assets\ps_black_32x32.ico" />
</BootstrapperApplicationRef>
<Chain>
<MsiPackage SourceFile="$(var.TargetPath)" Compressed="yes" />
</Chain>
</Bundle>
</Wix>

View file

@ -0,0 +1,126 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
Describe -Name "Windows EXE" -Fixture {
BeforeAll {
function Test-Elevated {
[CmdletBinding()]
[OutputType([bool])]
Param()
# if the current Powershell session was called with administrator privileges,
# the Administrator Group's well-known SID will show up in the Groups for the current identity.
# Note that the SID won't show up unless the process is elevated.
return (([Security.Principal.WindowsIdentity]::GetCurrent()).Groups -contains "S-1-5-32-544")
}
function Invoke-ExeInstaller {
param(
[Parameter(ParameterSetName = 'Install', Mandatory)]
[Switch]$Install,
[Parameter(ParameterSetName = 'Uninstall', Mandatory)]
[Switch]$Uninstall,
[Parameter(Mandatory)]
[ValidateScript({Test-Path -Path $_})]
[String]$ExePath
)
$action = "$($PSCmdlet.ParameterSetName)ing"
if ($Install) {
$switch = '/install'
} else {
$switch = '/uninstall'
}
$installProcess = Start-Process -wait $ExePath -ArgumentList $switch, '/quiet', '/norestart' -PassThru
if ($installProcess.ExitCode -ne 0) {
$exitCode = $installProcess.ExitCode
throw "$action EXE failed and returned error code $exitCode."
}
}
$exePath = $env:PsExePath
$channel = $env:PSMsiChannel
$runtime = $env:PSMsiRuntime
# Get any existing powershell in the path
$beforePath = @(([System.Environment]::GetEnvironmentVariable('PATH', 'MACHINE')) -split ';' |
Where-Object {$_ -like '*files\powershell*'})
foreach ($pathPart in $beforePath) {
Write-Warning "Found existing PowerShell path: $pathPart"
}
if (!(Test-Elevated)) {
Write-Warning "Tests must be elevated"
}
}
BeforeEach {
$error.Clear()
}
Context "$Channel-$Runtime" {
BeforeAll {
Write-Verbose "cr-$channel-$runtime" -Verbose
switch ("$channel-$runtime") {
"preview-win7-x64" {
$msiUpgradeCode = '39243d76-adaf-42b1-94fb-16ecf83237c8'
}
"stable-win7-x64" {
$msiUpgradeCode = '31ab5147-9a97-4452-8443-d9709f0516e1'
}
"preview-win7-x86" {
$msiUpgradeCode = '86abcfbd-1ccc-4a88-b8b2-0facfde29094'
}
"stable-win7-x86" {
$msiUpgradeCode = '1d00683b-0f84-4db8-a64f-2f98ad42fe06'
}
default {
throw "'$_' not a valid channel runtime combination"
}
}
}
It "$Channel MSI should not be installed before test" -Skip:(!(Test-Elevated)) {
$result = @(Get-CimInstance -Query "SELECT Value FROM Win32_Property WHERE Property='UpgradeCode' and Value = '{$msiUpgradeCode}'")
$result.Count | Should -Be 0 -Because "Query should return nothing if $channel $runtime is not installed"
}
It "EXE should install without error" -Skip:(!(Test-Elevated)) {
{
Invoke-ExeInstaller -Install -ExePath $exePath
} | Should -Not -Throw
}
It "Upgrade code should be correct" -Skip:(!(Test-Elevated)) {
$result = @(Get-CimInstance -Query "SELECT Value FROM Win32_Property WHERE Property='UpgradeCode' and Value = '{$msiUpgradeCode}'")
$result.Count | Should -Be 1 -Because "Query should return 1 result if Upgrade code is for $runtime $channel"
}
It "MSI should have updated path" -Skip:(!(Test-Elevated)) {
if ($channel -eq 'preview') {
$pattern = '*files*\powershell*\preview*'
} else {
$pattern = '*files*\powershell*'
}
$psPath = ([System.Environment]::GetEnvironmentVariable('PATH', 'MACHINE')) -split ';' |
Where-Object { $_ -like $pattern -and $_ -notin $beforePath }
if (!$psPath) {
([System.Environment]::GetEnvironmentVariable('PATH', 'MACHINE')) -split ';' |
Where-Object { $_ -notin $beforePath } |
ForEach-Object { Write-Verbose -Verbose $_ }
}
$psPath | Should -Not -BeNullOrEmpty
}
It "MSI should uninstall without error" -Skip:(!(Test-Elevated)) {
{
Invoke-ExeInstaller -Uninstall -ExePath $exePath
} | Should -Not -Throw
}
}
}

View file

@ -106,7 +106,7 @@ Describe -Name "Windows MSI" -Fixture {
It "$Channel MSI should not be installed before test" -Skip:(!(Test-Elevated)) {
$result = @(Get-CimInstance -Query "SELECT Value FROM Win32_Property WHERE Property='UpgradeCode' and Value = '{$msiUpgradeCode}'")
$result.Count | Should -Be 0 -Because "Query should return nothing if $channel x64 is not installed"
$result.Count | Should -Be 0 -Because "Query should return nothing if $channel $runtime is not installed"
}
It "MSI should install without error" -Skip:(!(Test-Elevated)) {
@ -117,7 +117,7 @@ Describe -Name "Windows MSI" -Fixture {
It "Upgrade code should be correct" -Skip:(!(Test-Elevated)) {
$result = @(Get-CimInstance -Query "SELECT Value FROM Win32_Property WHERE Property='UpgradeCode' and Value = '{$msiUpgradeCode}'")
$result.Count | Should -Be 1 -Because "Query should return 1 result if Upgrade code is for x64 $channel"
$result.Count | Should -Be 1 -Because "Query should return 1 result if Upgrade code is for $runtime $channel"
}
It "MSI should uninstall without error" -Skip:(!(Test-Elevated)) {

View file

@ -501,6 +501,7 @@ function Invoke-CIFinish
# the packaging tests find the MSI package using env:PSMsiX64Path
$env:PSMsiX64Path = $artifacts | Where-Object { $_.EndsWith(".msi")}
$env:PSExePath = $artifacts | Where-Object { $_.EndsWith(".exe") }
$env:PSMsiChannel = $Channel
$env:PSMsiRuntime = $Runtime
@ -509,8 +510,12 @@ function Invoke-CIFinish
Install-Module Pester -Force -SkipPublisherCheck -MaximumVersion $maximumPesterVersion
Import-Module Pester -Force -MaximumVersion $maximumPesterVersion
$testResultPath = Join-Path -Path $env:TEMP -ChildPath "win-package-$channel-$runtime.xml"
# start the packaging tests and get the results
$packagingTestResult = Invoke-Pester -Script (Join-Path $repoRoot '.\test\packaging\windows\') -PassThru
$packagingTestResult = Invoke-Pester -Script (Join-Path $repoRoot '.\test\packaging\windows\') -PassThru -OutputFormat NUnitXml -OutputFile $testResultPath
Publish-TestResults -Title "win-package-$channel-$runtime" -Path $testResultPath
# fail the CI job if the tests failed, or nothing passed
if(-not $packagingTestResult -is [pscustomobject] -or $packagingTestResult.FailedCount -ne 0 -or $packagingTestResult.PassedCount -eq 0)

View file

@ -513,7 +513,9 @@ function New-TarballPackage {
[switch] $Force,
[switch] $ExcludeSymbolicLinks
[switch] $ExcludeSymbolicLinks,
[string] $CurrentLocation = (Get-Location)
)
if ($PackageNameSuffix) {
@ -530,7 +532,7 @@ function New-TarballPackage {
$packageName = $packageName -f "osx"
}
$packagePath = Join-Path -Path $PWD -ChildPath $packageName
$packagePath = Join-Path -Path $CurrentLocation -ChildPath $packageName
Write-Verbose "Create package $packageName"
Write-Verbose "Package destination path: $packagePath"
@ -734,7 +736,10 @@ function New-UnixPackage {
$NoSudo,
[switch]
$LTS
$LTS,
[string]
$CurrentLocation = (Get-Location)
)
DynamicParam {
@ -967,7 +972,7 @@ function New-UnixPackage {
}
# Magic to get path output
$createdPackage = Get-Item (Join-Path $PWD (($Output[-1] -split ":path=>")[-1] -replace '["{}]'))
$createdPackage = Get-Item (Join-Path $CurrentLocation (($Output[-1] -split ":path=>")[-1] -replace '["{}]'))
if ($Environment.IsMacOS) {
if ($PSCmdlet.ShouldProcess("Add distribution information and Fix PackageName"))
@ -1610,7 +1615,9 @@ function New-ZipPackage
[ValidateNotNullOrEmpty()]
[string] $PackageSourcePath,
[switch] $Force
[switch] $Force,
[string] $CurrentLocation = (Get-Location)
)
$ProductSemanticVersion = Get-PackageSemanticVersion -Version $PackageVersion
@ -1622,7 +1629,7 @@ function New-ZipPackage
Write-Verbose "Create Zip for Product $zipPackageName"
$zipLocationPath = Join-Path $PWD "$zipPackageName.zip"
$zipLocationPath = Join-Path $CurrentLocation "$zipPackageName.zip"
if ($Force.IsPresent)
{
@ -1682,7 +1689,9 @@ function New-PdbZipPackage
[Parameter(Mandatory = $true)]
[string] $PackageSourcePath,
[switch] $Force
[switch] $Force,
[string] $CurrentLocation = (Get-Location)
)
$ProductSemanticVersion = Get-PackageSemanticVersion -Version $PackageVersion
@ -1694,7 +1703,7 @@ function New-PdbZipPackage
Write-Verbose "Create Symbols Zip for Product $zipPackageName"
$zipLocationPath = Join-Path $PWD "$zipPackageName.zip"
$zipLocationPath = Join-Path $CurrentLocation "$zipPackageName.zip"
if ($Force.IsPresent)
{
@ -2982,6 +2991,11 @@ function New-MSIPackage
[ValidateScript( {Test-Path $_})]
[string] $FilesWxsPath = "$RepoRoot\assets\wix\Files.wxs",
# File describing the MSI Package creation semantics
[ValidateNotNullOrEmpty()]
[ValidateScript({Test-Path $_})]
[string] $BundleWxsPath = "$RepoRoot\assets\wix\bundle.wxs",
# Path to Assets folder containing artifacts such as icons, images
[ValidateNotNullOrEmpty()]
[ValidateScript( {Test-Path $_})]
@ -2994,7 +3008,9 @@ function New-MSIPackage
[string] $ProductTargetArchitecture,
# Force overwrite of package
[Switch] $Force
[Switch] $Force,
[string] $CurrentLocation = (Get-Location)
)
$wixPaths = Get-WixPath
@ -3043,11 +3059,11 @@ function New-MSIPackage
if ($ProductNameSuffix) {
$packageName += "-$ProductNameSuffix"
}
$msiLocationPath = Join-Path $PWD "$packageName.msi"
$msiPdbLocationPath = Join-Path $PWD "$packageName.wixpdb"
if (!$Force.IsPresent -and (Test-Path -Path $msiLocationPath))
{
$msiLocationPath = Join-Path $CurrentLocation "$packageName.msi"
$msiPdbLocationPath = Join-Path $CurrentLocation "$packageName.wixpdb"
if (!$Force.IsPresent -and (Test-Path -Path $msiLocationPath)) {
Write-Error -Message "Package already exists, use -Force to overwrite, path: $msiLocationPath" -ErrorAction Stop
}
@ -3103,6 +3119,27 @@ function New-MSIPackage
$errorMessage = "Failed to create $msiLocationPath"
throw $errorMessage
}
$exeLocationPath = Join-Path $CurrentLocation "$packageName.exe"
$exePdbLocationPath = Join-Path $CurrentLocation "$packageName.exe.wixpdb"
$windowsVersion = Get-WindowsVersion -packageName $packageName
Start-MsiBuild -WxsFile $BundleWxsPath -ProductTargetArchitecture $ProductTargetArchitecture -Argument @{
IsPreview = $isPreview
TargetPath = $msiLocationPath
WindowsVersion = $windowsVersion
} -MsiLocationPath $exeLocationPath -MsiPdbLocationPath $exePdbLocationPath
if (Test-Path $exeLocationPath)
{
Write-Verbose "You can find the MSI @ $exeLocationPath" -Verbose
$exeLocationPath
}
else
{
$errorMessage = "Failed to create $exeLocationPath"
throw $errorMessage
}
}
function New-MsiArgsArray {
@ -3212,7 +3249,9 @@ function New-MSIXPackage
[string] $Architecture,
# Force overwrite of package
[Switch] $Force
[Switch] $Force,
[string] $CurrentLocation = (Get-Location)
)
$makeappx = Get-Command makeappx -CommandType Application -ErrorAction Ignore
@ -3251,27 +3290,7 @@ function New-MSIXPackage
Write-Verbose -Verbose "ProductName: $productName"
Write-Verbose -Verbose "DisplayName: $displayName"
$ProductVersion = Get-PackageVersionAsMajorMinorBuildRevision -Version $ProductVersion
if (([Version]$ProductVersion).Revision -eq -1) {
$ProductVersion += ".0"
}
# The Store requires the last digit of the version to be 0 so we swap the build and revision
# This only affects Preview versions where the last digit is the preview number
# For stable versions, the last digit is already zero so no changes
$pversion = [version]$ProductVersion
if ($pversion.Revision -ne 0) {
$revision = $pversion.Revision
if ($packageName.Contains('-rc')) {
# For Release Candidates, we use numbers in the 100 range
$revision += 100
}
$pversion = [version]::new($pversion.Major, $pversion.Minor, $revision, 0)
$ProductVersion = $pversion.ToString()
}
Write-Verbose "Version: $productversion" -Verbose
$ProductVersion = Get-WindowsVersion -PackageName $packageName
$isPreview = Test-IsPreview -Version $ProductSemanticVersion
if ($isPreview) {
@ -3317,7 +3336,7 @@ function New-MSIXPackage
Start-NativeExecution -VerboseOutputOnError { & $makepri new /v /o /pr $ProductSourcePath /cf (Join-Path $ProductSourcePath "priconfig.xml") }
Pop-Location
Write-Verbose "Creating msix package" -Verbose
Start-NativeExecution -VerboseOutputOnError { & $makeappx pack /o /v /h SHA256 /d $ProductSourcePath /p (Join-Path -Path $PWD -ChildPath "$packageName.msix") }
Start-NativeExecution -VerboseOutputOnError { & $makeappx pack /o /v /h SHA256 /d $ProductSourcePath /p (Join-Path -Path $CurrentLocation -ChildPath "$packageName.msix") }
Write-Verbose "Created $packageName.msix" -Verbose
}
}
@ -3601,6 +3620,36 @@ function New-WixId
"$Prefix$guidPortion"
}
function Get-WindowsVersion {
param (
[parameter(Mandatory)]
[string]$PackageName
)
$ProductVersion = Get-PackageVersionAsMajorMinorBuildRevision -Version $ProductVersion
if (([Version]$ProductVersion).Revision -eq -1) {
$ProductVersion += ".0"
}
# The Store requires the last digit of the version to be 0 so we swap the build and revision
# This only affects Preview versions where the last digit is the preview number
# For stable versions, the last digit is already zero so no changes
$pversion = [version]$ProductVersion
if ($pversion.Revision -ne 0) {
$revision = $pversion.Revision
if ($packageName.Contains('-rc')) {
# For Release Candidates, we use numbers in the 100 range
$revision += 100
}
$pversion = [version]::new($pversion.Major, $pversion.Minor, $revision, 0)
$ProductVersion = $pversion.ToString()
}
Write-Verbose "Version: $productversion" -Verbose
return $productversion
}
# Builds coming out of this project can have version number as 'a.b.c' OR 'a.b.c-d-f'
# This function converts the above version into major.minor[.build[.revision]] format
function Get-PackageVersionAsMajorMinorBuildRevision
@ -3617,7 +3666,7 @@ function Get-PackageVersionAsMajorMinorBuildRevision
$packageVersionTokens = $Version.Split('-')
$packageVersion = ([regex]::matches($Version, "\d+(\.\d+)+"))[0].value
if (1 -eq $packageVersionTokens.Count) {
if (1 -eq $packageVersionTokens.Count -and ([Version]$packageVersion).Revision -eq -1) {
# In case the input is of the form a.b.c, add a '0' at the end for revision field
$packageVersion = $packageVersion + '.0'
} elseif (1 -lt $packageVersionTokens.Count) {