diff --git a/FFUDevelopment/BuildFFUVM.ps1 b/FFUDevelopment/BuildFFUVM.ps1 index a3bff05..d93bcc0 100644 --- a/FFUDevelopment/BuildFFUVM.ps1 +++ b/FFUDevelopment/BuildFFUVM.ps1 @@ -5871,6 +5871,13 @@ if ($AllowVHDXCaching) { throw $_ } } + +# Remove WinGetWin32Apps.json so it is always rebuilt next run +if (Test-Path -Path $wingetWin32jsonFile -PathType Leaf) { + WriteLog "Removing $wingetWin32jsonFile" + Remove-Item -Path $wingetWin32jsonFile -Force -ErrorAction SilentlyContinue + WriteLog "Removal complete" +} #Set $LongPathsEnabled registry value back to original value. $LongPathsEnabled could be $null if the registry value was not found if ($null -eq $LongPathsEnabled) { Remove-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem' -Name 'LongPathsEnabled' -ErrorAction SilentlyContinue diff --git a/FFUDevelopment/FFU.Common/FFU.Common.Winget.psm1 b/FFUDevelopment/FFU.Common/FFU.Common.Winget.psm1 index 5246ebc..624623d 100644 --- a/FFUDevelopment/FFU.Common/FFU.Common.Winget.psm1 +++ b/FFUDevelopment/FFU.Common/FFU.Common.Winget.psm1 @@ -22,7 +22,8 @@ function Get-Application { [Parameter(Mandatory = $true)] [string]$WindowsArch, [Parameter(Mandatory = $true)] - [string]$OrchestrationPath + [string]$OrchestrationPath, + [switch]$SkipWin32Json ) # Block Company Portal from winget source @@ -48,6 +49,29 @@ function Get-Application { # Check if the folder is not empty. if (Get-ChildItem -Path $appBaseFolderPathForCheck -Recurse -ErrorAction SilentlyContinue | Select-Object -First 1) { WriteLog "Application '$AppName' appears to be already downloaded as content exists in '$appBaseFolderPathForCheck'. Skipping download." + + # Add silent install command(s) only if not skipping JSON generation (build-time scenario) + $appIsWin32Existing = ($Source -eq 'winget' -or ($Source -eq 'msstore' -and $AppId.StartsWith('XP'))) + if ($appIsWin32Existing -and -not $SkipWin32Json) { + $win32BasePath = Join-Path -Path "$AppsPath\Win32" -ChildPath $AppName + if (Test-Path -Path $win32BasePath -PathType Container) { + $archFolders = Get-ChildItem -Path $win32BasePath -Directory -ErrorAction SilentlyContinue | Where-Object { $_.Name -in @('x86', 'x64', 'arm64') } + if ($archFolders) { + foreach ($archFolder in $archFolders) { + WriteLog "Adding silent install command for pre-downloaded $AppName ($($archFolder.Name)) to $OrchestrationPath\WinGetWin32Apps.json" + Add-Win32SilentInstallCommand -AppFolder $AppName -AppFolderPath $archFolder.FullName -OrchestrationPath $OrchestrationPath -SubFolder $archFolder.Name | Out-Null + } + } + else { + WriteLog "Adding silent install command for pre-downloaded $AppName to $OrchestrationPath\WinGetWin32Apps.json" + Add-Win32SilentInstallCommand -AppFolder $AppName -AppFolderPath $win32BasePath -OrchestrationPath $OrchestrationPath | Out-Null + } + } + } + elseif ($appIsWin32Existing -and $SkipWin32Json) { + WriteLog "Skipping WinGetWin32Apps.json regeneration for pre-downloaded $AppName (UI mode)." + } + return 0 # Success, already present } } @@ -196,8 +220,14 @@ function Get-Application { } # If app is in Win32 folder, add the silent install command to the WinGetWin32Apps.json file elseif ($appFolderPath -match 'Win32') { - WriteLog "$AppName is a Win32 app. Adding silent install command to $OrchestrationPath\WinGetWin32Apps.json" - $result = Add-Win32SilentInstallCommand -AppFolder $AppName -AppFolderPath $appFolderPath -OrchestrationPath $OrchestrationPath -SubFolder $subFolderForCommand + if (-not $SkipWin32Json) { + WriteLog "$AppName is a Win32 app. Adding silent install command to $OrchestrationPath\WinGetWin32Apps.json" + $result = Add-Win32SilentInstallCommand -AppFolder $AppName -AppFolderPath $appFolderPath -OrchestrationPath $OrchestrationPath -SubFolder $subFolderForCommand + } + else { + WriteLog "$AppName is a Win32 app. Skipping WinGetWin32Apps.json generation (UI mode)." + $result = 0 + } } else { # For any other case, set result to 0 (success) @@ -314,7 +344,7 @@ function Get-Apps { if (-not (Test-Path -Path $storeAppsFolder -PathType Container)) { New-Item -Path $storeAppsFolder -ItemType Directory -Force | Out-Null } - + foreach ($storeApp in $StoreApps) { try { $appArch = if ($storeApp.PSObject.Properties['architecture']) { $storeApp.architecture } else { $WindowsArch } @@ -326,6 +356,62 @@ function Get-Apps { } } } + + # Post-processing: Override CommandLine / Arguments from AppList.json if provided + # Users may supply custom silent install commands or arguments. These optional + # properties (CommandLine, Arguments) in AppList.json replace the auto-generated + # values in WinGetWin32Apps.json. Keyed by Name. + try { + $overrideMap = @{} + foreach ($app in $apps.apps) { + if ($app.source -in @('winget', 'msstore')) { + $hasCmd = ($app.PSObject.Properties['CommandLine'] -and -not [string]::IsNullOrWhiteSpace($app.CommandLine)) + $hasArgs = ($app.PSObject.Properties['Arguments'] -and -not [string]::IsNullOrWhiteSpace($app.Arguments)) + if ($hasCmd -or $hasArgs) { + $overrideMap[$app.name] = @{ + CommandLine = if ($hasCmd) { $app.CommandLine } else { $null } + Arguments = if ($hasArgs) { $app.Arguments } else { $null } + } + } + } + } + + if ($overrideMap.Count -gt 0) { + $winGetWin32Path = Join-Path -Path $OrchestrationPath -ChildPath 'WinGetWin32Apps.json' + if (Test-Path -Path $winGetWin32Path) { + [array]$appsDataUpdated = Get-Content -Path $winGetWin32Path -Raw | ConvertFrom-Json + $changed = $false + foreach ($entry in $appsDataUpdated) { + if ($overrideMap.ContainsKey($entry.Name)) { + $ov = $overrideMap[$entry.Name] + if ($ov.CommandLine) { + WriteLog "Override (AppList.json) CommandLine for $($entry.Name)" + $entry.CommandLine = $ov.CommandLine + $changed = $true + } + if ($ov.Arguments) { + WriteLog "Override (AppList.json) Arguments for $($entry.Name)" + $entry.Arguments = $ov.Arguments + $changed = $true + } + } + } + if ($changed) { + $appsDataUpdated | ConvertTo-Json -Depth 10 | Set-Content -Path $winGetWin32Path + WriteLog "Applied AppList.json command overrides to WinGetWin32Apps.json" + } + else { + WriteLog "No matching apps required command overrides." + } + } + else { + WriteLog "WinGetWin32Apps.json not found; no overrides applied." + } + } + } + catch { + WriteLog "Failed to apply AppList.json command overrides: $($_.Exception.Message)" + } } function Install-WinGet { param ( diff --git a/FFUDevelopment/FFUUI.Core/FFUUI.Core.Winget.psm1 b/FFUDevelopment/FFUUI.Core/FFUUI.Core.Winget.psm1 index 84b615e..58777bb 100644 --- a/FFUDevelopment/FFUUI.Core/FFUUI.Core.Winget.psm1 +++ b/FFUDevelopment/FFUUI.Core/FFUUI.Core.Winget.psm1 @@ -450,70 +450,35 @@ function Start-WingetAppDownloadTask { } } - # 2. Check previous Winget download - if (-not $appFound) { - if (-not $appFound) { - $wingetWin32jsonFile = Join-Path -Path $OrchestrationPath -ChildPath "WinGetWin32Apps.json" - if (Test-Path -Path $wingetWin32jsonFile) { - try { - $wingetAppsJson = Get-Content -Path $wingetWin32jsonFile -Raw | ConvertFrom-Json - # Check if app already exists in WinGetWin32Apps.json - # For multi-arch apps, there might be entries like "AppName (x86)" and "AppName (x64)" - $existingWin32Entries = @($wingetAppsJson | Where-Object { - $_.Name -eq $appName -or - $_.Name -eq "$appName (x86)" -or - $_.Name -eq "$appName (x64)" - }) - - if ($existingWin32Entries.Count -gt 0) { - $appFolder = Join-Path -Path "$AppsPath\Win32" -ChildPath $appName - $appContentFound = $false - - # Check if it's a multi-arch app with subfolders - if ($ApplicationItemData.Architecture -eq 'x86 x64') { - $x86Folder = Join-Path -Path $appFolder -ChildPath "x86" - $x64Folder = Join-Path -Path $appFolder -ChildPath "x64" - - if ((Test-Path -Path $x86Folder -PathType Container) -and (Test-Path -Path $x64Folder -PathType Container)) { - $x86Size = (Get-ChildItem -Path $x86Folder -Recurse | Measure-Object -Property Length -Sum -ErrorAction SilentlyContinue).Sum - $x64Size = (Get-ChildItem -Path $x64Folder -Recurse | Measure-Object -Property Length -Sum -ErrorAction SilentlyContinue).Sum - - if ($x86Size -gt 1MB -and $x64Size -gt 1MB) { - $appContentFound = $true - } - } - } - else { - # Single architecture app - if (Test-Path -Path $appFolder -PathType Container) { - $folderSize = (Get-ChildItem -Path $appFolder -Recurse | Measure-Object -Property Length -Sum -ErrorAction SilentlyContinue).Sum - if ($folderSize -gt 1MB) { - $appContentFound = $true - } - } - } - - if ($appContentFound) { - $appFound = $true - $status = "Not Downloaded: App already in $wingetWin32jsonFile and found in $appFolder" - Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $appId -Status $status - WriteLog "Found '$appName' in WinGetWin32Apps.json and content exists in '$appFolder'. Skipping download to prevent duplicate entry." - return [PSCustomObject]@{ Id = $appId; Status = $status; ResultCode = 0 } - } - else { - # App entry exists in WinGetWin32Apps.json but folder is missing or incomplete - $appFound = $true - $status = "App in '$wingetWin32jsonFile' but content folder '$appFolder' not found or incomplete. Remove entry from WinGetWin32Apps.json or restore content." - Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $appId -Status $status - WriteLog $status - return [PSCustomObject]@{ Id = $appId; Status = $status; ResultCode = 1 } - } + # 2. Check existing downloaded Win32 content (folder-based; no WinGetWin32Apps.json dependency) + if (-not $appFound -and $source -eq 'winget') { + $appFolder = Join-Path -Path "$AppsPath\Win32" -ChildPath $appName + if (Test-Path -Path $appFolder -PathType Container) { + $contentFound = $false + if ($ApplicationItemData.Architecture -eq 'x86 x64') { + $x86Folder = Join-Path -Path $appFolder -ChildPath "x86" + $x64Folder = Join-Path -Path $appFolder -ChildPath "x64" + if ((Test-Path -Path $x86Folder -PathType Container) -and (Test-Path -Path $x64Folder -PathType Container)) { + $x86Size = (Get-ChildItem -Path $x86Folder -Recurse | Measure-Object -Property Length -Sum -ErrorAction SilentlyContinue).Sum + $x64Size = (Get-ChildItem -Path $x64Folder -Recurse | Measure-Object -Property Length -Sum -ErrorAction SilentlyContinue).Sum + if ($x86Size -gt 1MB -and $x64Size -gt 1MB) { + $contentFound = $true } } - catch { - WriteLog "Warning: Could not read or parse '$wingetWin32jsonFile'. Error: $($_.Exception.Message)" + } + else { + $folderSize = (Get-ChildItem -Path $appFolder -Recurse | Measure-Object -Property Length -Sum -ErrorAction SilentlyContinue).Sum + if ($folderSize -gt 1MB) { + $contentFound = $true } } + if ($contentFound) { + $appFound = $true + $status = "Not Downloaded: Existing content found in $appFolder" + Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $appId -Status $status + WriteLog "Found existing content for '$appName' in '$appFolder'. Skipping download to prevent duplicate entry." + return [PSCustomObject]@{ Id = $appId; Status = $status; ResultCode = 0 } + } } } @@ -634,7 +599,7 @@ function Start-WingetAppDownloadTask { try { # Call Get-Application - $resultCode = Get-Application -AppName $appName -AppId $appId -Source $source -AppsPath $AppsPath -WindowsArch $ApplicationItemData.Architecture -OrchestrationPath $OrchestrationPath -ErrorAction Stop + $resultCode = Get-Application -AppName $appName -AppId $appId -Source $source -AppsPath $AppsPath -WindowsArch $ApplicationItemData.Architecture -OrchestrationPath $OrchestrationPath -SkipWin32Json -ErrorAction Stop # Determine status based on result code switch ($resultCode) {