mirror of
https://github.com/MAKS-IT-COM/maksit-cluster-console.git
synced 2026-09-30 00:38:10 +02:00
Rollout control, PVC resize, reclaim policy, certificates, tokens, node images, and an optional confirmed chat agent are in this release. YAML coloring now covers the whole document, including long lines.
305 lines
12 KiB
PowerShell
305 lines
12 KiB
PowerShell
#requires -Version 7.0
|
|
#requires -PSEdition Core
|
|
|
|
<#
|
|
.SYNOPSIS
|
|
Store MSIX plugin for a published win-* .NET desktop app (Community).
|
|
|
|
.DESCRIPTION
|
|
Packs the win-* DotNetPublish folder into a full-trust .msix and signs it
|
|
with a short-lived self-signed certificate whose subject is `publisher`.
|
|
The Microsoft Store re-signs the package; this signature is only the upload
|
|
envelope. The .msix is not a GitHub release asset and is not added to the
|
|
portable zip. Requires makeappx.exe and signtool.exe from the Windows SDK.
|
|
#>
|
|
|
|
if (-not (Get-Command Import-PluginDependency -ErrorAction SilentlyContinue)) {
|
|
$srcDir = Split-Path (Split-Path $PSScriptRoot -Parent) -Parent
|
|
$pluginSupportModulePath = Join-Path $srcDir "modules/Engine/PluginSupport.psm1"
|
|
if (Test-Path $pluginSupportModulePath -PathType Leaf) {
|
|
Import-Module $pluginSupportModulePath -Force -Global -ErrorAction Stop
|
|
}
|
|
}
|
|
|
|
function Get-WindowsSdkToolPath {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$FileName
|
|
)
|
|
|
|
$roots = @(
|
|
${env:ProgramFiles(x86)},
|
|
$env:ProgramFiles
|
|
) | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }
|
|
|
|
foreach ($root in $roots) {
|
|
$bin = Join-Path $root 'Windows Kits\10\bin'
|
|
if (-not (Test-Path -LiteralPath $bin -PathType Container)) {
|
|
continue
|
|
}
|
|
|
|
$versions = @(
|
|
Get-ChildItem -LiteralPath $bin -Directory |
|
|
Where-Object { $_.Name -match '^10\.' } |
|
|
Sort-Object { try { [version]$_.Name } catch { [version]'0.0' } } -Descending
|
|
)
|
|
foreach ($version in $versions) {
|
|
$candidate = Join-Path $version.FullName "x64\$FileName"
|
|
if (Test-Path -LiteralPath $candidate -PathType Leaf) {
|
|
return $candidate
|
|
}
|
|
}
|
|
}
|
|
|
|
return $null
|
|
}
|
|
|
|
function Get-MsixToolCommand {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$FileName,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$StubName
|
|
)
|
|
|
|
if (Test-ExternalCommandTestHandler) {
|
|
return $StubName
|
|
}
|
|
|
|
$path = Get-WindowsSdkToolPath -FileName $FileName
|
|
if ([string]::IsNullOrWhiteSpace($path)) {
|
|
throw "Windows SDK tool '$FileName' was not found. Install the Windows 10 SDK so $FileName is under 'Windows Kits\10\bin\<version>\x64'."
|
|
}
|
|
|
|
return $path
|
|
}
|
|
|
|
function Invoke-MsixSign {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$MsixPath,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Publisher,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$SignTool
|
|
)
|
|
|
|
$thumb = 'TEST'
|
|
$created = $false
|
|
try {
|
|
if (-not (Test-ExternalCommandTestHandler)) {
|
|
$cert = New-SelfSignedCertificate `
|
|
-Type Custom `
|
|
-Subject $Publisher `
|
|
-KeyUsage DigitalSignature `
|
|
-KeyAlgorithm RSA `
|
|
-KeyLength 2048 `
|
|
-FriendlyName 'MaksIT MsixPack ephemeral' `
|
|
-CertStoreLocation 'Cert:\CurrentUser\My' `
|
|
-TextExtension @('2.5.29.37={text}1.3.6.1.5.5.7.3.3') `
|
|
-NotAfter (Get-Date).AddDays(1)
|
|
$thumb = $cert.Thumbprint
|
|
$created = $true
|
|
}
|
|
|
|
Invoke-ExternalCommand -Name $SignTool -ArgumentList @(
|
|
'sign', '/fd', 'SHA256', '/sha1', $thumb,
|
|
'/tr', 'http://timestamp.digicert.com', '/td', 'SHA256',
|
|
$MsixPath
|
|
) | Out-Null
|
|
}
|
|
finally {
|
|
if ($created -and -not [string]::IsNullOrWhiteSpace($thumb)) {
|
|
$certPath = Join-Path 'Cert:\CurrentUser\My' $thumb
|
|
if (Test-Path -LiteralPath $certPath) {
|
|
Remove-Item -LiteralPath $certPath -Force
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function Remove-MsixSatelliteCultures {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Layout
|
|
)
|
|
|
|
$names = [System.Collections.Generic.HashSet[string]]::new([StringComparer]::OrdinalIgnoreCase)
|
|
foreach ($culture in [System.Globalization.CultureInfo]::GetCultures([System.Globalization.CultureTypes]::AllCultures)) {
|
|
if (-not [string]::IsNullOrWhiteSpace($culture.Name)) {
|
|
[void]$names.Add($culture.Name)
|
|
}
|
|
}
|
|
|
|
foreach ($dir in @(Get-ChildItem -LiteralPath $Layout -Directory -ErrorAction SilentlyContinue)) {
|
|
if (-not $names.Contains($dir.Name)) {
|
|
continue
|
|
}
|
|
|
|
$satellites = @(Get-ChildItem -LiteralPath $dir.FullName -Filter '*.resources.dll' -File -ErrorAction SilentlyContinue)
|
|
if ($satellites.Count -eq 0) {
|
|
continue
|
|
}
|
|
|
|
Write-Log -Level "STEP" -Message "Omitting '$($dir.Name)' satellite resources so the Store package stays English-only."
|
|
Remove-Item -LiteralPath $dir.FullName -Recurse -Force
|
|
}
|
|
}
|
|
|
|
function Invoke-Plugin {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
$Settings
|
|
)
|
|
|
|
Import-PluginDependency -ModuleName "Logging" -RequiredCommand "Write-Log"
|
|
Import-PluginDependency -ModuleName "EngineContext" -RequiredCommand "Set-EngineFact"
|
|
Import-PluginDependency -ModuleName "ExternalCommandSupport" -RequiredCommand "Invoke-ExternalCommand"
|
|
Import-PluginDependency -ModuleName "DesktopPackSupport" -RequiredCommand "New-MsixManifestXml"
|
|
|
|
if (-not $IsWindows -and -not (Test-ExternalCommandTestHandler)) {
|
|
throw "MsixPack requires Windows (Windows SDK makeappx.exe and signtool.exe)."
|
|
}
|
|
|
|
$pluginSettings = $Settings
|
|
$sharedSettings = $Settings.context
|
|
$scriptDir = [string]$sharedSettings.scriptDir
|
|
$version = [string]$sharedSettings.version
|
|
$artifactsDirectory = [string]$sharedSettings.artifactsDirectory
|
|
|
|
if ([string]::IsNullOrWhiteSpace($version)) {
|
|
throw "MsixPack requires a release version in the shared context (DotNetReleaseVersion)."
|
|
}
|
|
|
|
if ([string]::IsNullOrWhiteSpace($artifactsDirectory)) {
|
|
throw "MsixPack requires an artifacts directory in the shared context."
|
|
}
|
|
|
|
$packageName = [string](Get-PluginPropertyValue -PluginSettings $pluginSettings -Name 'packageName')
|
|
$publisher = [string](Get-PluginPropertyValue -PluginSettings $pluginSettings -Name 'publisher')
|
|
$executableName = [string](Get-PluginPropertyValue -PluginSettings $pluginSettings -Name 'executableName')
|
|
if ([string]::IsNullOrWhiteSpace($packageName)) {
|
|
throw "MsixPack requires packageName."
|
|
}
|
|
|
|
if ([string]::IsNullOrWhiteSpace($publisher) -or $publisher -eq 'CN=PartnerCenter') {
|
|
throw "MsixPack requires publisher set to the Partner Center package identity (CN=...)."
|
|
}
|
|
|
|
if ($publisher -notmatch '(?i)(^|,)\s*CN=') {
|
|
throw "MsixPack publisher must be a distinguished name starting with CN=: $publisher"
|
|
}
|
|
|
|
if ([string]::IsNullOrWhiteSpace($executableName)) {
|
|
throw "MsixPack requires executableName."
|
|
}
|
|
|
|
Assert-MsixPackageName -PackageName $packageName
|
|
$packageVersion = ConvertTo-MsixPackageVersion -Version $version
|
|
|
|
$displayName = [string](Get-PluginPropertyValue -PluginSettings $pluginSettings -Name 'displayName' -Default $packageName)
|
|
$publisherDisplayName = [string](Get-PluginPropertyValue -PluginSettings $pluginSettings -Name 'publisherDisplayName' -Default 'MaksIT')
|
|
$description = [string](Get-PluginPropertyValue -PluginSettings $pluginSettings -Name 'description' -Default '')
|
|
$language = [string](Get-PluginPropertyValue -PluginSettings $pluginSettings -Name 'language' -Default 'en-us')
|
|
$runtimeIdentifier = [string](Get-PluginPropertyValue -PluginSettings $pluginSettings -Name 'runtimeIdentifier' -Default 'win-x64')
|
|
$architecture = Get-WixArchitectureFromRuntimeIdentifier -RuntimeIdentifier $runtimeIdentifier
|
|
$publishDirSetting = [string](Get-PluginPropertyValue -PluginSettings $pluginSettings -Name 'publishDir')
|
|
$iconSetting = [string](Get-PluginPropertyValue -PluginSettings $pluginSettings -Name 'iconPath')
|
|
|
|
if (-not (Test-Path -LiteralPath $artifactsDirectory -PathType Container)) {
|
|
New-Item -ItemType Directory -Path $artifactsDirectory | Out-Null
|
|
}
|
|
|
|
$publishDirectory = Resolve-DesktopPublishDirectory `
|
|
-Context $sharedSettings `
|
|
-RuntimeIdentifier $runtimeIdentifier `
|
|
-PublishDir $publishDirSetting `
|
|
-ScriptDir $scriptDir
|
|
|
|
$null = Resolve-DesktopExecutablePath `
|
|
-PublishDirectory $publishDirectory `
|
|
-ExecutableName $executableName `
|
|
-Windows
|
|
|
|
$resolvePackPath = {
|
|
param([string]$Setting)
|
|
if ([string]::IsNullOrWhiteSpace($Setting)) {
|
|
return $null
|
|
}
|
|
|
|
if ([System.IO.Path]::IsPathRooted($Setting)) {
|
|
return $Setting
|
|
}
|
|
|
|
return [System.IO.Path]::GetFullPath((Join-Path $scriptDir $Setting))
|
|
}
|
|
|
|
$iconPath = & $resolvePackPath $iconSetting
|
|
if ([string]::IsNullOrWhiteSpace($iconPath)) {
|
|
$fallback = Join-Path $PSScriptRoot 'brand\mark.png'
|
|
if (Test-Path -LiteralPath $fallback -PathType Leaf) {
|
|
$iconPath = $fallback
|
|
}
|
|
}
|
|
|
|
if ([string]::IsNullOrWhiteSpace($iconPath) -or -not (Test-Path -LiteralPath $iconPath -PathType Leaf)) {
|
|
throw "MsixPack iconPath not found: $iconSetting"
|
|
}
|
|
|
|
$safeName = ($packageName -replace '[^A-Za-z0-9._-]', '-').Trim('-')
|
|
$namePattern = [string](Get-PluginPropertyValue -PluginSettings $pluginSettings -Name 'msixNamePattern' -Default '{name}-{version}.msix')
|
|
$msixFileName = $namePattern.Replace('{version}', $version).Replace('{name}', $safeName)
|
|
$msixPath = Join-Path $artifactsDirectory $msixFileName
|
|
|
|
$stageDir = Join-Path $artifactsDirectory '.msix-stage'
|
|
if (Test-Path -LiteralPath $stageDir) {
|
|
Remove-Item -LiteralPath $stageDir -Recurse -Force
|
|
}
|
|
|
|
$layout = Join-Path $stageDir 'layout'
|
|
New-Item -ItemType Directory -Path $layout | Out-Null
|
|
Copy-Item -Path (Join-Path $publishDirectory '*') -Destination $layout -Recurse -Force
|
|
|
|
$manifest = New-MsixManifestXml `
|
|
-PackageName $packageName `
|
|
-Publisher $publisher `
|
|
-PackageVersion $packageVersion `
|
|
-ProcessorArchitecture $architecture `
|
|
-DisplayName $displayName `
|
|
-PublisherDisplayName $publisherDisplayName `
|
|
-ExecutableName $executableName `
|
|
-Description $description `
|
|
-Language $language
|
|
[System.IO.File]::WriteAllText((Join-Path $layout 'AppxManifest.xml'), $manifest, [System.Text.UTF8Encoding]::new($false))
|
|
Copy-MsixPackageLogos -IconPath $iconPath -AssetsDirectory (Join-Path $layout 'Assets')
|
|
Remove-MsixSatelliteCultures -Layout $layout
|
|
|
|
$makeAppx = Get-MsixToolCommand -FileName 'makeappx.exe' -StubName 'makeappx'
|
|
$signTool = Get-MsixToolCommand -FileName 'signtool.exe' -StubName 'signtool'
|
|
|
|
if (Test-Path -LiteralPath $msixPath -PathType Leaf) {
|
|
Remove-Item -LiteralPath $msixPath -Force
|
|
}
|
|
|
|
Write-Log -Level "STEP" -Message "Packing MSIX for '$displayName' ($architecture, $packageVersion)..."
|
|
Invoke-ExternalCommand -Name $makeAppx -ArgumentList @('pack', '/d', $layout, '/p', $msixPath, '/o') | Out-Null
|
|
if (-not (Test-Path -LiteralPath $msixPath -PathType Leaf)) {
|
|
throw "makeappx completed but MSIX was not produced: $msixPath"
|
|
}
|
|
|
|
Write-Log -Level "STEP" -Message "Signing MSIX with an ephemeral certificate ($publisher)..."
|
|
Invoke-MsixSign -MsixPath $msixPath -Publisher $publisher -SignTool $signTool
|
|
|
|
if (Test-Path -LiteralPath $stageDir) {
|
|
Remove-Item -LiteralPath $stageDir -Recurse -Force
|
|
}
|
|
|
|
Set-EngineFact -Context $sharedSettings -Namespace 'desktop' -Name 'msixPath' -Value $msixPath -Overwrite Replace
|
|
Write-Log -Level "OK" -Message " Store MSIX ready: $msixPath"
|
|
}
|
|
|
|
Export-ModuleMember -Function Invoke-Plugin
|