Add exe wrapper for Microsoft Update scenarios (#14737)
This commit is contained in:
parent
425c40dc8f
commit
b1ad606818
BIN
assets/wix/ExeLicense.rtf
Normal file
BIN
assets/wix/ExeLicense.rtf
Normal file
Binary file not shown.
31
assets/wix/bundle.wxs
Normal file
31
assets/wix/bundle.wxs
Normal 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>
|
126
test/packaging/windows/exe.tests.ps1
Normal file
126
test/packaging/windows/exe.tests.ps1
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)) {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in a new issue