mirror of
https://github.com/rbalsleyMSFT/FFU.git
synced 2026-06-14 10:19:36 -06:00
Adds exit-code overrides and UI for winget apps
Adds per-app control for additional accepted exit codes and ignoring non‑zero exit codes to improve handling of installers with nonstandard returns. Exposes editable fields in the app list UI, persists them across search defaults, import/export, and pre-download save, and applies overrides during app resolution to honor configured behavior.
This commit is contained in:
@@ -428,10 +428,14 @@ function Get-Apps {
|
|||||||
if ($app.source -in @('winget', 'msstore')) {
|
if ($app.source -in @('winget', 'msstore')) {
|
||||||
$hasCmd = ($app.PSObject.Properties['CommandLine'] -and -not [string]::IsNullOrWhiteSpace($app.CommandLine))
|
$hasCmd = ($app.PSObject.Properties['CommandLine'] -and -not [string]::IsNullOrWhiteSpace($app.CommandLine))
|
||||||
$hasArgs = ($app.PSObject.Properties['Arguments'] -and -not [string]::IsNullOrWhiteSpace($app.Arguments))
|
$hasArgs = ($app.PSObject.Properties['Arguments'] -and -not [string]::IsNullOrWhiteSpace($app.Arguments))
|
||||||
if ($hasCmd -or $hasArgs) {
|
$hasAdd = ($app.PSObject.Properties['AdditionalExitCodes'] -and -not [string]::IsNullOrWhiteSpace($app.AdditionalExitCodes))
|
||||||
|
$hasIgnore = ($app.PSObject.Properties['IgnoreNonZeroExitCodes'])
|
||||||
|
if ($hasCmd -or $hasArgs -or $hasAdd -or $hasIgnore) {
|
||||||
$overrideMap[$app.name] = @{
|
$overrideMap[$app.name] = @{
|
||||||
CommandLine = if ($hasCmd) { $app.CommandLine } else { $null }
|
CommandLine = if ($hasCmd) { $app.CommandLine } else { $null }
|
||||||
Arguments = if ($hasArgs) { $app.Arguments } else { $null }
|
Arguments = if ($hasArgs) { $app.Arguments } else { $null }
|
||||||
|
AdditionalExitCodes = if ($hasAdd) { $app.AdditionalExitCodes } else { $null }
|
||||||
|
IgnoreNonZeroExitCodes = if ($hasIgnore) { [bool]$app.IgnoreNonZeroExitCodes } else { $null }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -455,6 +459,16 @@ function Get-Apps {
|
|||||||
$entry.Arguments = $ov.Arguments
|
$entry.Arguments = $ov.Arguments
|
||||||
$changed = $true
|
$changed = $true
|
||||||
}
|
}
|
||||||
|
if ($ov.ContainsKey('AdditionalExitCodes') -and $null -ne $ov.AdditionalExitCodes) {
|
||||||
|
WriteLog "Override (AppList.json) AdditionalExitCodes for $($entry.Name)"
|
||||||
|
$entry | Add-Member -NotePropertyName AdditionalExitCodes -NotePropertyValue $ov.AdditionalExitCodes -Force
|
||||||
|
$changed = $true
|
||||||
|
}
|
||||||
|
if ($ov.ContainsKey('IgnoreNonZeroExitCodes') -and $null -ne $ov.IgnoreNonZeroExitCodes) {
|
||||||
|
WriteLog "Override (AppList.json) IgnoreNonZeroExitCodes for $($entry.Name)"
|
||||||
|
$entry | Add-Member -NotePropertyName IgnoreNonZeroExitCodes -NotePropertyValue ([bool]$ov.IgnoreNonZeroExitCodes) -Force
|
||||||
|
$changed = $true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($changed) {
|
if ($changed) {
|
||||||
|
|||||||
@@ -891,6 +891,8 @@ function Import-ConfigSupplementalAssets {
|
|||||||
Version = ""
|
Version = ""
|
||||||
Source = $appInfo.source
|
Source = $appInfo.source
|
||||||
Architecture = $arch
|
Architecture = $arch
|
||||||
|
AdditionalExitCodes = if ($appInfo.PSObject.Properties['AdditionalExitCodes']) { $appInfo.AdditionalExitCodes } else { "" }
|
||||||
|
IgnoreNonZeroExitCodes = if ($appInfo.PSObject.Properties['IgnoreNonZeroExitCodes']) { [bool]$appInfo.IgnoreNonZeroExitCodes } else { $false }
|
||||||
DownloadStatus = ""
|
DownloadStatus = ""
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -446,6 +446,75 @@ function Initialize-DynamicUIElements {
|
|||||||
$wingetGridView.Columns.Add($archColumn)
|
$wingetGridView.Columns.Add($archColumn)
|
||||||
# --- END: Add Architecture Column ---
|
# --- END: Add Architecture Column ---
|
||||||
|
|
||||||
|
# --- START: Add Additional Exit Codes Column ---
|
||||||
|
$exitCodesColumn = New-Object System.Windows.Controls.GridViewColumn
|
||||||
|
$exitCodesHeader = New-Object System.Windows.Controls.GridViewColumnHeader
|
||||||
|
$exitCodesHeader.Tag = "AdditionalExitCodes"
|
||||||
|
$exitCodesHeader.HorizontalContentAlignment = [System.Windows.HorizontalAlignment]::Left
|
||||||
|
|
||||||
|
$exitHeaderTextFactory = New-Object System.Windows.FrameworkElementFactory([System.Windows.Controls.TextBlock])
|
||||||
|
$exitHeaderTextFactory.SetValue([System.Windows.Controls.TextBlock]::TextProperty, "Additional Exit Codes")
|
||||||
|
$exitHeaderTextFactory.SetValue([System.Windows.Controls.TextBlock]::PaddingProperty, (New-Object System.Windows.Thickness(5,2,5,2)))
|
||||||
|
$exitHeaderTextFactory.SetValue([System.Windows.FrameworkElement]::VerticalAlignmentProperty, [System.Windows.VerticalAlignment]::Center)
|
||||||
|
|
||||||
|
$exitHeaderTemplate = New-Object System.Windows.DataTemplate
|
||||||
|
$exitHeaderTemplate.VisualTree = $exitHeaderTextFactory
|
||||||
|
$exitCodesHeader.ContentTemplate = $exitHeaderTemplate
|
||||||
|
|
||||||
|
$exitCodesColumn.Header = $exitCodesHeader
|
||||||
|
$exitCodesColumn.Width = 140
|
||||||
|
|
||||||
|
$exitCodesCellTemplate = New-Object System.Windows.DataTemplate
|
||||||
|
$exitCodesTextBoxFactory = New-Object System.Windows.FrameworkElementFactory([System.Windows.Controls.TextBox])
|
||||||
|
$exitBinding = New-Object System.Windows.Data.Binding("AdditionalExitCodes")
|
||||||
|
$exitBinding.Mode = [System.Windows.Data.BindingMode]::TwoWay
|
||||||
|
$exitCodesTextBoxFactory.SetBinding([System.Windows.Controls.TextBox]::TextProperty, $exitBinding)
|
||||||
|
$exitCodesCellTemplate.VisualTree = $exitCodesTextBoxFactory
|
||||||
|
$exitCodesColumn.CellTemplate = $exitCodesCellTemplate
|
||||||
|
$wingetGridView.Columns.Add($exitCodesColumn)
|
||||||
|
# --- END: Add Additional Exit Codes Column ---
|
||||||
|
|
||||||
|
# --- START: Add Ignore Non-Zero Exit Codes Column ---
|
||||||
|
$ignoreColumn = New-Object System.Windows.Controls.GridViewColumn
|
||||||
|
$ignoreHeader = New-Object System.Windows.Controls.GridViewColumnHeader
|
||||||
|
$ignoreHeader.Tag = "IgnoreNonZeroExitCodes"
|
||||||
|
$ignoreHeader.HorizontalContentAlignment = [System.Windows.HorizontalAlignment]::Left
|
||||||
|
|
||||||
|
$ignoreHeaderTextFactory = New-Object System.Windows.FrameworkElementFactory([System.Windows.Controls.TextBlock])
|
||||||
|
$ignoreHeaderTextFactory.SetValue([System.Windows.Controls.TextBlock]::TextProperty, "Ignore Exit Codes")
|
||||||
|
$ignoreHeaderTextFactory.SetValue([System.Windows.Controls.TextBlock]::PaddingProperty, (New-Object System.Windows.Thickness(5,2,5,2)))
|
||||||
|
$ignoreHeaderTextFactory.SetValue([System.Windows.FrameworkElement]::VerticalAlignmentProperty, [System.Windows.VerticalAlignment]::Center)
|
||||||
|
|
||||||
|
$ignoreHeaderTemplate = New-Object System.Windows.DataTemplate
|
||||||
|
$ignoreHeaderTemplate.VisualTree = $ignoreHeaderTextFactory
|
||||||
|
$ignoreHeader.ContentTemplate = $ignoreHeaderTemplate
|
||||||
|
|
||||||
|
$ignoreColumn.Header = $ignoreHeader
|
||||||
|
$ignoreColumn.Width = 140
|
||||||
|
|
||||||
|
$ignoreCellTemplate = New-Object System.Windows.DataTemplate
|
||||||
|
|
||||||
|
# Center the checkbox in the cell
|
||||||
|
$ignoreCellGridFactory = New-Object System.Windows.FrameworkElementFactory([System.Windows.Controls.Grid])
|
||||||
|
$ignoreCellGridFactory.SetValue([System.Windows.FrameworkElement]::HorizontalAlignmentProperty, [System.Windows.HorizontalAlignment]::Stretch)
|
||||||
|
$ignoreCellGridFactory.SetValue([System.Windows.FrameworkElement]::VerticalAlignmentProperty, [System.Windows.VerticalAlignment]::Stretch)
|
||||||
|
|
||||||
|
$ignoreCheckFactory = New-Object System.Windows.FrameworkElementFactory([System.Windows.Controls.CheckBox])
|
||||||
|
$ignoreCheckFactory.SetValue([System.Windows.FrameworkElement]::HorizontalAlignmentProperty, [System.Windows.HorizontalAlignment]::Center)
|
||||||
|
$ignoreCheckFactory.SetValue([System.Windows.FrameworkElement]::VerticalAlignmentProperty, [System.Windows.VerticalAlignment]::Center)
|
||||||
|
|
||||||
|
$ignoreBinding = New-Object System.Windows.Data.Binding("IgnoreNonZeroExitCodes")
|
||||||
|
$ignoreBinding.Mode = [System.Windows.Data.BindingMode]::TwoWay
|
||||||
|
$ignoreCheckFactory.SetBinding([System.Windows.Controls.Primitives.ToggleButton]::IsCheckedProperty, $ignoreBinding)
|
||||||
|
|
||||||
|
# Build the visual tree: Grid -> CheckBox
|
||||||
|
$ignoreCellGridFactory.AppendChild($ignoreCheckFactory)
|
||||||
|
$ignoreCellTemplate.VisualTree = $ignoreCellGridFactory
|
||||||
|
|
||||||
|
$ignoreColumn.CellTemplate = $ignoreCellTemplate
|
||||||
|
$wingetGridView.Columns.Add($ignoreColumn)
|
||||||
|
# --- END: Add Ignore Non-Zero Exit Codes Column ---
|
||||||
|
|
||||||
Add-SortableColumn -gridView $wingetGridView -header "Download Status" -binding "DownloadStatus" -width 150 -headerHorizontalAlignment Left
|
Add-SortableColumn -gridView $wingetGridView -header "Download Status" -binding "DownloadStatus" -width 150 -headerHorizontalAlignment Left
|
||||||
$State.Controls.lstWingetResults.AddHandler(
|
$State.Controls.lstWingetResults.AddHandler(
|
||||||
[System.Windows.Controls.GridViewColumnHeader]::ClickEvent,
|
[System.Windows.Controls.GridViewColumnHeader]::ClickEvent,
|
||||||
|
|||||||
@@ -102,6 +102,8 @@ function Save-WingetList {
|
|||||||
id = $_.Id
|
id = $_.Id
|
||||||
source = $_.Source.ToLower()
|
source = $_.Source.ToLower()
|
||||||
architecture = $_.Architecture
|
architecture = $_.Architecture
|
||||||
|
AdditionalExitCodes = if ($_.PSObject.Properties['AdditionalExitCodes']) { $_.AdditionalExitCodes } else { "" }
|
||||||
|
IgnoreNonZeroExitCodes = if ($_.PSObject.Properties['IgnoreNonZeroExitCodes']) { [bool]$_.IgnoreNonZeroExitCodes } else { $false }
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -154,6 +156,8 @@ function Import-WingetList {
|
|||||||
Version = "" # Will be populated when searching or if data exists
|
Version = "" # Will be populated when searching or if data exists
|
||||||
Source = $appInfo.source
|
Source = $appInfo.source
|
||||||
Architecture = $arch
|
Architecture = $arch
|
||||||
|
AdditionalExitCodes = if ($appInfo.PSObject.Properties['AdditionalExitCodes']) { $appInfo.AdditionalExitCodes } else { "" }
|
||||||
|
IgnoreNonZeroExitCodes = if ($appInfo.PSObject.Properties['IgnoreNonZeroExitCodes']) { [bool]$appInfo.IgnoreNonZeroExitCodes } else { $false }
|
||||||
DownloadStatus = ""
|
DownloadStatus = ""
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -197,6 +201,8 @@ function Search-WingetPackagesPublic {
|
|||||||
Version = [string]$_.Version
|
Version = [string]$_.Version
|
||||||
Source = [string]$_.Source
|
Source = [string]$_.Source
|
||||||
Architecture = [string]$arch
|
Architecture = [string]$arch
|
||||||
|
AdditionalExitCodes = [string]::Empty
|
||||||
|
IgnoreNonZeroExitCodes = [bool]$false
|
||||||
DownloadStatus = [string]::Empty
|
DownloadStatus = [string]::Empty
|
||||||
}
|
}
|
||||||
} -ThrottleLimit 20
|
} -ThrottleLimit 20
|
||||||
@@ -724,6 +730,42 @@ function Invoke-WingetDownload {
|
|||||||
|
|
||||||
# Select only necessary properties before passing to Invoke-ParallelProcessing
|
# Select only necessary properties before passing to Invoke-ParallelProcessing
|
||||||
$itemsToProcess = $selectedApps | Select-Object Name, Id, Source, Version, Architecture # Include Version and Architecture if needed
|
$itemsToProcess = $selectedApps | Select-Object Name, Id, Source, Version, Architecture # Include Version and Architecture if needed
|
||||||
|
|
||||||
|
# Before downloading, persist the selected apps to AppList.json including exit-code fields (parity with Save-WingetList)
|
||||||
|
try {
|
||||||
|
# Determine AppList.json path; default if empty
|
||||||
|
if ([string]::IsNullOrWhiteSpace($localAppListJsonPath)) {
|
||||||
|
$localAppListJsonPath = Join-Path -Path $localAppsPath -ChildPath "AppList.json"
|
||||||
|
$taskArguments.AppListJsonPath = $localAppListJsonPath
|
||||||
|
WriteLog "AppListJsonPath was empty. Defaulting to: $localAppListJsonPath"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Build apps payload from current selection, preserving AdditionalExitCodes/IgnoreNonZeroExitCodes
|
||||||
|
$appListToSave = @{
|
||||||
|
apps = @($selectedApps | ForEach-Object {
|
||||||
|
[ordered]@{
|
||||||
|
name = (ConvertTo-SafeName -Name $_.Name)
|
||||||
|
id = $_.Id
|
||||||
|
source = $_.Source.ToLower()
|
||||||
|
architecture = $_.Architecture
|
||||||
|
AdditionalExitCodes = if ($_.PSObject.Properties['AdditionalExitCodes']) { $_.AdditionalExitCodes } else { "" }
|
||||||
|
IgnoreNonZeroExitCodes = if ($_.PSObject.Properties['IgnoreNonZeroExitCodes']) { [bool]$_.IgnoreNonZeroExitCodes } else { $false }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
# Ensure destination directory exists and write AppList.json
|
||||||
|
$destDir = Split-Path -Parent $localAppListJsonPath
|
||||||
|
if (-not (Test-Path -LiteralPath $destDir)) {
|
||||||
|
[void][System.IO.Directory]::CreateDirectory($destDir)
|
||||||
|
}
|
||||||
|
$appListToSave | ConvertTo-Json -Depth 10 | Set-Content -Path $localAppListJsonPath -Encoding UTF8
|
||||||
|
WriteLog "Persisted AppList.json with selected apps and exit-code fields to: $localAppListJsonPath"
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
WriteLog "Warning: Failed to persist AppList.json prior to download. Error: $($_.Exception.Message)"
|
||||||
|
}
|
||||||
|
|
||||||
# Invoke the centralized parallel processing function
|
# Invoke the centralized parallel processing function
|
||||||
# Pass task type and task-specific arguments
|
# Pass task type and task-specific arguments
|
||||||
Invoke-ParallelProcessing -ItemsToProcess $itemsToProcess `
|
Invoke-ParallelProcessing -ItemsToProcess $itemsToProcess `
|
||||||
|
|||||||
Reference in New Issue
Block a user